Python: Properly configure structured outputs based on new options dict (#3213)

* Properly configure structured outputs based on new options dict

* Fix mypy
This commit is contained in:
Evan Mattson
2026-01-15 11:41:46 +09:00
committed by GitHub
Unverified
parent 620da7a829
commit 15d0c34d9f
18 changed files with 56 additions and 55 deletions
@@ -34,7 +34,7 @@ async def main() -> None:
name="ProductMarketerAgent",
instructions="Return launch briefs as structured JSON.",
# Specify type to use as response
response_format=ReleaseBrief,
options={"response_format": ReleaseBrief},
)
query = "Draft a launch brief for the Contoso Note app."
@@ -35,7 +35,7 @@ async def non_streaming_example() -> None:
print(f"User: {query}")
# Get structured response from the agent using response_format parameter
result = await agent.run(query, response_format=OutputStruct)
result = await agent.run(query, options={"response_format": OutputStruct})
# Access the structured output directly from the response value
if result.value:
@@ -63,7 +63,7 @@ async def streaming_example() -> None:
# Get structured response from streaming agent using AgentResponse.from_agent_response_generator
# This method collects all streaming updates and combines them into a single AgentResponse
result = await AgentResponse.from_agent_response_generator(
agent.run_stream(query, response_format=OutputStruct),
agent.run_stream(query, options={"response_format": OutputStruct}),
output_format_type=OutputStruct,
)
@@ -99,7 +99,7 @@ def spam_detection_orchestration(context: DurableOrchestrationContext):
spam_result_raw = yield spam_agent.run(
messages=spam_prompt,
thread=spam_thread,
response_format=SpamDetectionResult,
options={"response_format": SpamDetectionResult},
)
spam_result = cast(SpamDetectionResult, spam_result_raw.value)
@@ -120,7 +120,7 @@ def spam_detection_orchestration(context: DurableOrchestrationContext):
email_result_raw = yield email_agent.run(
messages=email_prompt,
thread=email_thread,
response_format=EmailResponse,
options={"response_format": EmailResponse},
)
email_result = cast(EmailResponse, email_result_raw.value)
@@ -98,7 +98,7 @@ def content_generation_hitl_orchestration(context: DurableOrchestrationContext):
initial_raw = yield writer.run(
messages=f"Write a short article about '{payload.topic}'.",
thread=writer_thread,
response_format=GeneratedContent,
options={"response_format": GeneratedContent},
)
content = initial_raw.value
@@ -135,9 +135,7 @@ def content_generation_hitl_orchestration(context: DurableOrchestrationContext):
)
return {"content": content.content}
context.set_custom_status(
"Content rejected by human reviewer. Incorporating feedback and regenerating..."
)
context.set_custom_status("Content rejected by human reviewer. Incorporating feedback and regenerating...")
rewrite_prompt = (
"The content was rejected by a human reviewer. Please rewrite the article incorporating their feedback.\n\n"
f"Human Feedback: {approval_payload.feedback or 'No feedback provided.'}"
@@ -145,7 +143,7 @@ def content_generation_hitl_orchestration(context: DurableOrchestrationContext):
rewritten_raw = yield writer.run(
messages=rewrite_prompt,
thread=writer_thread,
response_format=GeneratedContent,
options={"response_format": GeneratedContent},
)
rewritten_value = rewritten_raw.value
@@ -157,9 +155,7 @@ def content_generation_hitl_orchestration(context: DurableOrchestrationContext):
context.set_custom_status(
f"Human approval timed out after {payload.approval_timeout_hours} hour(s). Treating as rejection."
)
raise TimeoutError(
f"Human approval timed out after {payload.approval_timeout_hours} hour(s)."
)
raise TimeoutError(f"Human approval timed out after {payload.approval_timeout_hours} hour(s).")
raise RuntimeError(f"Content could not be approved after {payload.max_review_attempts} iteration(s).")
@@ -41,13 +41,13 @@ async def main() -> None:
print(f"User: {message}")
if stream:
response = await ChatResponse.from_chat_response_generator(
client.get_streaming_response(message, tools=get_weather, response_format=OutputStruct),
client.get_streaming_response(message, tools=get_weather, options={"response_format": OutputStruct}),
output_format_type=OutputStruct,
)
print(f"Assistant: {response.value}")
else:
response = await client.get_response(message, tools=get_weather, response_format=OutputStruct)
response = await client.get_response(message, tools=get_weather, options={"response_format": OutputStruct})
print(f"Assistant: {response.value}")
@@ -48,7 +48,7 @@ class UserInfoMemory(ContextProvider):
messages=request_messages, # type: ignore
instructions="Extract the user's name and age from the message if present. "
"If not present return nulls.",
response_format=UserInfo,
options={"response_format": UserInfo},
)
# Update user info with extracted data
@@ -86,7 +86,7 @@ reviewer = chat_client.create_agent(
"- feedback: concise, actionable feedback\n"
"- clarity, completeness, accuracy, structure: individual scores (0-100)"
),
response_format=ReviewResult,
default_options={"response_format": ReviewResult},
)
# Create Editor agent - improves content based on feedback
@@ -100,7 +100,7 @@ class Reviewer(Executor):
messages.append(ChatMessage(role=Role.USER, text="Please review the agent's responses."))
print("Reviewer: Sending review request to LLM...")
response = await self._chat_client.get_response(messages=messages, response_format=_Response)
response = await self._chat_client.get_response(messages=messages, options={"response_format": _Response})
parsed = _Response.model_validate_json(response.messages[-1].text)
@@ -138,7 +138,7 @@ def create_spam_detector_agent() -> ChatAgent:
"Include the original email content in email_content."
),
name="spam_detection_agent",
response_format=DetectionResult,
default_options={"response_format": DetectionResult},
)
@@ -152,7 +152,7 @@ def create_email_assistant_agent() -> ChatAgent:
"Return JSON with a single field 'response' containing the drafted reply."
),
name="email_assistant_agent",
response_format=EmailResponse,
default_options={"response_format": EmailResponse},
)
@@ -190,7 +190,7 @@ def create_email_analysis_agent() -> ChatAgent:
"and 'reason' (string)."
),
name="email_analysis_agent",
response_format=AnalysisResultAgent,
default_options={"response_format": AnalysisResultAgent},
)
@@ -199,7 +199,7 @@ def create_email_assistant_agent() -> ChatAgent:
return AzureOpenAIChatClient(credential=AzureCliCredential()).create_agent(
instructions=("You are an email assistant that helps users draft responses to emails with professionalism."),
name="email_assistant_agent",
response_format=EmailResponse,
default_options={"response_format": EmailResponse},
)
@@ -208,7 +208,7 @@ def create_email_summary_agent() -> ChatAgent:
return AzureOpenAIChatClient(credential=AzureCliCredential()).create_agent(
instructions=("You are an assistant that helps users summarize emails."),
name="email_summary_agent",
response_format=EmailSummaryModel,
default_options={"response_format": EmailSummaryModel},
)
@@ -243,7 +243,8 @@ async def main() -> None:
)
workflow = (
workflow_builder.set_start_executor("store_email")
workflow_builder
.set_start_executor("store_email")
.add_edge("store_email", "email_analysis_agent")
.add_edge("email_analysis_agent", "to_analysis_result")
.add_multi_selection_edge_group(
@@ -162,7 +162,7 @@ def create_spam_detection_agent() -> ChatAgent:
"and 'reason' (string)."
),
name="spam_detection_agent",
response_format=DetectionResultAgent,
default_options={"response_format": DetectionResultAgent},
)
@@ -171,7 +171,7 @@ def create_email_assistant_agent() -> ChatAgent:
return AzureOpenAIChatClient(credential=AzureCliCredential()).create_agent(
instructions=("You are an email assistant that helps users draft responses to emails with professionalism."),
name="email_assistant_agent",
response_format=EmailResponse,
default_options={"response_format": EmailResponse},
)
@@ -171,28 +171,28 @@ async def main() -> None:
self_service_agent = chat_client.create_agent(
name="SelfServiceAgent",
instructions=SELF_SERVICE_INSTRUCTIONS,
response_format=SelfServiceResponse,
default_options={"response_format": SelfServiceResponse},
)
ticketing_agent = chat_client.create_agent(
name="TicketingAgent",
instructions=TICKETING_INSTRUCTIONS,
tools=plugin.get_functions(),
response_format=TicketingResponse,
default_options={"response_format": TicketingResponse},
)
routing_agent = chat_client.create_agent(
name="TicketRoutingAgent",
instructions=TICKET_ROUTING_INSTRUCTIONS,
tools=[plugin.get_ticket],
response_format=RoutingResponse,
default_options={"response_format": RoutingResponse},
)
windows_support_agent = chat_client.create_agent(
name="WindowsSupportAgent",
instructions=WINDOWS_SUPPORT_INSTRUCTIONS,
tools=[plugin.get_ticket],
response_format=SupportResponse,
default_options={"response_format": SupportResponse},
)
resolution_agent = chat_client.create_agent(
@@ -205,7 +205,7 @@ async def main() -> None:
name="TicketEscalationAgent",
instructions=ESCALATION_INSTRUCTIONS,
tools=[plugin.get_ticket, plugin.send_notification],
response_format=EscalationResponse,
default_options={"response_format": EscalationResponse},
)
# Agent registry for lookup
@@ -139,7 +139,7 @@ async def main() -> None:
manager_agent = chat_client.create_agent(
name="ManagerAgent",
instructions=MANAGER_INSTRUCTIONS,
response_format=ManagerResponse,
default_options={"response_format": ManagerResponse},
)
summary_agent = chat_client.create_agent(
@@ -154,7 +154,7 @@ def create_guessing_agent() -> ChatAgent:
"No explanations or additional text."
),
# response_format enforces that the model produces JSON compatible with GuessOutput.
response_format=GuessOutput,
default_options={"response_format": GuessOutput},
)
@@ -162,7 +162,7 @@ def create_spam_detection_agent() -> ChatAgent:
"You are a spam detection assistant that identifies spam emails. "
"Always return JSON with fields is_spam (bool) and reason (string)."
),
response_format=DetectionResultAgent,
default_options={"response_format": DetectionResultAgent},
# response_format enforces structured JSON from each agent.
name="spam_detection_agent",
)
@@ -176,7 +176,7 @@ def create_email_assistant_agent() -> ChatAgent:
"Return JSON with a single field 'response' containing the drafted reply."
),
# response_format enforces structured JSON from each agent.
response_format=EmailResponse,
default_options={"response_format": EmailResponse},
name="email_assistant_agent",
)
@@ -49,7 +49,7 @@ async def run_agent_framework() -> None:
# AF forwards the same response_format payload at invocation time.
reply = await chat_agent.run(
"Draft a launch brief for the Contoso Note app.",
response_format=ReleaseBrief,
options={"response_format": ReleaseBrief},
)
print("[AF]", reply.text)