Python: DevUI improvements. (#1091)

* enable deeplinking in ui, add agent details to entity info, add usage data, add middleware example in samples and foundry agent.

* update ui build

* Update python/packages/devui/frontend/src/components/workflow/workflow-input-form.tsx

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update python/packages/devui/pyproject.toml

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update python/packages/devui/pyproject.toml

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* imporove mapping for agent nodes and serialiation for agent run events

* lint fixes

* update pyproj toml and ui updates

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Victor Dibia
2025-10-03 15:22:03 -07:00
committed by GitHub
Unverified
parent 61ac6d43b2
commit 01f438d710
33 changed files with 2733 additions and 1164 deletions
@@ -2,12 +2,73 @@
"""Sample weather agent for Agent Framework Debug UI."""
import os
from collections.abc import Awaitable, Callable
from typing import Annotated
from agent_framework import ChatAgent
from agent_framework import (
ChatAgent,
ChatContext,
ChatMessage,
ChatResponse,
FunctionInvocationContext,
Role,
chat_middleware,
function_middleware,
)
from agent_framework.azure import AzureOpenAIChatClient
@chat_middleware
async def security_filter_middleware(
context: ChatContext,
next: Callable[[ChatContext], Awaitable[None]],
) -> None:
"""Chat middleware that blocks requests containing sensitive information."""
# Block requests with sensitive information
blocked_terms = ["password", "secret", "api_key", "token"]
for message in context.messages:
if message.text:
message_lower = message.text.lower()
for term in blocked_terms:
if term in message_lower:
# Override the response without calling the LLM
context.result = ChatResponse(
messages=[
ChatMessage(
role=Role.ASSISTANT,
text=(
"I cannot process requests containing sensitive information. "
"Please rephrase your question without including passwords, secrets, "
"or other sensitive data."
),
)
]
)
return
await next(context)
@function_middleware
async def atlantis_location_filter_middleware(
context: FunctionInvocationContext,
next: Callable[[FunctionInvocationContext], Awaitable[None]],
) -> None:
"""Function middleware that blocks weather requests for Atlantis."""
# Check if location parameter is "atlantis"
location = getattr(context.arguments, "location", None)
if location and location.lower() == "atlantis":
context.result = (
"Blocked! Hold up right there!! Tell the user that "
"'Atlantis is a special place, we must never ask about the weather there!!'"
)
context.terminate = True
return
await next(context)
def get_weather(
location: Annotated[str, "The location to get the weather for."],
) -> str:
@@ -46,6 +107,7 @@ agent = ChatAgent(
api_key=os.environ.get("AZURE_OPENAI_API_KEY", ""),
),
tools=[get_weather, get_forecast],
middleware=[security_filter_middleware, atlantis_location_filter_middleware],
)
@@ -0,0 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
"""Sequential Agents Workflow - Writer → Reviewer."""
from .workflow import workflow
__all__ = ["workflow"]
@@ -0,0 +1,167 @@
# Copyright (c) Microsoft. All rights reserved.
"""Agent Workflow - Content Review with Quality Routing.
This sample demonstrates:
- Using agents directly as executors
- Conditional routing based on structured outputs
- Quality-based workflow paths with convergence
Use case: Content creation with automated review.
Writer creates content, Reviewer evaluates quality:
- High quality (score >= 80): → Publisher → Summarizer
- Low quality (score < 80): → Editor → Publisher → Summarizer
Both paths converge at Summarizer for final report.
"""
import os
from typing import Any
from agent_framework import AgentExecutorResponse, WorkflowBuilder
from agent_framework.azure import AzureOpenAIChatClient
from pydantic import BaseModel
# Define structured output for review results
class ReviewResult(BaseModel):
"""Review evaluation with scores and feedback."""
score: int # Overall quality score (0-100)
feedback: str # Concise, actionable feedback
clarity: int # Clarity score (0-100)
completeness: int # Completeness score (0-100)
accuracy: int # Accuracy score (0-100)
structure: int # Structure score (0-100)
# Condition function: route to editor if score < 80
def needs_editing(message: Any) -> bool:
"""Check if content needs editing based on review score."""
if not isinstance(message, AgentExecutorResponse):
return False
try:
review = ReviewResult.model_validate_json(message.agent_run_response.text)
return review.score < 80
except Exception:
return False
# Condition function: content is approved (score >= 80)
def is_approved(message: Any) -> bool:
"""Check if content is approved (high quality)."""
if not isinstance(message, AgentExecutorResponse):
return True
try:
review = ReviewResult.model_validate_json(message.agent_run_response.text)
return review.score >= 80
except Exception:
return True
# Create Azure OpenAI chat client
chat_client = AzureOpenAIChatClient(api_key=os.environ.get("AZURE_OPENAI_API_KEY", ""))
# Create Writer agent - generates content
writer = chat_client.create_agent(
name="Writer",
instructions=(
"You are an excellent content writer. "
"Create clear, engaging content based on the user's request. "
"Focus on clarity, accuracy, and proper structure."
),
)
# Create Reviewer agent - evaluates and provides structured feedback
reviewer = chat_client.create_agent(
name="Reviewer",
instructions=(
"You are an expert content reviewer. "
"Evaluate the writer's content based on:\n"
"1. Clarity - Is it easy to understand?\n"
"2. Completeness - Does it fully address the topic?\n"
"3. Accuracy - Is the information correct?\n"
"4. Structure - Is it well-organized?\n\n"
"Return a JSON object with:\n"
"- score: overall quality (0-100)\n"
"- feedback: concise, actionable feedback\n"
"- clarity, completeness, accuracy, structure: individual scores (0-100)"
),
response_format=ReviewResult,
)
# Create Editor agent - improves content based on feedback
editor = chat_client.create_agent(
name="Editor",
instructions=(
"You are a skilled editor. "
"You will receive content along with review feedback. "
"Improve the content by addressing all the issues mentioned in the feedback. "
"Maintain the original intent while enhancing clarity, completeness, accuracy, and structure."
),
)
# Create Publisher agent - formats content for publication
publisher = chat_client.create_agent(
name="Publisher",
instructions=(
"You are a publishing agent. "
"You receive either approved content or edited content. "
"Format it for publication with proper headings and structure."
),
)
# Create Summarizer agent - creates final publication report
summarizer = chat_client.create_agent(
name="Summarizer",
instructions=(
"You are a summarizer agent. "
"Create a final publication report that includes:\n"
"1. A brief summary of the published content\n"
"2. The workflow path taken (direct approval or edited)\n"
"3. Key highlights and takeaways\n"
"Keep it concise and professional."
),
)
# Build workflow with branching and convergence:
# Writer → Reviewer → [branches]:
# - If score >= 80: → Publisher → Summarizer (direct approval path)
# - If score < 80: → Editor → Publisher → Summarizer (improvement path)
# Both paths converge at Summarizer for final report
workflow = (
WorkflowBuilder()
.set_start_executor(writer)
.add_edge(writer, reviewer)
# Branch 1: High quality (>= 80) goes directly to publisher
.add_edge(reviewer, publisher, condition=is_approved)
# Branch 2: Low quality (< 80) goes to editor first, then publisher
.add_edge(reviewer, editor, condition=needs_editing)
.add_edge(editor, publisher)
# Both paths converge: Publisher → Summarizer
.add_edge(publisher, summarizer)
.build()
)
def main():
"""Launch the branching workflow in DevUI."""
import logging
from agent_framework.devui import serve
logging.basicConfig(level=logging.INFO, format="%(message)s")
logger = logging.getLogger(__name__)
logger.info("Starting Agent Workflow (Content Review with Quality Routing)")
logger.info("Available at: http://localhost:8093")
logger.info("\nThis workflow demonstrates:")
logger.info("- Conditional routing based on structured outputs")
logger.info("- Path 1 (score >= 80): Reviewer → Publisher → Summarizer")
logger.info("- Path 2 (score < 80): Reviewer → Editor → Publisher → Summarizer")
logger.info("- Both paths converge at Summarizer for final report")
serve(entities=[workflow], port=8093, auto_open=True)
if __name__ == "__main__":
main()