mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: [BREAKING] Make response_format validation errors visible to users (#3274)
* Make response_format validation errors visible to users * Small fix * Addressed comments
This commit is contained in:
committed by
GitHub
Unverified
parent
3c1be2a713
commit
83e8965c8e
@@ -41,12 +41,13 @@ async def main() -> None:
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
|
||||
if isinstance(result.value, ReleaseBrief):
|
||||
release_brief = result.value
|
||||
if release_brief := result.try_parse_value(ReleaseBrief):
|
||||
print("Agent:")
|
||||
print(f"Feature: {release_brief.feature}")
|
||||
print(f"Benefit: {release_brief.benefit}")
|
||||
print(f"Launch date: {release_brief.launch_date}")
|
||||
else:
|
||||
print(f"Failed to parse response: {result.text}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
+6
-4
@@ -56,13 +56,14 @@ async def main() -> None:
|
||||
|
||||
result1 = await agent.run(query1)
|
||||
|
||||
if isinstance(result1.value, WeatherInfo):
|
||||
weather = result1.value
|
||||
if weather := result1.try_parse_value(WeatherInfo):
|
||||
print("Agent:")
|
||||
print(f" Location: {weather.location}")
|
||||
print(f" Temperature: {weather.temperature}")
|
||||
print(f" Conditions: {weather.conditions}")
|
||||
print(f" Recommendation: {weather.recommendation}")
|
||||
else:
|
||||
print(f"Failed to parse response: {result1.text}")
|
||||
|
||||
# Request 2: Override response_format at runtime with CityInfo
|
||||
print("\n--- Request 2: Runtime override with CityInfo ---")
|
||||
@@ -71,12 +72,13 @@ async def main() -> None:
|
||||
|
||||
result2 = await agent.run(query2, options={"response_format": CityInfo})
|
||||
|
||||
if isinstance(result2.value, CityInfo):
|
||||
city = result2.value
|
||||
if city := result2.try_parse_value(CityInfo):
|
||||
print("Agent:")
|
||||
print(f" City: {city.city_name}")
|
||||
print(f" Population: {city.population}")
|
||||
print(f" Country: {city.country}")
|
||||
else:
|
||||
print(f"Failed to parse response: {result2.text}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
+6
-4
@@ -59,13 +59,14 @@ async def main() -> None:
|
||||
|
||||
result1 = await agent.run(query1)
|
||||
|
||||
if isinstance(result1.value, WeatherInfo):
|
||||
weather = result1.value
|
||||
if weather := result1.try_parse_value(WeatherInfo):
|
||||
print("Agent:")
|
||||
print(f" Location: {weather.location}")
|
||||
print(f" Temperature: {weather.temperature}")
|
||||
print(f" Conditions: {weather.conditions}")
|
||||
print(f" Recommendation: {weather.recommendation}")
|
||||
else:
|
||||
print(f"Failed to parse response: {result1.text}")
|
||||
|
||||
# Request 2: Override response_format at runtime with CityInfo
|
||||
print("\n--- Request 2: Runtime override with CityInfo ---")
|
||||
@@ -74,12 +75,13 @@ async def main() -> None:
|
||||
|
||||
result2 = await agent.run(query2, options={"response_format": CityInfo})
|
||||
|
||||
if isinstance(result2.value, CityInfo):
|
||||
city = result2.value
|
||||
if city := result2.try_parse_value(CityInfo):
|
||||
print("Agent:")
|
||||
print(f" City: {city.city_name}")
|
||||
print(f" Population: {city.population}")
|
||||
print(f" Country: {city.country}")
|
||||
else:
|
||||
print(f"Failed to parse response: {result2.text}")
|
||||
finally:
|
||||
await client.beta.assistants.delete(agent.id)
|
||||
|
||||
|
||||
+7
-9
@@ -37,14 +37,13 @@ async def non_streaming_example() -> None:
|
||||
# Get structured response from the agent using response_format parameter
|
||||
result = await agent.run(query, options={"response_format": OutputStruct})
|
||||
|
||||
# Access the structured output directly from the response value
|
||||
if result.value:
|
||||
structured_data: OutputStruct = result.value # type: ignore
|
||||
print("Structured Output Agent (from result.value):")
|
||||
# Access the structured output using try_parse_value for safe parsing
|
||||
if structured_data := result.try_parse_value(OutputStruct):
|
||||
print("Structured Output Agent (from result.try_parse_value):")
|
||||
print(f"City: {structured_data.city}")
|
||||
print(f"Description: {structured_data.description}")
|
||||
else:
|
||||
print("Error: No structured data found in result.value")
|
||||
print(f"Failed to parse response: {result.text}")
|
||||
|
||||
|
||||
async def streaming_example() -> None:
|
||||
@@ -67,14 +66,13 @@ async def streaming_example() -> None:
|
||||
output_format_type=OutputStruct,
|
||||
)
|
||||
|
||||
# Access the structured output directly from the response value
|
||||
if result.value:
|
||||
structured_data: OutputStruct = result.value # type: ignore
|
||||
# Access the structured output using try_parse_value for safe parsing
|
||||
if structured_data := result.try_parse_value(OutputStruct):
|
||||
print("Structured Output (from streaming with AgentResponse.from_agent_response_generator):")
|
||||
print(f"City: {structured_data.city}")
|
||||
print(f"Description: {structured_data.description}")
|
||||
else:
|
||||
print("Error: No structured data found in result.value")
|
||||
print(f"Failed to parse response: {result.text}")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
|
||||
+7
-3
@@ -12,7 +12,7 @@ Functions host."""
|
||||
import json
|
||||
import logging
|
||||
from collections.abc import Mapping
|
||||
from typing import Any, cast
|
||||
from typing import Any
|
||||
|
||||
import azure.functions as func
|
||||
from agent_framework.azure import AgentFunctionApp, AzureOpenAIChatClient
|
||||
@@ -102,7 +102,9 @@ def spam_detection_orchestration(context: DurableOrchestrationContext):
|
||||
options={"response_format": SpamDetectionResult},
|
||||
)
|
||||
|
||||
spam_result = cast(SpamDetectionResult, spam_result_raw.value)
|
||||
spam_result = spam_result_raw.try_parse_value(SpamDetectionResult)
|
||||
if spam_result is None:
|
||||
raise ValueError("Failed to parse spam detection result")
|
||||
|
||||
if spam_result.is_spam:
|
||||
result = yield context.call_activity("handle_spam_email", spam_result.reason)
|
||||
@@ -123,7 +125,9 @@ def spam_detection_orchestration(context: DurableOrchestrationContext):
|
||||
options={"response_format": EmailResponse},
|
||||
)
|
||||
|
||||
email_result = cast(EmailResponse, email_result_raw.value)
|
||||
email_result = email_result_raw.try_parse_value(EmailResponse)
|
||||
if email_result is None:
|
||||
raise ValueError("Failed to parse email response")
|
||||
|
||||
result = yield context.call_activity("send_email", email_result.response)
|
||||
return result
|
||||
|
||||
+4
-6
@@ -101,10 +101,10 @@ def content_generation_hitl_orchestration(context: DurableOrchestrationContext):
|
||||
options={"response_format": GeneratedContent},
|
||||
)
|
||||
|
||||
content = initial_raw.value
|
||||
content = initial_raw.try_parse_value(GeneratedContent)
|
||||
logger.info("Type of content after extraction: %s", type(content))
|
||||
|
||||
if content is None or not isinstance(content, GeneratedContent):
|
||||
if content is None:
|
||||
raise ValueError("Agent returned no content after extraction.")
|
||||
|
||||
attempt = 0
|
||||
@@ -146,11 +146,9 @@ def content_generation_hitl_orchestration(context: DurableOrchestrationContext):
|
||||
options={"response_format": GeneratedContent},
|
||||
)
|
||||
|
||||
rewritten_value = rewritten_raw.value
|
||||
if rewritten_value is None or not isinstance(rewritten_value, GeneratedContent):
|
||||
content = rewritten_raw.try_parse_value(GeneratedContent)
|
||||
if content is None:
|
||||
raise ValueError("Agent returned no content after rewrite.")
|
||||
|
||||
content = rewritten_value
|
||||
else:
|
||||
context.set_custom_status(
|
||||
f"Human approval timed out after {payload.approval_timeout_hours} hour(s). Treating as rejection."
|
||||
|
||||
@@ -44,11 +44,16 @@ async def main() -> None:
|
||||
client.get_streaming_response(message, tools=get_weather, options={"response_format": OutputStruct}),
|
||||
output_format_type=OutputStruct,
|
||||
)
|
||||
print(f"Assistant: {response.value}")
|
||||
|
||||
if result := response.try_parse_value(OutputStruct):
|
||||
print(f"Assistant: {result}")
|
||||
else:
|
||||
print(f"Assistant: {response.text}")
|
||||
else:
|
||||
response = await client.get_response(message, tools=get_weather, options={"response_format": OutputStruct})
|
||||
print(f"Assistant: {response.value}")
|
||||
if result := response.try_parse_value(OutputStruct):
|
||||
print(f"Assistant: {result}")
|
||||
else:
|
||||
print(f"Assistant: {response.text}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -52,11 +52,11 @@ class UserInfoMemory(ContextProvider):
|
||||
)
|
||||
|
||||
# Update user info with extracted data
|
||||
if result.value and isinstance(result.value, UserInfo):
|
||||
if self.user_info.name is None and result.value.name:
|
||||
self.user_info.name = result.value.name
|
||||
if self.user_info.age is None and result.value.age:
|
||||
self.user_info.age = result.value.age
|
||||
if extracted := result.try_parse_value(UserInfo):
|
||||
if self.user_info.name is None and extracted.name:
|
||||
self.user_info.name = extracted.name
|
||||
if self.user_info.age is None and extracted.age:
|
||||
self.user_info.age = extracted.age
|
||||
|
||||
except Exception:
|
||||
pass # Failed to extract, continue without updating
|
||||
|
||||
@@ -20,7 +20,11 @@ async def main():
|
||||
agent = AgentFactory(client_kwargs={"credential": AzureCliCredential()}).create_agent_from_yaml(yaml_str)
|
||||
# use the agent
|
||||
response = await agent.run("Why is the sky blue, answer in Dutch?")
|
||||
print("Agent response:", response.value.model_dump_json(indent=2))
|
||||
# Use try_parse_value() for safe parsing - returns None if no response_format or parsing fails
|
||||
if parsed := response.try_parse_value():
|
||||
print("Agent response:", parsed.model_dump_json(indent=2))
|
||||
else:
|
||||
print("Agent response:", response.text)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -19,7 +19,11 @@ async def main():
|
||||
agent = AgentFactory().create_agent_from_yaml(yaml_str)
|
||||
# use the agent
|
||||
response = await agent.run("Why is the sky blue, answer in Dutch?")
|
||||
print("Agent response:", response.value)
|
||||
# Use try_parse_value() for safe parsing - returns None if no response_format or parsing fails
|
||||
if parsed := response.try_parse_value():
|
||||
print("Agent response:", parsed)
|
||||
else:
|
||||
print("Agent response:", response.text)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user