Python: (samples): adopt AzureOpenAIResponsesClient, reorganize orchestration examples, and fix workflow/orchestration bugs (#3873)

* adopt AzureOpenAIResponsesClient, reorganize orchestration examples, and fix workflow/orchestration bugs

* Updates

* add comment
This commit is contained in:
Evan Mattson
2026-02-12 19:46:58 +09:00
committed by GitHub
Unverified
parent 8457533c69
commit 1b10b051fd
73 changed files with 1612 additions and 686 deletions
@@ -1,19 +1,21 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
import os
from dataclasses import dataclass
from agent_framework import (
AgentExecutor, # Wraps a ChatAgent as an Executor for use in workflows
AgentExecutorRequest, # The message bundle sent to an AgentExecutor
AgentExecutorResponse, # The structured result returned by an AgentExecutor
AgentResponseUpdate,
Executor, # Base class for custom Python executors
Message, # Chat message structure
WorkflowBuilder, # Fluent builder for wiring the workflow graph
WorkflowContext, # Per run context and event bus
handler, # Decorator to mark an Executor method as invokable
)
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.azure import AzureOpenAIResponsesClient
from azure.identity import AzureCliCredential # Uses your az CLI login for credentials
from typing_extensions import Never
@@ -29,8 +31,9 @@ Show how to construct a parallel branch pattern in workflows. Demonstrate:
- Fan in by collecting a list of AgentExecutorResponse objects and reducing them to a single result.
Prerequisites:
- AZURE_AI_PROJECT_ENDPOINT must be your Azure AI Foundry Agent Service (V2) project endpoint.
- Familiarity with WorkflowBuilder, executors, edges, events, and streaming runs.
- Azure OpenAI access configured for AzureOpenAIChatClient. Log in with Azure CLI and set any required environment variables.
- Azure OpenAI access configured for AzureOpenAIResponsesClient. Log in with Azure CLI and set any required environment variables.
- Comfort reading AgentExecutorResponse.agent_response.text for assistant output aggregation.
"""
@@ -87,13 +90,31 @@ class AggregateInsights(Executor):
await ctx.yield_output(consolidated)
def render_live_streams(buffers: dict[str, str], order: list[str], completed: set[str]) -> None:
"""Render concurrent agent streams in separate sections."""
# Clear terminal and move cursor to top-left for a live dashboard effect.
print("\033[2J\033[H", end="")
print("=== Expert Streams (Live) ===")
print("Concurrent agent updates are shown below as they stream.\n")
for agent_id in order:
state = "completed" if agent_id in completed else "streaming"
print(f"[{agent_id}] ({state})")
print(buffers.get(agent_id, ""))
print("-" * 80)
print("", end="", flush=True)
async def main() -> None:
# 1) Create executor and agent instances
dispatcher = DispatchToExperts(id="dispatcher")
aggregator = AggregateInsights(id="aggregator")
researcher = AgentExecutor(
AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
AzureOpenAIResponsesClient(
project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
credential=AzureCliCredential(),
).as_agent(
instructions=(
"You're an expert market and product researcher. Given a prompt, provide concise, factual insights,"
" opportunities, and risks."
@@ -102,7 +123,11 @@ async def main() -> None:
)
)
marketer = AgentExecutor(
AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
AzureOpenAIResponsesClient(
project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
credential=AzureCliCredential(),
).as_agent(
instructions=(
"You're a creative marketing strategist. Craft compelling value propositions and target messaging"
" aligned to the prompt."
@@ -111,7 +136,11 @@ async def main() -> None:
)
)
legal = AgentExecutor(
AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
AzureOpenAIResponsesClient(
project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
credential=AzureCliCredential(),
).as_agent(
instructions=(
"You're a cautious legal/compliance reviewer. Highlight constraints, disclaimers, and policy concerns"
" based on the prompt."
@@ -128,18 +157,32 @@ async def main() -> None:
.build()
)
# 3) Run with a single prompt and print progress plus the final consolidated output
# 3) Run with a single prompt and render live expert streams plus final consolidated output.
expert_order = ["researcher", "marketer", "legal"]
expert_buffers: dict[str, str] = {expert_id: "" for expert_id in expert_order}
completed_experts: set[str] = set()
final_output: str | None = None
async for event in workflow.run(
"We are launching a new budget-friendly electric bike for urban commuters.", stream=True
):
if event.type == "executor_invoked":
# Show when executors are invoked and completed for lightweight observability.
print(f"{event.executor_id} invoked")
elif event.type == "executor_completed":
print(f"{event.executor_id} completed")
if event.type == "executor_completed" and event.executor_id in expert_buffers:
completed_experts.add(event.executor_id)
render_live_streams(expert_buffers, expert_order, completed_experts)
elif event.type == "output":
print("===== Final Aggregated Output =====")
print(event.data)
if isinstance(event.data, AgentResponseUpdate):
executor_id = event.executor_id or ""
if executor_id in expert_buffers:
expert_buffers[executor_id] += event.data.text
render_live_streams(expert_buffers, expert_order, completed_experts)
continue
if event.executor_id == "aggregator":
final_output = str(event.data)
if final_output:
print("\n=== Final Consolidated Output ===\n")
print(final_output)
if __name__ == "__main__":