From 2ae2d106f469c6a3691456c9126ac2d22f6b8189 Mon Sep 17 00:00:00 2001 From: muhammadhamidrazasidtechno <160109562+muhammadhamidrazasidtechno@users.noreply.github.com> Date: Mon, 28 Jul 2025 20:03:04 +0500 Subject: [PATCH 01/16] Docs: Improve and clarify tool behavior definitions Enhances the "Tool Behavior Definitions" documentation to provide clearer explanations and more robust examples for agent tool usage. Key improvements include: - Explicitly defining import paths for `StopAtTools` and `ToolsToFinalOutputFunction`. - Providing comprehensive and corrected code examples for all `tool_choice` and `tool_use_behavior` configurations, including `"stop_on_first_tool"`, `StopAtTools`, and the usage of `ToolsToFinalOutputFunction`. - Ensuring proper Markdown formatting for code blocks and notes to enhance readability and accuracy. This update aims to significantly reduce ambiguity and improve the developer experience by offering ready-to-use and well-explained code snippets. --- docs/agents.md | 101 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/docs/agents.md b/docs/agents.md index d6b719824..1c0d2e4a9 100644 --- a/docs/agents.md +++ b/docs/agents.md @@ -142,8 +142,107 @@ Supplying a list of tools doesn't always mean the LLM will use a tool. You can f 3. `none`, which requires the LLM to _not_ use a tool. 4. Setting a specific string e.g. `my_tool`, which requires the LLM to use that specific tool. +```python +from agents import Agent, Runner, function_tool, ModelSettings + +@function_tool +def get_stock_price(ticker: str) -> str: + """Fetches the stock price for a given ticker symbol.""" + prices = {"AAPL": "$150.00", "GOOGL": "$2750.00"} + return prices.get(ticker, "Stock not found") + +agent = Agent( + name="Stock Price Agent", + instructions="Retrieve the stock price if relevant, otherwise respond directly.", + tools=[get_stock_price], + model_settings=ModelSettings(tool_choice="get_stock_price") +) + +``` + +## Tool Use Behavior + +The `tool_use_behavior` parameter in the `Agent` configuration controls how tool outputs are handled: +- `"run_llm_again"`: The default. Tools are run, and the LLM processes the results to produce a final response. +- `"stop_on_first_tool"`: The output of the first tool call is used as the final response, without further LLM processing. + +```python +from agents import Agent, Runner, function_tool, ModelSettings + +@function_tool +def get_stock_price(ticker: str) -> str: + """Fetches the stock price for a given ticker symbol.""" + prices = {"AAPL": "$150.00", "GOOGL": "$2750.00"} + return prices.get(ticker, "Stock not found") + +# Create the agent +agent = Agent( + name="Stock Price Agent", + instructions="Retrieve the stock price.", + tools=[get_stock_price], + tool_use_behavior="stop_on_first_tool" +) +``` + +- `StopAtTools(stop_at_tool_names=[...])`: Stops if any specified tool is called, using its output as the final response. +```python +from agents import Agent, Runner, function_tool +from agents.agent import StopAtTools + +@function_tool +def get_stock_price(ticker: str) -> str: + """Fetches the stock price for a given ticker symbol.""" + prices = {"AAPL": "$150.00", "GOOGL": "$2750.00"} + return prices.get(ticker, "Stock not found") + +agent = Agent( + name="Stop At Stock Agent", + instructions="Get stock price and stop.", + tools=[get_stock_price], + tool_use_behavior=StopAtTools(stop_at_tool_names=["get_stock_price"]) +) +``` +- `ToolsToFinalOutputFunction`: A custom function that processes tool results and decides whether to stop or continue with the LLM. + +```python + +from agents import Agent, Runner, function_tool, FunctionToolResult, RunContextWrapper +from agents.agent import ToolsToFinalOutputResult +from typing import List, Any + +@function_tool +def get_product_price(product_id: str) -> str: + """Fetches the price for a given product ID.""" + prices = {"P101": "$25.00", "P102": "$49.99"} + return prices.get(product_id, "Product not found") + +def custom_tool_handler( + context: RunContextWrapper[Any], + tool_results: List[FunctionToolResult] +) -> ToolsToFinalOutputResult: + """Processes tool results to decide final output.""" + for result in tool_results: + if result.output and "$25.00" in result.output: + return ToolsToFinalOutputResult( + is_final_output=True, + final_output=f"Final result: {result.output}" + ) + return ToolsToFinalOutputResult( + is_final_output=False, + final_output=None + ) + +# Create the agent +agent = Agent( + name="Product Price Agent", + instructions="Retrieve product prices and format them.", + tools=[get_product_price], + tool_use_behavior=custom_tool_handler +) + +``` + !!! note To prevent infinite loops, the framework automatically resets `tool_choice` to "auto" after a tool call. This behavior is configurable via [`agent.reset_tool_choice`][agents.agent.Agent.reset_tool_choice]. The infinite loop is because tool results are sent to the LLM, which then generates another tool call because of `tool_choice`, ad infinitum. - If you want the Agent to completely stop after a tool call (rather than continuing with auto mode), you can set [`Agent.tool_use_behavior="stop_on_first_tool"`] which will directly use the tool output as the final response without further LLM processing. From c0b68beba2c0f6b7e8fbdc32809ba016d9271c06 Mon Sep 17 00:00:00 2001 From: muhammadhamidrazasidtechno <160109562+muhammadhamidrazasidtechno@users.noreply.github.com> Date: Tue, 29 Jul 2025 11:31:14 +0500 Subject: [PATCH 02/16] Update agents.md --- docs/agents.md | 65 +++++++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/docs/agents.md b/docs/agents.md index 1c0d2e4a9..df0d3cfab 100644 --- a/docs/agents.md +++ b/docs/agents.md @@ -146,16 +146,15 @@ Supplying a list of tools doesn't always mean the LLM will use a tool. You can f from agents import Agent, Runner, function_tool, ModelSettings @function_tool -def get_stock_price(ticker: str) -> str: - """Fetches the stock price for a given ticker symbol.""" - prices = {"AAPL": "$150.00", "GOOGL": "$2750.00"} - return prices.get(ticker, "Stock not found") +def get_weather(city: str) -> str: + """Returns weather info for the specified city.""" + return f"The weather in {city} is sunny" agent = Agent( - name="Stock Price Agent", - instructions="Retrieve the stock price if relevant, otherwise respond directly.", - tools=[get_stock_price], - model_settings=ModelSettings(tool_choice="get_stock_price") + name="Weather Agent", + instructions="Retrieve weather details.", + tools=[get_weather], + model_settings=ModelSettings(tool_choice="get_weather") ) ``` @@ -170,16 +169,14 @@ The `tool_use_behavior` parameter in the `Agent` configuration controls how tool from agents import Agent, Runner, function_tool, ModelSettings @function_tool -def get_stock_price(ticker: str) -> str: - """Fetches the stock price for a given ticker symbol.""" - prices = {"AAPL": "$150.00", "GOOGL": "$2750.00"} - return prices.get(ticker, "Stock not found") +def get_weather(city: str) -> str: + """Returns weather info for the specified city.""" + return f"The weather in {city} is sunny" -# Create the agent agent = Agent( - name="Stock Price Agent", - instructions="Retrieve the stock price.", - tools=[get_stock_price], + name="Weather Agent", + instructions="Retrieve weather details.", + tools=[get_weather], tool_use_behavior="stop_on_first_tool" ) ``` @@ -190,16 +187,20 @@ from agents import Agent, Runner, function_tool from agents.agent import StopAtTools @function_tool -def get_stock_price(ticker: str) -> str: - """Fetches the stock price for a given ticker symbol.""" - prices = {"AAPL": "$150.00", "GOOGL": "$2750.00"} - return prices.get(ticker, "Stock not found") +def get_weather(city: str) -> str: + """Returns weather info for the specified city.""" + return f"The weather in {city} is sunny" + +@function_tool +def sum_numbers(a: int, b: int) -> int: + """Adds two numbers.""" + return a + b agent = Agent( name="Stop At Stock Agent", - instructions="Get stock price and stop.", - tools=[get_stock_price], - tool_use_behavior=StopAtTools(stop_at_tool_names=["get_stock_price"]) + instructions="Get weather or sum numbers.", + tools=[get_weather, sum_numbers], + tool_use_behavior=StopAtTools(stop_at_tool_names=["get_weather"]) ) ``` - `ToolsToFinalOutputFunction`: A custom function that processes tool results and decides whether to stop or continue with the LLM. @@ -211,10 +212,9 @@ from agents.agent import ToolsToFinalOutputResult from typing import List, Any @function_tool -def get_product_price(product_id: str) -> str: - """Fetches the price for a given product ID.""" - prices = {"P101": "$25.00", "P102": "$49.99"} - return prices.get(product_id, "Product not found") +def get_weather(city: str) -> str: + """Returns weather info for the specified city.""" + return f"The weather in {city} is sunny" def custom_tool_handler( context: RunContextWrapper[Any], @@ -222,21 +222,20 @@ def custom_tool_handler( ) -> ToolsToFinalOutputResult: """Processes tool results to decide final output.""" for result in tool_results: - if result.output and "$25.00" in result.output: + if result.output and "sunny" in result.output: return ToolsToFinalOutputResult( is_final_output=True, - final_output=f"Final result: {result.output}" + final_output=f"Final weather: {result.output}" ) return ToolsToFinalOutputResult( is_final_output=False, final_output=None ) -# Create the agent agent = Agent( - name="Product Price Agent", - instructions="Retrieve product prices and format them.", - tools=[get_product_price], + name="Weather Agent", + instructions="Retrieve weather details.", + tools=[get_weather], tool_use_behavior=custom_tool_handler ) From 49c37d9b05ab2de8cb7395411b14cea357befdb3 Mon Sep 17 00:00:00 2001 From: muhammadhamidrazasidtechno <160109562+muhammadhamidrazasidtechno@users.noreply.github.com> Date: Tue, 29 Jul 2025 11:51:41 +0500 Subject: [PATCH 03/16] Drastically Improve Agent Tool Usage Clarity for All Developers This update significantly enhances the "Tool Behavior Definitions" documentation, directly addressing the common challenges and wasted time developers previously experienced. Without clear examples and explicit guidance on import paths and usage patterns, implementing advanced agent tool behaviors was often a source of confusion and trial-and-error. **Key improvements in this update include:** - **Explicitly defining crucial import paths** for `StopAtTools` and `ToolsToFinalOutputFunction`, removing guesswork. - **Providing comprehensive and corrected code examples** for all `tool_choice` and `tool_use_behavior` configurations, including `"stop_on_first_tool"`, `StopAtTools`, and `ToolsToFinalOutputFunction`. These examples are now streamlined and use consistent, easy-to-understand tools like `get_weather`. - **Ensuring proper Markdown formatting** for code blocks and notes to enhance readability and accuracy. My personal experience, including significant time spent troubleshooting these very behaviors due to lack of clear examples, fueled this contribution. This update aims to drastically reduce ambiguity and improve the developer experience by offering ready-to-use and well-explained code snippets, saving countless hours for others. --- docs/agents.md | 68 +++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/docs/agents.md b/docs/agents.md index 620112e0e..c220779ce 100644 --- a/docs/agents.md +++ b/docs/agents.md @@ -147,15 +147,16 @@ Supplying a list of tools doesn't always mean the LLM will use a tool. You can f from agents import Agent, Runner, function_tool, ModelSettings @function_tool -def get_weather(city: str) -> str: - """Returns weather info for the specified city.""" - return f"The weather in {city} is sunny" +def get_stock_price(ticker: str) -> str: + """Fetches the stock price for a given ticker symbol.""" + prices = {"AAPL": "$150.00", "GOOGL": "$2750.00"} + return prices.get(ticker, "Stock not found") agent = Agent( - name="Weather Agent", - instructions="Retrieve weather details.", - tools=[get_weather], - model_settings=ModelSettings(tool_choice="get_weather") + name="Stock Price Agent", + instructions="Retrieve the stock price if relevant, otherwise respond directly.", + tools=[get_stock_price], + model_settings=ModelSettings(tool_choice="get_stock_price") ) ``` @@ -170,14 +171,16 @@ The `tool_use_behavior` parameter in the `Agent` configuration controls how tool from agents import Agent, Runner, function_tool, ModelSettings @function_tool -def get_weather(city: str) -> str: - """Returns weather info for the specified city.""" - return f"The weather in {city} is sunny" +def get_stock_price(ticker: str) -> str: + """Fetches the stock price for a given ticker symbol.""" + prices = {"AAPL": "$150.00", "GOOGL": "$2750.00"} + return prices.get(ticker, "Stock not found") +# Create the agent agent = Agent( - name="Weather Agent", - instructions="Retrieve weather details.", - tools=[get_weather], + name="Stock Price Agent", + instructions="Retrieve the stock price.", + tools=[get_stock_price], tool_use_behavior="stop_on_first_tool" ) ``` @@ -188,20 +191,16 @@ from agents import Agent, Runner, function_tool from agents.agent import StopAtTools @function_tool -def get_weather(city: str) -> str: - """Returns weather info for the specified city.""" - return f"The weather in {city} is sunny" - -@function_tool -def sum_numbers(a: int, b: int) -> int: - """Adds two numbers.""" - return a + b +def get_stock_price(ticker: str) -> str: + """Fetches the stock price for a given ticker symbol.""" + prices = {"AAPL": "$150.00", "GOOGL": "$2750.00"} + return prices.get(ticker, "Stock not found") agent = Agent( name="Stop At Stock Agent", - instructions="Get weather or sum numbers.", - tools=[get_weather, sum_numbers], - tool_use_behavior=StopAtTools(stop_at_tool_names=["get_weather"]) + instructions="Get stock price and stop.", + tools=[get_stock_price], + tool_use_behavior=StopAtTools(stop_at_tool_names=["get_stock_price"]) ) ``` - `ToolsToFinalOutputFunction`: A custom function that processes tool results and decides whether to stop or continue with the LLM. @@ -213,9 +212,10 @@ from agents.agent import ToolsToFinalOutputResult from typing import List, Any @function_tool -def get_weather(city: str) -> str: - """Returns weather info for the specified city.""" - return f"The weather in {city} is sunny" +def get_product_price(product_id: str) -> str: + """Fetches the price for a given product ID.""" + prices = {"P101": "$25.00", "P102": "$49.99"} + return prices.get(product_id, "Product not found") def custom_tool_handler( context: RunContextWrapper[Any], @@ -223,26 +223,26 @@ def custom_tool_handler( ) -> ToolsToFinalOutputResult: """Processes tool results to decide final output.""" for result in tool_results: - if result.output and "sunny" in result.output: + if result.output and "$25.00" in result.output: return ToolsToFinalOutputResult( is_final_output=True, - final_output=f"Final weather: {result.output}" + final_output=f"Final result: {result.output}" ) return ToolsToFinalOutputResult( is_final_output=False, final_output=None ) +# Create the agent agent = Agent( - name="Weather Agent", - instructions="Retrieve weather details.", - tools=[get_weather], + name="Product Price Agent", + instructions="Retrieve product prices and format them.", + tools=[get_product_price], tool_use_behavior=custom_tool_handler ) ``` - -!!! note + !!! note To prevent infinite loops, the framework automatically resets `tool_choice` to "auto" after a tool call. This behavior is configurable via [`agent.reset_tool_choice`][agents.agent.Agent.reset_tool_choice]. The infinite loop is because tool results are sent to the LLM, which then generates another tool call because of `tool_choice`, ad infinitum. From ff935671df9083a67a951c62305c1b991296d557 Mon Sep 17 00:00:00 2001 From: muhammadhamidrazasidtechno <160109562+muhammadhamidrazasidtechno@users.noreply.github.com> Date: Tue, 29 Jul 2025 11:52:26 +0500 Subject: [PATCH 04/16] Drastically Improve Agent Tool Usage Clarity for All Developers This update significantly enhances the "Tool Behavior Definitions" documentation, directly addressing the common challenges and wasted time developers previously experienced. Without clear examples and explicit guidance on import paths and usage patterns, implementing advanced agent tool behaviors was often a source of confusion and trial-and-error. **Key improvements in this update include:** - **Explicitly defining crucial import paths** for `StopAtTools` and `ToolsToFinalOutputFunction`, removing guesswork. - **Providing comprehensive and corrected code examples** for all `tool_choice` and `tool_use_behavior` configurations, including `"stop_on_first_tool"`, `StopAtTools`, and `ToolsToFinalOutputFunction`. These examples are now streamlined and use consistent, easy-to-understand tools like `get_weather`. - **Ensuring proper Markdown formatting** for code blocks and notes to enhance readability and accuracy. My personal experience, including significant time spent troubleshooting these very behaviors due to lack of clear examples, fueled this contribution. This update aims to drastically reduce ambiguity and improve the developer experience by offering ready-to-use and well-explained code snippets, saving countless hours for others. --- docs/agents.md | 68 +++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/docs/agents.md b/docs/agents.md index c220779ce..620112e0e 100644 --- a/docs/agents.md +++ b/docs/agents.md @@ -147,16 +147,15 @@ Supplying a list of tools doesn't always mean the LLM will use a tool. You can f from agents import Agent, Runner, function_tool, ModelSettings @function_tool -def get_stock_price(ticker: str) -> str: - """Fetches the stock price for a given ticker symbol.""" - prices = {"AAPL": "$150.00", "GOOGL": "$2750.00"} - return prices.get(ticker, "Stock not found") +def get_weather(city: str) -> str: + """Returns weather info for the specified city.""" + return f"The weather in {city} is sunny" agent = Agent( - name="Stock Price Agent", - instructions="Retrieve the stock price if relevant, otherwise respond directly.", - tools=[get_stock_price], - model_settings=ModelSettings(tool_choice="get_stock_price") + name="Weather Agent", + instructions="Retrieve weather details.", + tools=[get_weather], + model_settings=ModelSettings(tool_choice="get_weather") ) ``` @@ -171,16 +170,14 @@ The `tool_use_behavior` parameter in the `Agent` configuration controls how tool from agents import Agent, Runner, function_tool, ModelSettings @function_tool -def get_stock_price(ticker: str) -> str: - """Fetches the stock price for a given ticker symbol.""" - prices = {"AAPL": "$150.00", "GOOGL": "$2750.00"} - return prices.get(ticker, "Stock not found") +def get_weather(city: str) -> str: + """Returns weather info for the specified city.""" + return f"The weather in {city} is sunny" -# Create the agent agent = Agent( - name="Stock Price Agent", - instructions="Retrieve the stock price.", - tools=[get_stock_price], + name="Weather Agent", + instructions="Retrieve weather details.", + tools=[get_weather], tool_use_behavior="stop_on_first_tool" ) ``` @@ -191,16 +188,20 @@ from agents import Agent, Runner, function_tool from agents.agent import StopAtTools @function_tool -def get_stock_price(ticker: str) -> str: - """Fetches the stock price for a given ticker symbol.""" - prices = {"AAPL": "$150.00", "GOOGL": "$2750.00"} - return prices.get(ticker, "Stock not found") +def get_weather(city: str) -> str: + """Returns weather info for the specified city.""" + return f"The weather in {city} is sunny" + +@function_tool +def sum_numbers(a: int, b: int) -> int: + """Adds two numbers.""" + return a + b agent = Agent( name="Stop At Stock Agent", - instructions="Get stock price and stop.", - tools=[get_stock_price], - tool_use_behavior=StopAtTools(stop_at_tool_names=["get_stock_price"]) + instructions="Get weather or sum numbers.", + tools=[get_weather, sum_numbers], + tool_use_behavior=StopAtTools(stop_at_tool_names=["get_weather"]) ) ``` - `ToolsToFinalOutputFunction`: A custom function that processes tool results and decides whether to stop or continue with the LLM. @@ -212,10 +213,9 @@ from agents.agent import ToolsToFinalOutputResult from typing import List, Any @function_tool -def get_product_price(product_id: str) -> str: - """Fetches the price for a given product ID.""" - prices = {"P101": "$25.00", "P102": "$49.99"} - return prices.get(product_id, "Product not found") +def get_weather(city: str) -> str: + """Returns weather info for the specified city.""" + return f"The weather in {city} is sunny" def custom_tool_handler( context: RunContextWrapper[Any], @@ -223,26 +223,26 @@ def custom_tool_handler( ) -> ToolsToFinalOutputResult: """Processes tool results to decide final output.""" for result in tool_results: - if result.output and "$25.00" in result.output: + if result.output and "sunny" in result.output: return ToolsToFinalOutputResult( is_final_output=True, - final_output=f"Final result: {result.output}" + final_output=f"Final weather: {result.output}" ) return ToolsToFinalOutputResult( is_final_output=False, final_output=None ) -# Create the agent agent = Agent( - name="Product Price Agent", - instructions="Retrieve product prices and format them.", - tools=[get_product_price], + name="Weather Agent", + instructions="Retrieve weather details.", + tools=[get_weather], tool_use_behavior=custom_tool_handler ) ``` - !!! note + +!!! note To prevent infinite loops, the framework automatically resets `tool_choice` to "auto" after a tool call. This behavior is configurable via [`agent.reset_tool_choice`][agents.agent.Agent.reset_tool_choice]. The infinite loop is because tool results are sent to the LLM, which then generates another tool call because of `tool_choice`, ad infinitum. From b18e888a4beda3a0e4c10a409e9d9a5f57f22b2c Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Tue, 29 Jul 2025 17:17:18 +0900 Subject: [PATCH 05/16] Apply suggestions from code review --- docs/agents.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/agents.md b/docs/agents.md index 620112e0e..30058fa48 100644 --- a/docs/agents.md +++ b/docs/agents.md @@ -157,7 +157,6 @@ agent = Agent( tools=[get_weather], model_settings=ModelSettings(tool_choice="get_weather") ) - ``` ## Tool Use Behavior @@ -207,7 +206,6 @@ agent = Agent( - `ToolsToFinalOutputFunction`: A custom function that processes tool results and decides whether to stop or continue with the LLM. ```python - from agents import Agent, Runner, function_tool, FunctionToolResult, RunContextWrapper from agents.agent import ToolsToFinalOutputResult from typing import List, Any @@ -239,7 +237,6 @@ agent = Agent( tools=[get_weather], tool_use_behavior=custom_tool_handler ) - ``` !!! note From d7e6099fb42c9da48eec36ecb2f49be0a12c997b Mon Sep 17 00:00:00 2001 From: muhammadhamidrazasidtechno <160109562+muhammadhamidrazasidtechno@users.noreply.github.com> Date: Thu, 31 Jul 2025 07:02:09 +0500 Subject: [PATCH 06/16] update all --- examples/exceptions/agents_exception.py | 59 ++++++++++++++++++ .../input_guardrail_tripwire_triggered.py | 56 +++++++++++++++++ examples/exceptions/max_turns_exceeded.py | 42 +++++++++++++ examples/exceptions/model_behavior_error.py | 34 +++++++++++ .../output_guardrail_tripwire_triggered.py | 60 +++++++++++++++++++ examples/exceptions/user_error.py | 36 +++++++++++ 6 files changed, 287 insertions(+) create mode 100644 examples/exceptions/agents_exception.py create mode 100644 examples/exceptions/input_guardrail_tripwire_triggered.py create mode 100644 examples/exceptions/max_turns_exceeded.py create mode 100644 examples/exceptions/model_behavior_error.py create mode 100644 examples/exceptions/output_guardrail_tripwire_triggered.py create mode 100644 examples/exceptions/user_error.py diff --git a/examples/exceptions/agents_exception.py b/examples/exceptions/agents_exception.py new file mode 100644 index 000000000..9f47524e0 --- /dev/null +++ b/examples/exceptions/agents_exception.py @@ -0,0 +1,59 @@ +from __future__ import annotations +from agents import Agent, RunContextWrapper, Runner, function_tool +from agents.exceptions import AgentsException +import asyncio + + +""" +This example demonstrates the use of the OpenAI Agents SDK with tools and comprehensive error handling. + +The agent, 'Triage Agent', is configured to handle two tasks: +- Fetching weather information for a specified city using the `get_weather` tool. +- Adding two numbers using the `sum_numbers` tool. + +The agent is instructed to use only one tool per execution cycle and can switch to another tool in subsequent cycles. +The example sets a `max_turns=1` limit to intentionally restrict the agent to a single turn, which may trigger a `MaxTurnsExceeded` error if the agent attempts multiple tool calls. + +Error handling is implemented with `AgentsException`, which is the base class for all SDK-related exceptions, including: +- `MaxTurnsExceeded`: Raised when the run exceeds the `max_turns` specified in the run methods. +- `ModelBehaviorError`: Raised when the model produces invalid outputs, e.g., malformed JSON or using non-existent tools. +- `UserError`: Raised when the SDK user makes an error in code implementation. +- `InputGuardrailTripwireTriggered`: Raised when an input guardrail is violated (e.g., invalid or off-topic input). +- `OutputGuardrailTripwireTriggered`: Raised when an output guardrail is violated (e.g., invalid tool output). + +Although this example does not include explicit guardrails, the structure supports adding input/output guardrails to validate user inputs or tool outputs. The `AgentsException` catch block ensures all SDK-related errors are handled gracefully. +""" + + + +@function_tool +def get_weather(city: str) -> str: + """Returns weather info for the specified city.""" + return f"The weather in {city} is sunny" + + +@function_tool +def sum_numbers(a: int, b: int) -> int: + """Adds two numbers.""" + return a + b + + +agent = Agent( + name="Triage Agent", + instructions="Get weather or sum numbers. You can use one tool at a time, switching to another tool in subsequent turns.", + tools=[sum_numbers, get_weather], +) + + +async def main(): + try: + user_input = input("Enter a message: ") + + result = await Runner.run(agent, user_input, max_turns=1) + print(result.final_output) + except AgentsException as e: + print(f"Caught AgentsException: {e}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/exceptions/input_guardrail_tripwire_triggered.py b/examples/exceptions/input_guardrail_tripwire_triggered.py new file mode 100644 index 000000000..d31cbaf52 --- /dev/null +++ b/examples/exceptions/input_guardrail_tripwire_triggered.py @@ -0,0 +1,56 @@ +from __future__ import annotations +import asyncio +from pydantic import BaseModel +from agents import ( + Agent, + GuardrailFunctionOutput, + InputGuardrailTripwireTriggered, + Runner, + input_guardrail, +) +from agents.exceptions import AgentsException + +""" +This example demonstrates an OpenAI Agents SDK agent with an input guardrail to block math homework queries. + +The 'CustomerSupportAgent' processes user queries provided as direct string inputs in an interactive loop. A guardrail, implemented via 'GuardrailAgent' and a Pydantic model (`MathHomeworkOutput`), checks if the input is a math homework question. If detected, the guardrail raises `InputGuardrailTripwireTriggered`, triggering a refusal message ("Sorry, I can't help with math homework."). Otherwise, the agent responds to the query. The loop continues to prompt for new inputs, handling each independently. +""" + +class MathHomeworkOutput(BaseModel): + is_math_homework: bool + + +guardrail_agent = Agent( + name="GuardrailAgent", + instructions="Check if the input is a math homework question.", + output_type=MathHomeworkOutput, +) + + +@input_guardrail +async def math_guardrail(context, agent: Agent, input: str) -> GuardrailFunctionOutput: + result = await Runner.run(guardrail_agent, input) + output = result.final_output_as(MathHomeworkOutput) + return GuardrailFunctionOutput( + output_info=output, + tripwire_triggered=output.is_math_homework, + ) + + +async def main(): + agent = Agent( + name="CustomerSupportAgent", + instructions="Answer user queries.", + input_guardrails=[math_guardrail], + ) + + user_input = "What is 2 + 2" + try: + result = await Runner.run(agent, user_input) + print(result.final_output) + except InputGuardrailTripwireTriggered: + print("InputGuardrailTripwireTriggered, I can't help with math homework.") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/exceptions/max_turns_exceeded.py b/examples/exceptions/max_turns_exceeded.py new file mode 100644 index 000000000..a0452a042 --- /dev/null +++ b/examples/exceptions/max_turns_exceeded.py @@ -0,0 +1,42 @@ +from __future__ import annotations +import asyncio +from agents import Agent, Runner, function_tool +from agents.exceptions import MaxTurnsExceeded + +""" +This example demonstrates an OpenAI Agents SDK agent that triggers a MaxTurnsExceeded error. + +The 'TriageAgent' handles user queries using tools for fetching weather (`get_weather`) or adding numbers (`sum_numbers`). The instructions direct the agent to process both tasks in a single turn, but with `max_turns=1`, this causes a `MaxTurnsExceeded` error. The interactive loop processes user queries as direct string inputs, catching and displaying the `MaxTurnsExceeded` error message. +""" + +@function_tool +def get_weather(city: str) -> str: + """Returns weather info for the specified city.""" + return f"The weather in {city} is sunny" + + +@function_tool +def sum_numbers(a: int, b: int) -> int: + """Adds two numbers.""" + return a + b + + +async def main(): + agent = Agent( + name="TriageAgent", + instructions="Process both get_weather and sum_numbers in a single turn when asked for both.", + tools=[sum_numbers, get_weather], + model=model, + ) + + user_input = "What is US Weather and sum 2 + 2." + try: + result = await Runner.run(agent, user_input, max_turns=1) + print(result.final_output) + except MaxTurnsExceeded as e: + print(f"Caught MaxTurnsExceeded: {e}") + + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/exceptions/model_behavior_error.py b/examples/exceptions/model_behavior_error.py new file mode 100644 index 000000000..efac13e56 --- /dev/null +++ b/examples/exceptions/model_behavior_error.py @@ -0,0 +1,34 @@ +from __future__ import annotations +import asyncio +from pydantic import BaseModel +from typing import Literal +from agents import Agent, Runner +from agents.exceptions import ModelBehaviorError + +""" +This example demonstrates an OpenAI Agents SDK agent that triggers a ModelBehaviorError due to invalid model output. + +The 'MiniErrorBot' agent uses a Pydantic model (`Output`) requiring a `value` field with the literal 'EXPECTED_VALUE'. The instructions tell the model to return 'Hello', causing a `ModelBehaviorError` when the output fails validation. The interactive loop processes user queries as direct string inputs, catching and displaying the `ModelBehaviorError` message. +""" + +class Output(BaseModel): + value: Literal["EXPECTED_VALUE"] + + +async def main(): + agent = Agent( + name="MiniErrorBot", + instructions="Just say: Hello", + output_type=Output, + ) + + user_input = "hello" + try: + result = await Runner.run(agent, user_input) + print(result.final_output) + except ModelBehaviorError as e: + print(f"ModelBehaviorError: {e}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/exceptions/output_guardrail_tripwire_triggered.py b/examples/exceptions/output_guardrail_tripwire_triggered.py new file mode 100644 index 000000000..687db4d71 --- /dev/null +++ b/examples/exceptions/output_guardrail_tripwire_triggered.py @@ -0,0 +1,60 @@ +from __future__ import annotations +import asyncio +from pydantic import BaseModel +from agents import ( + Agent, + GuardrailFunctionOutput, + OutputGuardrailTripwireTriggered, + Runner, + output_guardrail, +) + + +""" +This example demonstrates an OpenAI Agents SDK agent with an output guardrail to block math homework responses. + +The 'Assistant' agent processes user queries provided as direct string inputs in an interactive loop. An output guardrail, using a Pydantic model (`MathHomeworkOutput`) and a guardrail agent, checks if the response is a math homework answer. If detected, the guardrail raises `OutputGuardrailTripwireTriggered`, and a refusal message is printed. The loop continues to prompt for new inputs, handling each independently. +""" + + +class MathHomeworkOutput(BaseModel): + is_math_homework: bool + + +guardrail_agent = Agent( + name="GuardrailAgent", + instructions="Check if the output is a math homework answer.", + output_type=MathHomeworkOutput, +) + + +@output_guardrail +async def math_guardrail(context, agent: Agent, output: str) -> GuardrailFunctionOutput: + result = await Runner.run(guardrail_agent, output) + output_data = result.final_output_as(MathHomeworkOutput) + return GuardrailFunctionOutput( + output_info=output_data, + tripwire_triggered=output_data.is_math_homework, + ) + + +async def main(): + agent = Agent( + name="Assistant", + instructions="Answer user queries.", + output_guardrails=[math_guardrail], + ) + + user_input = "What is 2 + 2" + + try: + result = await Runner.run(agent, user_input) + print(result.final_output) + except OutputGuardrailTripwireTriggered: + print( + "OutputGuardrailTripwireTriggered, I can't provide math homework answers." + ) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/exceptions/user_error.py b/examples/exceptions/user_error.py new file mode 100644 index 000000000..252a7c3aa --- /dev/null +++ b/examples/exceptions/user_error.py @@ -0,0 +1,36 @@ +from __future__ import annotations +import asyncio +from agents import Agent, Runner, function_tool +from agents.exceptions import UserError + + +""" +This example demonstrates an OpenAI Agents SDK agent that triggers a UserError due to incorrect SDK usage. + +The 'Assistant' agent is configured with an invalid `tool_use_behavior` (empty string) and an invalid tool (`invalid_tool`) that declares a `None` return type but returns a string. Either issue raises a `UserError` when the agent is executed, indicating improper SDK configuration by the user. The interactive loop processes user queries as direct string inputs, catching and displaying the `UserError` message. +""" + + +@function_tool +def invalid_tool() -> None: + return "I return a string" # Type mismatch triggers UserError + + +async def main(): + agent = Agent( + name="Assistant", + instructions="Use the invalid_tool to process queries.", + tools=[invalid_tool], + tool_use_behavior="invalid_tool", + model=model + ) + user_input = "Do Something." + try: + result = await Runner.run(agent, user_input) + print(result.final_output) + except UserError as e: + print(f"UserError: {e}") + + +if __name__ == "__main__": + asyncio.run(main()) \ No newline at end of file From 37bd43a4a32394953e555ac2c3a03f0331a8ddfb Mon Sep 17 00:00:00 2001 From: muhammadhamidrazasidtechno <160109562+muhammadhamidrazasidtechno@users.noreply.github.com> Date: Thu, 31 Jul 2025 07:04:58 +0500 Subject: [PATCH 07/16] remove model --- examples/exceptions/max_turns_exceeded.py | 1 - examples/exceptions/user_error.py | 1 - 2 files changed, 2 deletions(-) diff --git a/examples/exceptions/max_turns_exceeded.py b/examples/exceptions/max_turns_exceeded.py index a0452a042..a92d55d8f 100644 --- a/examples/exceptions/max_turns_exceeded.py +++ b/examples/exceptions/max_turns_exceeded.py @@ -26,7 +26,6 @@ async def main(): name="TriageAgent", instructions="Process both get_weather and sum_numbers in a single turn when asked for both.", tools=[sum_numbers, get_weather], - model=model, ) user_input = "What is US Weather and sum 2 + 2." diff --git a/examples/exceptions/user_error.py b/examples/exceptions/user_error.py index 252a7c3aa..c62e26d38 100644 --- a/examples/exceptions/user_error.py +++ b/examples/exceptions/user_error.py @@ -22,7 +22,6 @@ async def main(): instructions="Use the invalid_tool to process queries.", tools=[invalid_tool], tool_use_behavior="invalid_tool", - model=model ) user_input = "Do Something." try: From 9b1f53adb87563fc1a91c5298bcdb046d4122696 Mon Sep 17 00:00:00 2001 From: muhammadhamidrazasidtechno <160109562+muhammadhamidrazasidtechno@users.noreply.github.com> Date: Thu, 31 Jul 2025 07:07:45 +0500 Subject: [PATCH 08/16] Update input_guardrail_tripwire_triggered.py --- examples/exceptions/input_guardrail_tripwire_triggered.py | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/exceptions/input_guardrail_tripwire_triggered.py b/examples/exceptions/input_guardrail_tripwire_triggered.py index d31cbaf52..d306e802a 100644 --- a/examples/exceptions/input_guardrail_tripwire_triggered.py +++ b/examples/exceptions/input_guardrail_tripwire_triggered.py @@ -8,7 +8,6 @@ Runner, input_guardrail, ) -from agents.exceptions import AgentsException """ This example demonstrates an OpenAI Agents SDK agent with an input guardrail to block math homework queries. From fbddcb4022f721de40a7f32ffd3daacab460dbcf Mon Sep 17 00:00:00 2001 From: muhammadhamidrazasidtechno <160109562+muhammadhamidrazasidtechno@users.noreply.github.com> Date: Thu, 31 Jul 2025 07:10:08 +0500 Subject: [PATCH 09/16] Update agents_exception.py --- examples/exceptions/agents_exception.py | 36 +++++++++++-------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/examples/exceptions/agents_exception.py b/examples/exceptions/agents_exception.py index 9f47524e0..c0f827eac 100644 --- a/examples/exceptions/agents_exception.py +++ b/examples/exceptions/agents_exception.py @@ -1,7 +1,8 @@ from __future__ import annotations -from agents import Agent, RunContextWrapper, Runner, function_tool -from agents.exceptions import AgentsException + import asyncio +from agents import Agent, Runner, function_tool +from agents.exceptions import AgentsException """ @@ -12,47 +13,40 @@ - Adding two numbers using the `sum_numbers` tool. The agent is instructed to use only one tool per execution cycle and can switch to another tool in subsequent cycles. -The example sets a `max_turns=1` limit to intentionally restrict the agent to a single turn, which may trigger a `MaxTurnsExceeded` error if the agent attempts multiple tool calls. +The example sets a `max_turns=1` limit to intentionally restrict the agent to a single turn, which may trigger a `MaxTurnsExceeded` error. -Error handling is implemented with `AgentsException`, which is the base class for all SDK-related exceptions, including: -- `MaxTurnsExceeded`: Raised when the run exceeds the `max_turns` specified in the run methods. -- `ModelBehaviorError`: Raised when the model produces invalid outputs, e.g., malformed JSON or using non-existent tools. -- `UserError`: Raised when the SDK user makes an error in code implementation. -- `InputGuardrailTripwireTriggered`: Raised when an input guardrail is violated (e.g., invalid or off-topic input). -- `OutputGuardrailTripwireTriggered`: Raised when an output guardrail is violated (e.g., invalid tool output). - -Although this example does not include explicit guardrails, the structure supports adding input/output guardrails to validate user inputs or tool outputs. The `AgentsException` catch block ensures all SDK-related errors are handled gracefully. +All exceptions are caught via `AgentsException`, the base class for SDK errors. """ - +# Define tools @function_tool -def get_weather(city: str) -> str: +async def get_weather(city: str) -> str: """Returns weather info for the specified city.""" - return f"The weather in {city} is sunny" + return f"The weather in {city} is sunny." @function_tool -def sum_numbers(a: int, b: int) -> int: +async def sum_numbers(a: int, b: int) -> str: """Adds two numbers.""" - return a + b + result = a + b + return f"The sum of {a} and {b} is {result}." agent = Agent( name="Triage Agent", - instructions="Get weather or sum numbers. You can use one tool at a time, switching to another tool in subsequent turns.", - tools=[sum_numbers, get_weather], + instructions="Get weather or sum numbers. Use only one tool per turn.", + tools=[get_weather, sum_numbers], ) async def main(): try: user_input = input("Enter a message: ") - result = await Runner.run(agent, user_input, max_turns=1) - print(result.final_output) + print("✅ Final Output:", result.final_output) except AgentsException as e: - print(f"Caught AgentsException: {e}") + print(f"❌ Caught {e.__class__.__name__}: {e}") if __name__ == "__main__": From 21cacb2b071ac514b077a3bc61a85af3eca8ee1b Mon Sep 17 00:00:00 2001 From: muhammadhamidrazasidtechno <160109562+muhammadhamidrazasidtechno@users.noreply.github.com> Date: Thu, 31 Jul 2025 07:27:57 +0500 Subject: [PATCH 10/16] update --- examples/exceptions/agents_exception.py | 2 +- examples/exceptions/input_guardrail_tripwire_triggered.py | 3 +++ examples/exceptions/max_turns_exceeded.py | 4 +++- examples/exceptions/model_behavior_error.py | 5 ++++- examples/exceptions/output_guardrail_tripwire_triggered.py | 4 +++- examples/exceptions/user_error.py | 5 +++-- 6 files changed, 17 insertions(+), 6 deletions(-) diff --git a/examples/exceptions/agents_exception.py b/examples/exceptions/agents_exception.py index c0f827eac..59fe20fd2 100644 --- a/examples/exceptions/agents_exception.py +++ b/examples/exceptions/agents_exception.py @@ -1,10 +1,10 @@ from __future__ import annotations import asyncio + from agents import Agent, Runner, function_tool from agents.exceptions import AgentsException - """ This example demonstrates the use of the OpenAI Agents SDK with tools and comprehensive error handling. diff --git a/examples/exceptions/input_guardrail_tripwire_triggered.py b/examples/exceptions/input_guardrail_tripwire_triggered.py index d306e802a..f6f96763a 100644 --- a/examples/exceptions/input_guardrail_tripwire_triggered.py +++ b/examples/exceptions/input_guardrail_tripwire_triggered.py @@ -1,6 +1,9 @@ from __future__ import annotations + import asyncio + from pydantic import BaseModel + from agents import ( Agent, GuardrailFunctionOutput, diff --git a/examples/exceptions/max_turns_exceeded.py b/examples/exceptions/max_turns_exceeded.py index a92d55d8f..b0f8c6a83 100644 --- a/examples/exceptions/max_turns_exceeded.py +++ b/examples/exceptions/max_turns_exceeded.py @@ -1,5 +1,7 @@ from __future__ import annotations + import asyncio + from agents import Agent, Runner, function_tool from agents.exceptions import MaxTurnsExceeded @@ -34,7 +36,7 @@ async def main(): print(result.final_output) except MaxTurnsExceeded as e: print(f"Caught MaxTurnsExceeded: {e}") - + if __name__ == "__main__": diff --git a/examples/exceptions/model_behavior_error.py b/examples/exceptions/model_behavior_error.py index efac13e56..00ec4d69c 100644 --- a/examples/exceptions/model_behavior_error.py +++ b/examples/exceptions/model_behavior_error.py @@ -1,7 +1,10 @@ from __future__ import annotations + import asyncio -from pydantic import BaseModel from typing import Literal + +from pydantic import BaseModel + from agents import Agent, Runner from agents.exceptions import ModelBehaviorError diff --git a/examples/exceptions/output_guardrail_tripwire_triggered.py b/examples/exceptions/output_guardrail_tripwire_triggered.py index 687db4d71..d60715627 100644 --- a/examples/exceptions/output_guardrail_tripwire_triggered.py +++ b/examples/exceptions/output_guardrail_tripwire_triggered.py @@ -1,6 +1,9 @@ from __future__ import annotations + import asyncio + from pydantic import BaseModel + from agents import ( Agent, GuardrailFunctionOutput, @@ -9,7 +12,6 @@ output_guardrail, ) - """ This example demonstrates an OpenAI Agents SDK agent with an output guardrail to block math homework responses. diff --git a/examples/exceptions/user_error.py b/examples/exceptions/user_error.py index c62e26d38..c5ce54cc3 100644 --- a/examples/exceptions/user_error.py +++ b/examples/exceptions/user_error.py @@ -1,9 +1,10 @@ from __future__ import annotations + import asyncio + from agents import Agent, Runner, function_tool from agents.exceptions import UserError - """ This example demonstrates an OpenAI Agents SDK agent that triggers a UserError due to incorrect SDK usage. @@ -32,4 +33,4 @@ async def main(): if __name__ == "__main__": - asyncio.run(main()) \ No newline at end of file + asyncio.run(main()) From 2da1531f99b3e9fe6bf0b0449538799485dc0eb5 Mon Sep 17 00:00:00 2001 From: muhammadhamidrazasidtechno <160109562+muhammadhamidrazasidtechno@users.noreply.github.com> Date: Thu, 31 Jul 2025 07:34:51 +0500 Subject: [PATCH 11/16] update --- .../input_guardrail_tripwire_triggered.py | 8 +++++++- examples/exceptions/user_error.py | 20 +++++++++++-------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/examples/exceptions/input_guardrail_tripwire_triggered.py b/examples/exceptions/input_guardrail_tripwire_triggered.py index f6f96763a..b42cc9266 100644 --- a/examples/exceptions/input_guardrail_tripwire_triggered.py +++ b/examples/exceptions/input_guardrail_tripwire_triggered.py @@ -9,6 +9,7 @@ GuardrailFunctionOutput, InputGuardrailTripwireTriggered, Runner, + RunContextWrapper, input_guardrail, ) @@ -18,6 +19,7 @@ The 'CustomerSupportAgent' processes user queries provided as direct string inputs in an interactive loop. A guardrail, implemented via 'GuardrailAgent' and a Pydantic model (`MathHomeworkOutput`), checks if the input is a math homework question. If detected, the guardrail raises `InputGuardrailTripwireTriggered`, triggering a refusal message ("Sorry, I can't help with math homework."). Otherwise, the agent responds to the query. The loop continues to prompt for new inputs, handling each independently. """ + class MathHomeworkOutput(BaseModel): is_math_homework: bool @@ -30,7 +32,11 @@ class MathHomeworkOutput(BaseModel): @input_guardrail -async def math_guardrail(context, agent: Agent, input: str) -> GuardrailFunctionOutput: +def my_input_guardrail( + context: RunContextWrapper[Any], + agent: Agent[Any], + inputs: str | list[Any], +) -> GuardrailFunctionOutput: result = await Runner.run(guardrail_agent, input) output = result.final_output_as(MathHomeworkOutput) return GuardrailFunctionOutput( diff --git a/examples/exceptions/user_error.py b/examples/exceptions/user_error.py index c5ce54cc3..a5b3daf5c 100644 --- a/examples/exceptions/user_error.py +++ b/examples/exceptions/user_error.py @@ -1,35 +1,39 @@ from __future__ import annotations import asyncio - from agents import Agent, Runner, function_tool from agents.exceptions import UserError + """ -This example demonstrates an OpenAI Agents SDK agent that triggers a UserError due to incorrect SDK usage. +This example demonstrates raising a `UserError` at runtime by violating a tool-specific logic rule +(e.g., returning the wrong type despite declaring a valid return type). -The 'Assistant' agent is configured with an invalid `tool_use_behavior` (empty string) and an invalid tool (`invalid_tool`) that declares a `None` return type but returns a string. Either issue raises a `UserError` when the agent is executed, indicating improper SDK configuration by the user. The interactive loop processes user queries as direct string inputs, catching and displaying the `UserError` message. +This passes `mypy` but fails at runtime due to logical misuse of the SDK. """ @function_tool def invalid_tool() -> None: - return "I return a string" # Type mismatch triggers UserError + # This tool *claims* to return None, but it returns a string instead. + # This misuse is caught by the SDK during runtime and raises UserError. + return "This return violates the declared return type." async def main(): agent = Agent( name="Assistant", - instructions="Use the invalid_tool to process queries.", + instructions="Use the tool to demonstrate misuse.", tools=[invalid_tool], - tool_use_behavior="invalid_tool", + tool_use_behavior="run_llm_again", # ✅ valid value — no mypy error ) - user_input = "Do Something." + + user_input = "Please do something invalid" try: result = await Runner.run(agent, user_input) print(result.final_output) except UserError as e: - print(f"UserError: {e}") + print(f"UserError caught as expected: {e}") if __name__ == "__main__": From 7071386b67527859ccda9453ee20e8f07b9f1acb Mon Sep 17 00:00:00 2001 From: muhammadhamidrazasidtechno <160109562+muhammadhamidrazasidtechno@users.noreply.github.com> Date: Thu, 31 Jul 2025 07:37:31 +0500 Subject: [PATCH 12/16] fix it --- .../input_guardrail_tripwire_triggered.py | 26 ++++++++++++------- examples/exceptions/user_error.py | 2 +- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/examples/exceptions/input_guardrail_tripwire_triggered.py b/examples/exceptions/input_guardrail_tripwire_triggered.py index b42cc9266..c9d7af843 100644 --- a/examples/exceptions/input_guardrail_tripwire_triggered.py +++ b/examples/exceptions/input_guardrail_tripwire_triggered.py @@ -1,6 +1,7 @@ from __future__ import annotations import asyncio +from typing import Any from pydantic import BaseModel @@ -8,56 +9,63 @@ Agent, GuardrailFunctionOutput, InputGuardrailTripwireTriggered, - Runner, RunContextWrapper, + Runner, input_guardrail, ) """ This example demonstrates an OpenAI Agents SDK agent with an input guardrail to block math homework queries. -The 'CustomerSupportAgent' processes user queries provided as direct string inputs in an interactive loop. A guardrail, implemented via 'GuardrailAgent' and a Pydantic model (`MathHomeworkOutput`), checks if the input is a math homework question. If detected, the guardrail raises `InputGuardrailTripwireTriggered`, triggering a refusal message ("Sorry, I can't help with math homework."). Otherwise, the agent responds to the query. The loop continues to prompt for new inputs, handling each independently. +If the user asks a math question, the input guardrail blocks it by raising InputGuardrailTripwireTriggered. """ +# Step 1: Define the output type of the guardrail agent class MathHomeworkOutput(BaseModel): is_math_homework: bool +# Step 2: Agent that checks if the input is math homework guardrail_agent = Agent( name="GuardrailAgent", - instructions="Check if the input is a math homework question.", + instructions="Return is_math_homework: true if the input is a math question.", output_type=MathHomeworkOutput, ) +# Step 3: Define the async input guardrail function @input_guardrail -def my_input_guardrail( +async def my_input_guardrail( context: RunContextWrapper[Any], agent: Agent[Any], inputs: str | list[Any], ) -> GuardrailFunctionOutput: - result = await Runner.run(guardrail_agent, input) + input_str = inputs if isinstance(inputs, str) else " ".join(str(i) for i in inputs) + result = await Runner.run(guardrail_agent, input_str) output = result.final_output_as(MathHomeworkOutput) + return GuardrailFunctionOutput( output_info=output, tripwire_triggered=output.is_math_homework, ) +# Step 4: Main agent that responds to queries async def main(): agent = Agent( name="CustomerSupportAgent", - instructions="Answer user queries.", - input_guardrails=[math_guardrail], + instructions="Answer user queries. Avoid math homework.", + input_guardrails=[my_input_guardrail], + tools=[], ) - user_input = "What is 2 + 2" + user_input = "What is 2 + 2?" try: result = await Runner.run(agent, user_input) print(result.final_output) except InputGuardrailTripwireTriggered: - print("InputGuardrailTripwireTriggered, I can't help with math homework.") + print("Sorry, I can't help with math homework.") if __name__ == "__main__": diff --git a/examples/exceptions/user_error.py b/examples/exceptions/user_error.py index a5b3daf5c..08b4b9c1c 100644 --- a/examples/exceptions/user_error.py +++ b/examples/exceptions/user_error.py @@ -1,10 +1,10 @@ from __future__ import annotations import asyncio + from agents import Agent, Runner, function_tool from agents.exceptions import UserError - """ This example demonstrates raising a `UserError` at runtime by violating a tool-specific logic rule (e.g., returning the wrong type despite declaring a valid return type). From 7b5501d29851f37d9ed8cbd20748de63f1cae00c Mon Sep 17 00:00:00 2001 From: muhammadhamidrazasidtechno <160109562+muhammadhamidrazasidtechno@users.noreply.github.com> Date: Thu, 31 Jul 2025 07:40:46 +0500 Subject: [PATCH 13/16] Update user_error.py --- examples/exceptions/user_error.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/examples/exceptions/user_error.py b/examples/exceptions/user_error.py index 08b4b9c1c..79201ce53 100644 --- a/examples/exceptions/user_error.py +++ b/examples/exceptions/user_error.py @@ -6,29 +6,26 @@ from agents.exceptions import UserError """ -This example demonstrates raising a `UserError` at runtime by violating a tool-specific logic rule -(e.g., returning the wrong type despite declaring a valid return type). - -This passes `mypy` but fails at runtime due to logical misuse of the SDK. +This example demonstrates raising a `UserError` manually during tool execution. +This avoids mypy errors but simulates incorrect SDK usage or logic issues. """ @function_tool -def invalid_tool() -> None: - # This tool *claims* to return None, but it returns a string instead. - # This misuse is caught by the SDK during runtime and raises UserError. - return "This return violates the declared return type." +def invalid_tool() -> str: + # Simulate misuse or invalid condition + raise UserError("This tool was misused and raised a UserError intentionally.") async def main(): agent = Agent( name="Assistant", - instructions="Use the tool to demonstrate misuse.", + instructions="Use the tool to demonstrate a manual UserError.", tools=[invalid_tool], - tool_use_behavior="run_llm_again", # ✅ valid value — no mypy error + tool_use_behavior="run_llm_again", # ✅ valid, passes mypy ) - user_input = "Please do something invalid" + user_input = "Trigger the error" try: result = await Runner.run(agent, user_input) print(result.final_output) From f533f656215102b903a04c308522760ad83e3e18 Mon Sep 17 00:00:00 2001 From: muhammadhamidrazasidtechno <160109562+muhammadhamidrazasidtechno@users.noreply.github.com> Date: Fri, 1 Aug 2025 12:46:08 +0500 Subject: [PATCH 14/16] Update index.md --- docs/index.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/index.md b/docs/index.md index 935c4be5b..b7ed85349 100644 --- a/docs/index.md +++ b/docs/index.md @@ -6,6 +6,8 @@ The [OpenAI Agents SDK](https://github.com/openai/openai-agents-python) enables - **Handoffs**, which allow agents to delegate to other agents for specific tasks - **Guardrails**, which enable the inputs to agents to be validated - **Sessions**, which automatically maintains conversation history across agent runs +- **Tracing**, which lets you visualize and debug the flow of an agent's actions + In combination with Python, these primitives are powerful enough to express complex relationships between tools and agents, and allow you to build real-world applications without a steep learning curve. In addition, the SDK comes with built-in **tracing** that lets you visualize and debug your agentic flows, as well as evaluate them and even fine-tune models for your application. From b66ef0a96fc37ce80e304ad1e931d160ed3796eb Mon Sep 17 00:00:00 2001 From: muhammadhamidrazasidtechno <160109562+muhammadhamidrazasidtechno@users.noreply.github.com> Date: Fri, 1 Aug 2025 17:39:42 +0500 Subject: [PATCH 15/16] Update litellm_model.py --- src/agents/extensions/models/litellm_model.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/agents/extensions/models/litellm_model.py b/src/agents/extensions/models/litellm_model.py index b01b84253..08678f30e 100644 --- a/src/agents/extensions/models/litellm_model.py +++ b/src/agents/extensions/models/litellm_model.py @@ -46,17 +46,25 @@ class InternalChatCompletionMessage(ChatCompletionMessage): - """ - An internal subclass to carry reasoning_content without modifying the original model. + """An internal subclass used by the SDK to carry the agent's reasoning content. + + This separates the agent's internal thought process from the final message + sent to the user, which is crucial for debugging and understanding + how the agent arrived at its conclusion without modifying the standard model output. """ reasoning_content: str class LitellmModel(Model): - """This class enables using any model via LiteLLM. LiteLLM allows you to acess OpenAPI, - Anthropic, Gemini, Mistral, and many other models. - See supported models here: [litellm models](https://docs.litellm.ai/docs/providers). + """This class acts as a flexible bridge, allowing you to use a wide variety + of LLM providers with the Agents SDK via LiteLLM. + + By using LiteLLM, you can easily switch between different models and providers + (like OpenAI, Anthropic, Gemini, Mistral, and many others) without changing + your core agent code. + + See a list of all supported models here: [litellm models](https://docs.litellm.ai/docs/providers). """ def __init__( From a0739dfc102d873bf534d14a30278dfc5e88c4fa Mon Sep 17 00:00:00 2001 From: muhammadhamidrazasidtechno <160109562+muhammadhamidrazasidtechno@users.noreply.github.com> Date: Fri, 1 Aug 2025 17:44:18 +0500 Subject: [PATCH 16/16] fix it error --- src/agents/extensions/models/litellm_model.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/agents/extensions/models/litellm_model.py b/src/agents/extensions/models/litellm_model.py index 08678f30e..f8a56183d 100644 --- a/src/agents/extensions/models/litellm_model.py +++ b/src/agents/extensions/models/litellm_model.py @@ -46,13 +46,14 @@ class InternalChatCompletionMessage(ChatCompletionMessage): - """An internal subclass used by the SDK to carry the agent's reasoning content. + """An internal subclass used by the SDK to carry the agent's reasoning content. This separates the agent's internal thought process from the final message sent to the user, which is crucial for debugging and understanding how the agent arrived at its conclusion without modifying the standard model output. """ + reasoning_content: str