Skip to content

Commit cbeb594

Browse files
authored
Merge pull request #42 from Azure-Samples/upgrade-maf
Upgrade agent-framework examples to latest SDK version
2 parents b4d4bec + c1ad08e commit cbeb594

8 files changed

Lines changed: 646 additions & 181 deletions

examples/agentframework_hitl.py

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,20 @@
99
from agent_framework import (
1010
AgentExecutorRequest,
1111
AgentExecutorResponse,
12-
AgentRunResponse,
12+
AgentResponse,
1313
AgentRunUpdateEvent,
1414
ChatAgent,
1515
ChatMessage,
16+
Content,
1617
Executor,
17-
FunctionCallContent,
18-
FunctionResultContent,
1918
RequestInfoEvent,
2019
Role,
21-
ToolMode,
2220
WorkflowBuilder,
2321
WorkflowContext,
2422
WorkflowOutputEvent,
2523
handler,
2624
response_handler,
25+
tool,
2726
)
2827
from agent_framework.openai import OpenAIChatClient
2928
from azure.identity.aio import DefaultAzureCredential, get_bearer_token_provider
@@ -81,6 +80,8 @@
8180
"""
8281

8382

83+
# NOTE: approval_mode="never_require" is for sample brevity.
84+
@tool(approval_mode="never_require")
8485
def fetch_product_brief(
8586
product_name: Annotated[str, Field(description="Product name to look up.")],
8687
) -> str:
@@ -97,6 +98,8 @@ def fetch_product_brief(
9798
return briefs.get(product_name.lower(), f"No stored brief for '{product_name}'.")
9899

99100

101+
# NOTE: approval_mode="never_require" is for sample brevity.
102+
@tool(approval_mode="never_require")
100103
def get_brand_voice_profile(
101104
voice_name: Annotated[str, Field(description="Brand or campaign voice to emulate.")],
102105
) -> str:
@@ -133,12 +136,12 @@ def __init__(self, id: str, writer_id: str, final_editor_id: str) -> None:
133136
async def on_writer_response(
134137
self,
135138
draft: AgentExecutorResponse,
136-
ctx: WorkflowContext[Never, AgentRunResponse],
139+
ctx: WorkflowContext[Never, AgentResponse],
137140
) -> None:
138141
"""Handle responses from the other two agents in the workflow."""
139142
if draft.executor_id == self.final_editor_id:
140143
# Final editor response; yield output directly.
141-
await ctx.yield_output(draft.agent_run_response)
144+
await ctx.yield_output(draft.agent_response)
142145
return
143146

144147
# Writer agent response; request human feedback.
@@ -148,8 +151,8 @@ async def on_writer_response(
148151
if draft.full_conversation is not None:
149152
conversation = list(draft.full_conversation)
150153
else:
151-
conversation = list(draft.agent_run_response.messages)
152-
draft_text = draft.agent_run_response.text.strip()
154+
conversation = list(draft.agent_response.messages)
155+
draft_text = draft.agent_response.text.strip()
153156
if not draft_text:
154157
draft_text = "No draft text was produced."
155158

@@ -199,21 +202,23 @@ async def on_human_feedback(
199202

200203
def create_writer_agent() -> ChatAgent:
201204
"""Creates a writer agent with tools."""
202-
return client.create_agent(
205+
return ChatAgent(
206+
chat_client=client,
203207
name="writer_agent",
204208
instructions=(
205209
"You are a marketing writer. Call the available tools before drafting copy so you are precise. "
206210
"Always call both tools once before drafting. Summarize tool outputs as bullet points, then "
207211
"produce a 3-sentence draft."
208212
),
209213
tools=[fetch_product_brief, get_brand_voice_profile],
210-
tool_choice=ToolMode.REQUIRED_ANY,
214+
tool_choice="required",
211215
)
212216

213217

214218
def create_final_editor_agent() -> ChatAgent:
215219
"""Creates a final editor agent."""
216-
return client.create_agent(
220+
return ChatAgent(
221+
chat_client=client,
217222
name="final_editor_agent",
218223
instructions=(
219224
"You are an editor who polishes marketing copy after human approval. "
@@ -229,8 +234,9 @@ def display_agent_run_update(event: AgentRunUpdateEvent, last_executor: str | No
229234
executor_id = event.executor_id
230235
update = event.data
231236
# Extract and print any new tool calls or results from the update.
232-
function_calls = [c for c in update.contents if isinstance(c, FunctionCallContent)] # type: ignore[union-attr]
233-
function_results = [c for c in update.contents if isinstance(c, FunctionResultContent)] # type: ignore[union-attr]
237+
# Content.type indicates the content kind: 'function_call', 'function_result', 'text', etc.
238+
function_calls = [c for c in update.contents if isinstance(c, Content) and c.type == "function_call"]
239+
function_results = [c for c in update.contents if isinstance(c, Content) and c.type == "function_result"]
234240
if executor_id != last_executor:
235241
if last_executor is not None:
236242
print()

examples/agentframework_magenticone.py

Lines changed: 73 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
"""
44
import asyncio
55
import os
6+
from typing import cast
67

