diff --git a/python/samples/getting_started/middleware/exception_handling_with_middleware.py b/python/samples/getting_started/middleware/exception_handling_with_middleware.py new file mode 100644 index 0000000000..7646ff68ed --- /dev/null +++ b/python/samples/getting_started/middleware/exception_handling_with_middleware.py @@ -0,0 +1,61 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio +from collections.abc import Awaitable, Callable +from typing import Annotated + +from agent_framework import FunctionInvocationContext +from agent_framework.foundry import FoundryChatClient +from azure.identity.aio import AzureCliCredential +from pydantic import Field + + +def unstable_data_service( + query: Annotated[str, Field(description="The data query to execute.")], +) -> str: + """A simulated data service that sometimes throws exceptions.""" + # Simulate failure + raise TimeoutError("Data service request timed out") + + +async def exception_handling_middleware( + context: FunctionInvocationContext, next: Callable[[FunctionInvocationContext], Awaitable[None]] +) -> None: + function_name = context.function.name + + try: + print(f"[ExceptionHandlingMiddleware] Executing function: {function_name}") + await next(context) + print(f"[ExceptionHandlingMiddleware] Function {function_name} completed successfully.") + except TimeoutError as e: + print(f"[ExceptionHandlingMiddleware] Caught TimeoutError: {e}") + # Override function result to provide custom message in response. + context.result = ( + "Request Timeout: The data service is taking longer than expected to respond.", + "Respond with message - 'Sorry for the inconvenience, please try again later.'", + ) + + +async def main() -> None: + """Example demonstrating exception handling with middleware.""" + print("=== Exception Handling Middleware Example ===") + + # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred + # authentication option. + async with ( + AzureCliCredential() as credential, + FoundryChatClient(async_credential=credential).create_agent( + name="DataAgent", + instructions="You are a helpful data assistant. Use the data service tool to fetch information for users.", + tools=unstable_data_service, + middleware=exception_handling_middleware, + ) as agent, + ): + query = "Get user statistics" + print(f"User: {query}") + result = await agent.run(query) + print(f"Agent: {result}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/samples/getting_started/middleware/override_result_with_middleware.py b/python/samples/getting_started/middleware/override_result_with_middleware.py new file mode 100644 index 0000000000..d038dc9d8b --- /dev/null +++ b/python/samples/getting_started/middleware/override_result_with_middleware.py @@ -0,0 +1,68 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio +from collections.abc import Awaitable, Callable +from random import randint +from typing import Annotated + +from agent_framework import FunctionInvocationContext +from agent_framework.foundry import FoundryChatClient +from azure.identity.aio import AzureCliCredential +from pydantic import Field + + +def get_weather( + location: Annotated[str, Field(description="The location to get the weather for.")], +) -> str: + """Get the weather for a given location.""" + conditions = ["sunny", "cloudy", "rainy", "stormy"] + return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C." + + +async def weather_override_middleware( + context: FunctionInvocationContext, next: Callable[[FunctionInvocationContext], Awaitable[None]] +) -> None: + function_name = context.function.name + + # Let the original function execute first + await next(context) + + # Override the result if it's a weather function + if function_name == "get_weather" and context.result is not None: + original_result = str(context.result) + print(f"[WeatherOverrideMiddleware] Original result: {original_result}") + + # Override with a custom message + custom_message = ( + "Weather Advisory - due to special atmospheric conditions, " + "all locations are experiencing perfect weather today! " + "Temperature is a comfortable 22°C with gentle breezes. " + "Perfect day for outdoor activities!" + ) + context.result = custom_message + print(f"[WeatherOverrideMiddleware] Overriding with custom message: {custom_message}") + + +async def main() -> None: + """Example demonstrating result override with middleware.""" + print("=== Result Override Middleware Example ===") + + # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred + # authentication option. + async with ( + AzureCliCredential() as credential, + FoundryChatClient(async_credential=credential).create_agent( + name="WeatherAgent", + instructions="You are a helpful weather assistant. Use the weather tool to get current conditions.", + tools=get_weather, + middleware=weather_override_middleware, + ) as agent, + ): + query = "What's the weather like in Seattle?" + print(f"User: {query}") + result = await agent.run(query) + print(f"Agent: {result}") + + +if __name__ == "__main__": + asyncio.run(main())