78
from agent_framework import (
9+
AgentRunUpdateEvent,
810
ChatAgent,
9-
MagenticAgentMessageEvent,
11+
ChatMessage,
1012
MagenticBuilder,
11-
MagenticCallbackEvent,
12-
MagenticCallbackMode,
13-
MagenticOrchestratorMessageEvent,
13+
MagenticOrchestratorEvent,
14+
MagenticProgressLedger,
1415
WorkflowOutputEvent,
1516
)
1617
from agent_framework.openai import OpenAIChatClient
@@ -19,6 +20,7 @@
1920
from rich.console import Console
2021
from rich.markdown import Markdown
2122
from rich.panel import Panel
23+
from rich.rule import Rule
2224

2325
# Configure OpenAI client based on environment
2426
load_dotenv(override=True)
@@ -85,40 +87,20 @@
8587
description="A helpful assistant that can summarize the travel plan.",
8688
)
8789

90+
# Create a manager agent for orchestration
91+
manager_agent = ChatAgent(
92+
chat_client=client,
93+
instructions="You coordinate a team to complete travel planning tasks efficiently.",
94+
name="magentic_manager",
95+
description="Orchestrator that coordinates the travel planning workflow",
96+
)
8897

89-
# Event callback for streaming output with rich formatting
90-
async def on_event(event: MagenticCallbackEvent) -> None:
91-
if isinstance(event, MagenticOrchestratorMessageEvent):
92-
emoji = "✅" if event.kind == "task_ledger" else "🦠"
93-
console.print(
94-
Panel(
95-
Markdown(event.message.text),
96-
title=f"{emoji} orchestrator: {event.kind}",
97-
border_style="bold green",
98-
padding=(1, 2),
99-
)
100-
)
101-
elif isinstance(event, MagenticAgentMessageEvent):
102-
console.print(
103-
Panel(
104-
Markdown(event.message.text),
105-
title=f"🤖 {event.agent_id}",
106-
border_style="bold blue",
107-
padding=(1, 2),
108-
)
109-
)
110-
111-
98+
# Build the Magentic workflow
11299
magentic_orchestrator = (
113100
MagenticBuilder()
114-
.participants(
115-
local_agent=local_agent,
116-
language_agent=language_agent,
117-
travel_summary_agent=travel_summary_agent,
118-
)
119-
.on_event(on_event, mode=MagenticCallbackMode.NON_STREAMING)
120-
.with_standard_manager(
121-
chat_client=client,
101+
.participants([local_agent, language_agent, travel_summary_agent])
102+
.with_manager(
103+
agent=manager_agent,
122104
max_round_count=20,
123105
max_stall_count=3,
124106
max_reset_count=2,
@@ -128,17 +110,69 @@ async def on_event(event: MagenticCallbackEvent) -> None:
128110

129111

130112
async def main():
113+
# Keep track of the last message to format output nicely in streaming mode
114+
last_message_id: str | None = None
115+
output_event: WorkflowOutputEvent | None = None
116+
131117
async for event in magentic_orchestrator.run_stream("Plan a half-day trip to Costa Rica"):
132-
if isinstance(event, WorkflowOutputEvent):
133-
final_result = event.data
118+
if isinstance(event, AgentRunUpdateEvent):
119+
message_id = event.data.message_id
120+
if message_id != last_message_id:
121+
if last_message_id is not None:
122+
console.print() # Add spacing after previous message
123+
console.print(Rule(f"🤖 {event.executor_id}", style="bold blue"))
124+
last_message_id = message_id
125+
console.print(event.data, end="")
126+
127+
elif isinstance(event, MagenticOrchestratorEvent):
128+
console.print() # Ensure panel starts on a new line
129+
if isinstance(event.data, ChatMessage):
130+
# Show the plan creation in a panel
131+
console.print(
132+
Panel(
133+
Markdown(event.data.text),
134+
title=f"📋 Orchestrator: {event.event_type.name}",
135+
border_style="bold green",
136+
padding=(1, 2),
137+
)
138+
)
139+
elif isinstance(event.data, MagenticProgressLedger):
140+
# Show a compact progress summary in a panel
141+
ledger = event.data
142+
satisfied = "✅" if ledger.is_request_satisfied.answer else "⏳ Steps pending"
143+
progress = "✅" if ledger.is_progress_being_made.answer else "❌ Progress stalled"
144+
loop = "⚠️ Loop detected" if ledger.is_in_loop.answer else ""
145+
next_agent = ledger.next_speaker.answer
146+
instruction = ledger.instruction_or_question.answer
147+
148+
status_text = f"Plan satisfied? {satisfied} | Making progress? {progress} {loop}\n\n➡️ Next step: [bold]{next_agent}[/bold]\n{instruction}"
149+
console.print(
150+
Panel(
151+
status_text,
152+
title=f"📊 Orchestrator: {event.event_type.name}",
153+
border_style="bold yellow",
154+
padding=(1, 2),
155+
)
156+
)
157+
158+
159+
elif isinstance(event, WorkflowOutputEvent):
160+
output_event = event
161+
162+
if output_event:
163+
console.print() # Add spacing
164+
# The output of the Magentic workflow is a list of ChatMessages with only one final message
165+
output_messages = cast(list[ChatMessage], output_event.data)
166+
if output_messages:
134167
console.print(
135168
Panel(
136-
Markdown(final_result.text),
137-
title="🌎 final travel plan",
169+
Markdown(output_messages[-1].text),
170+
title="🌎 Final Travel Plan",
138171
border_style="bold green",
139172
padding=(1, 2),
140173
)
141174
)
175+
142176
if async_credential:
143177
await async_credential.close()
144178

examples/agentframework_workflow.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import os
33
from typing import Any
44

5-
from agent_framework import AgentExecutorResponse, WorkflowBuilder
5+
from agent_framework import AgentExecutorResponse, ChatAgent, WorkflowBuilder
66
from agent_framework.openai import OpenAIChatClient
77
from azure.identity.aio import DefaultAzureCredential, get_bearer_token_provider
88
from dotenv import load_dotenv
@@ -74,7 +74,8 @@ def is_approved(message: Any) -> bool:
7474

7575
# Create Writer agent - generates content
7676
def create_writer():
77-
return client.create_agent(
77+
return ChatAgent(
78+
chat_client=client,
7879
name="Writer",
7980
instructions=(
8081
"You are an excellent content writer. "
@@ -86,7 +87,8 @@ def create_writer():
8687

8788
# Create Reviewer agent - evaluates and provides structured feedback
8889
def create_reviewer():
89-
return client.create_agent(
90+
return ChatAgent(
91+
chat_client=client,
9092
name="Reviewer",
9193
instructions=(
9294
"You are an expert content reviewer. "
@@ -106,7 +108,8 @@ def create_reviewer():
106108

107109
# Create Editor agent - improves content based on feedback
108110
def create_editor():
109-
return client.create_agent(
111+
return ChatAgent(
112+
chat_client=client,
110113
name="Editor",
111114
instructions=(
112115
"You are a skilled editor. "
@@ -119,7 +122,8 @@ def create_editor():
119122

120123
# Create Publisher agent - formats content for publication
121124
def create_publisher():
122-
return client.create_agent(
125+
return ChatAgent(
126+
chat_client=client,
123127
name="Publisher",
124128
instructions=(
125129
"You are a publishing agent. "
@@ -131,7 +135,8 @@ def create_publisher():
131135

132136
# Create Summarizer agent - creates final publication report
133137
def create_summarizer():
134-
return client.create_agent(
138+
return ChatAgent(
139+
chat_client=client,
135140
name="Summarizer",
136141
instructions=(
137142
"You are a summarizer agent. "

0 commit comments

Comments
 (0)