chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:39:17 +08:00
commit 4ed4e9ff99
1368 changed files with 334957 additions and 0 deletions
+140
View File
@@ -0,0 +1,140 @@
import asyncio
import random
from typing import Any
from pydantic import BaseModel
from agents import (
Agent,
AgentHookContext,
AgentHooks,
RunContextWrapper,
Runner,
Tool,
function_tool,
)
from examples.auto_mode import input_with_fallback, is_auto_mode
class CustomAgentHooks(AgentHooks):
def __init__(self, display_name: str):
self.event_counter = 0
self.display_name = display_name
async def on_start(self, context: AgentHookContext, agent: Agent) -> None:
self.event_counter += 1
# Access the turn_input from the context to see what input the agent received
print(
f"### ({self.display_name}) {self.event_counter}: Agent {agent.name} started with turn_input: {context.turn_input}"
)
async def on_end(self, context: RunContextWrapper, agent: Agent, output: Any) -> None:
self.event_counter += 1
print(
f"### ({self.display_name}) {self.event_counter}: Agent {agent.name} ended with output {output}"
)
async def on_handoff(self, context: RunContextWrapper, agent: Agent, source: Agent) -> None:
self.event_counter += 1
print(
f"### ({self.display_name}) {self.event_counter}: Agent {source.name} handed off to {agent.name}"
)
# Note: The on_tool_start and on_tool_end hooks apply only to local tools.
# They do not include hosted tools that run on the OpenAI server side,
# such as WebSearchTool, FileSearchTool, CodeInterpreterTool, HostedMCPTool,
# or other built-in hosted tools.
async def on_tool_start(self, context: RunContextWrapper, agent: Agent, tool: Tool) -> None:
self.event_counter += 1
print(
f"### ({self.display_name}) {self.event_counter}: Agent {agent.name} started tool {tool.name}"
)
async def on_tool_end(
self, context: RunContextWrapper, agent: Agent, tool: Tool, result: object
) -> None:
self.event_counter += 1
print(
f"### ({self.display_name}) {self.event_counter}: Agent {agent.name} ended tool {tool.name} with result {result}"
)
###
@function_tool
def random_number(max: int) -> int:
"""
Generate a random number from 0 to max (inclusive).
"""
if is_auto_mode():
if max <= 0:
print("[debug] auto mode returning deterministic value 0")
return 0
value = min(max, 37)
if value % 2 == 0:
value = value - 1 if value > 1 else 1
print(f"[debug] auto mode returning deterministic odd number {value}")
return value
return random.randint(0, max)
@function_tool
def multiply_by_two(x: int) -> int:
"""Simple multiplication by two."""
return x * 2
class FinalResult(BaseModel):
number: int
multiply_agent = Agent(
name="Multiply Agent",
instructions="Multiply the number by 2 and then return the final result.",
tools=[multiply_by_two],
output_type=FinalResult,
hooks=CustomAgentHooks(display_name="Multiply Agent"),
)
start_agent = Agent(
name="Start Agent",
instructions="Generate a random number. If it's even, stop. If it's odd, hand off to the multiply agent.",
tools=[random_number],
output_type=FinalResult,
handoffs=[multiply_agent],
hooks=CustomAgentHooks(display_name="Start Agent"),
)
async def main() -> None:
user_input = input_with_fallback("Enter a max number: ", "50")
try:
max_number = int(user_input)
await Runner.run(
start_agent,
input=f"Generate a random number between 0 and {max_number}.",
)
except ValueError:
print("Please enter a valid integer.")
return
print("Done!")
if __name__ == "__main__":
asyncio.run(main())
"""
$ python examples/basic/agent_lifecycle_example.py
Enter a max number: 250
### (Start Agent) 1: Agent Start Agent started
### (Start Agent) 2: Agent Start Agent started tool random_number
### (Start Agent) 3: Agent Start Agent ended tool random_number with result 37
### (Start Agent) 4: Agent Start Agent handed off to Multiply Agent
### (Multiply Agent) 1: Agent Multiply Agent started
### (Multiply Agent) 2: Agent Multiply Agent started tool multiply_by_two
### (Multiply Agent) 3: Agent Multiply Agent ended tool multiply_by_two with result 74
### (Multiply Agent) 4: Agent Multiply Agent ended with output number=74
Done!
"""
+70
View File
@@ -0,0 +1,70 @@
import asyncio
import random
from dataclasses import dataclass
from typing import Literal
from agents import Agent, RunContextWrapper, Runner
@dataclass
class CustomContext:
style: Literal["haiku", "pirate", "robot"]
def custom_instructions(
run_context: RunContextWrapper[CustomContext], agent: Agent[CustomContext]
) -> str:
context = run_context.context
if context.style == "haiku":
return "Only respond in haikus."
elif context.style == "pirate":
return "Respond as a pirate."
else:
return "Respond as a robot and say 'beep boop' a lot."
agent = Agent(
name="Chat agent",
instructions=custom_instructions,
)
async def main():
context = CustomContext(style=random.choice(["haiku", "pirate", "robot"]))
print(f"Using style: {context.style}\n")
user_message = "Tell me a joke."
print(f"User: {user_message}")
result = await Runner.run(agent, user_message, context=context)
print(f"Assistant: {result.final_output}")
if __name__ == "__main__":
asyncio.run(main())
"""
$ python examples/basic/dynamic_system_prompt.py
Using style: haiku
User: Tell me a joke.
Assistant: Why don't eggs tell jokes?
They might crack each other's shells,
leaving yolk on face.
$ python examples/basic/dynamic_system_prompt.py
Using style: robot
User: Tell me a joke.
Assistant: Beep boop! Why was the robot so bad at soccer? Beep boop... because it kept kicking up a debug! Beep boop!
$ python examples/basic/dynamic_system_prompt.py
Using style: pirate
User: Tell me a joke.
Assistant: Why did the pirate go to school?
To improve his arrr-ticulation! Har har har! 🏴‍☠️
"""
+20
View File
@@ -0,0 +1,20 @@
import asyncio
from agents import Agent, Runner
async def main():
agent = Agent(
name="Assistant",
instructions="You only respond in haikus.",
)
result = await Runner.run(agent, "Tell me about recursion in programming.")
print(result.final_output)
# Function calls itself,
# Looping in smaller pieces,
# Endless by design.
if __name__ == "__main__":
asyncio.run(main())
+30
View File
@@ -0,0 +1,30 @@
import asyncio
from openai.types.shared import Reasoning
from agents import Agent, ModelSettings, Runner
# If you have a certain reason to use Chat Completions, you can configure the model this way,
# and then you can pass the chat_completions_model to the Agent constructor.
# from openai import AsyncOpenAI
# client = AsyncOpenAI()
# from agents import OpenAIChatCompletionsModel
# chat_completions_model = OpenAIChatCompletionsModel(model="gpt-5.6-sol", openai_client=client)
async def main():
agent = Agent(
name="Knowledgable GPT-5 Assistant",
instructions="You're a knowledgable assistant. You always provide an interesting answer.",
model="gpt-5.6-sol",
model_settings=ModelSettings(
reasoning=Reasoning(effort="low"), # "none", "low", "medium", "high", "xhigh"
verbosity="low", # "low", "medium", "high"
),
)
result = await Runner.run(agent, "Tell me something about recursion in programming.")
print(result.final_output)
if __name__ == "__main__":
asyncio.run(main())
+39
View File
@@ -0,0 +1,39 @@
import asyncio
from openai import AsyncOpenAI
from agents import Agent, OpenAIChatCompletionsModel, Runner, set_tracing_disabled
set_tracing_disabled(True)
# import logging
# logging.basicConfig(level=logging.DEBUG)
# This is an example of how to use gpt-oss with Ollama.
# Refer to https://cookbook.openai.com/articles/gpt-oss/run-locally-ollama for more details.
# If you prefer using LM Studio, refer to https://cookbook.openai.com/articles/gpt-oss/run-locally-lmstudio
gpt_oss_model = OpenAIChatCompletionsModel(
model="gpt-oss:20b",
openai_client=AsyncOpenAI(
base_url="http://localhost:11434/v1",
api_key="ollama",
),
)
async def main():
# Note that using a custom outputType for an agent may not work well with gpt-oss models.
# Consider going with the default "text" outputType.
# See also: https://github.com/openai/openai-agents-python/issues/1414
agent = Agent(
name="Assistant",
instructions="You're a helpful assistant. You provide a concise answer to the user's question.",
model=gpt_oss_model,
)
result = await Runner.run(agent, "Tell me about recursion in programming.")
print(result.final_output)
if __name__ == "__main__":
asyncio.run(main())
+45
View File
@@ -0,0 +1,45 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "8a77ee2e-22f2-409c-837d-b994978b0aa2",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"A function calls self, \n",
"Unraveling layers deep, \n",
"Base case ends the quest. \n",
"\n",
"Infinite loops lurk, \n",
"Mind the base condition well, \n",
"Or it will not work. \n",
"\n",
"Trees and lists unfold, \n",
"Elegant solutions bloom, \n",
"Recursion's art told.\n"
]
}
],
"source": [
"from agents import Agent, Runner\n",
"\n",
"agent = Agent(name=\"Assistant\", instructions=\"You are a helpful assistant\")\n",
"\n",
"# Intended for Jupyter notebooks where there's an existing event loop\n",
"result = await Runner.run(agent, \"Write a haiku about recursion in programming.\") # type: ignore[top-level-await] # noqa: F704\n",
"print(result.final_output)"
]
}
],
"metadata": {
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
+37
View File
@@ -0,0 +1,37 @@
import asyncio
from agents import Agent, Runner, ToolOutputImage, ToolOutputImageDict, function_tool
return_typed_dict = True
URL = "https://images.unsplash.com/photo-1505761671935-60b3a7427bad?auto=format&fit=crop&w=400&q=80"
@function_tool
def fetch_random_image() -> ToolOutputImage | ToolOutputImageDict:
"""Fetch a random image."""
print("Image tool called")
if return_typed_dict:
return {"type": "image", "image_url": URL, "detail": "auto"}
return ToolOutputImage(image_url=URL, detail="auto")
async def main():
agent = Agent(
name="Assistant",
instructions="You are a helpful assistant.",
tools=[fetch_random_image],
)
result = await Runner.run(
agent,
input="Fetch an image using the random_image tool, then describe it",
)
print(result.final_output)
"""This image features the famous clock tower, commonly known as Big Ben, ..."""
if __name__ == "__main__":
asyncio.run(main())
+189
View File
@@ -0,0 +1,189 @@
import asyncio
import random
from typing import Any, cast
from pydantic import BaseModel
from agents import (
Agent,
AgentHookContext,
AgentHooks,
RunContextWrapper,
RunHooks,
Runner,
Tool,
Usage,
function_tool,
)
from agents.items import ModelResponse, TResponseInputItem
from agents.tool_context import ToolContext
from examples.auto_mode import input_with_fallback
class LoggingHooks(AgentHooks[Any]):
async def on_start(
self,
context: AgentHookContext[Any],
agent: Agent[Any],
) -> None:
# Access the turn_input from the context to see what input the agent received
print(f"#### {agent.name} is starting with turn_input: {context.turn_input}")
async def on_end(
self,
context: RunContextWrapper[Any],
agent: Agent[Any],
output: Any,
) -> None:
print(f"#### {agent.name} produced output: {output}.")
class ExampleHooks(RunHooks):
def __init__(self):
self.event_counter = 0
def _usage_to_str(self, usage: Usage) -> str:
return f"{usage.requests} requests, {usage.input_tokens} input tokens, {usage.output_tokens} output tokens, {usage.total_tokens} total tokens"
async def on_agent_start(self, context: AgentHookContext, agent: Agent) -> None:
self.event_counter += 1
# Access the turn_input from the context to see what input the agent received
print(
f"### {self.event_counter}: Agent {agent.name} started. turn_input: {context.turn_input}. Usage: {self._usage_to_str(context.usage)}"
)
async def on_llm_start(
self,
context: RunContextWrapper,
agent: Agent,
system_prompt: str | None,
input_items: list[TResponseInputItem],
) -> None:
self.event_counter += 1
print(f"### {self.event_counter}: LLM started. Usage: {self._usage_to_str(context.usage)}")
async def on_llm_end(
self, context: RunContextWrapper, agent: Agent, response: ModelResponse
) -> None:
self.event_counter += 1
print(f"### {self.event_counter}: LLM ended. Usage: {self._usage_to_str(context.usage)}")
async def on_agent_end(self, context: RunContextWrapper, agent: Agent, output: Any) -> None:
self.event_counter += 1
print(
f"### {self.event_counter}: Agent {agent.name} ended with output {output}. Usage: {self._usage_to_str(context.usage)}"
)
# Note: The on_tool_start and on_tool_end hooks apply only to local tools.
# They do not include hosted tools that run on the OpenAI server side,
# such as WebSearchTool, FileSearchTool, CodeInterpreterTool, HostedMCPTool,
# or other built-in hosted tools.
async def on_tool_start(self, context: RunContextWrapper, agent: Agent, tool: Tool) -> None:
self.event_counter += 1
# While this type cast is not ideal,
# we don't plan to change the context arg type in the near future for backwards compatibility.
tool_context = cast(ToolContext[Any], context)
print(
f"### {self.event_counter}: Tool {tool.name} started. name={tool_context.tool_name}, call_id={tool_context.tool_call_id}, args={tool_context.tool_arguments}. Usage: {self._usage_to_str(tool_context.usage)}"
)
async def on_tool_end(
self, context: RunContextWrapper, agent: Agent, tool: Tool, result: object
) -> None:
self.event_counter += 1
# While this type cast is not ideal,
# we don't plan to change the context arg type in the near future for backwards compatibility.
tool_context = cast(ToolContext[Any], context)
print(
f"### {self.event_counter}: Tool {tool.name} finished. result={result}, name={tool_context.tool_name}, call_id={tool_context.tool_call_id}, args={tool_context.tool_arguments}. Usage: {self._usage_to_str(tool_context.usage)}"
)
async def on_handoff(
self, context: RunContextWrapper, from_agent: Agent, to_agent: Agent
) -> None:
self.event_counter += 1
print(
f"### {self.event_counter}: Handoff from {from_agent.name} to {to_agent.name}. Usage: {self._usage_to_str(context.usage)}"
)
hooks = ExampleHooks()
###
@function_tool
def random_number(max: int) -> int:
"""Generate a random number from 0 to max (inclusive)."""
return random.randint(0, max)
@function_tool
def multiply_by_two(x: int) -> int:
"""Return x times two."""
return x * 2
class FinalResult(BaseModel):
number: int
multiply_agent = Agent(
name="Multiply Agent",
instructions="Multiply the number by 2 and then return the final result.",
tools=[multiply_by_two],
output_type=FinalResult,
hooks=LoggingHooks(),
)
start_agent = Agent(
name="Start Agent",
instructions="Generate a random number. If it's even, stop. If it's odd, hand off to the multiplier agent.",
tools=[random_number],
output_type=FinalResult,
handoffs=[multiply_agent],
hooks=LoggingHooks(),
)
async def main() -> None:
user_input = input_with_fallback("Enter a max number: ", "50")
try:
max_number = int(user_input)
await Runner.run(
start_agent,
hooks=hooks,
input=f"Generate a random number between 0 and {max_number}.",
)
except ValueError:
print("Please enter a valid integer.")
return
print("Done!")
if __name__ == "__main__":
asyncio.run(main())
"""
$ python examples/basic/lifecycle_example.py
Enter a max number: 250
### 1: Agent Start Agent started. Usage: 0 requests, 0 input tokens, 0 output tokens, 0 total tokens
### 2: LLM started. Usage: 0 requests, 0 input tokens, 0 output tokens, 0 total tokens
### 3: LLM ended. Usage: 1 requests, 143 input tokens, 15 output tokens, 158 total tokens
### 4: Tool random_number started. name=random_number, call_id=call_IujmDZYiM800H0hy7v17VTS0, args={"max":250}. Usage: 1 requests, 143 input tokens, 15 output tokens, 158 total tokens
### 5: Tool random_number finished. result=107, name=random_number, call_id=call_IujmDZYiM800H0hy7v17VTS0, args={"max":250}. Usage: 1 requests, 143 input tokens, 15 output tokens, 158 total tokens
### 6: LLM started. Usage: 1 requests, 143 input tokens, 15 output tokens, 158 total tokens
### 7: LLM ended. Usage: 2 requests, 310 input tokens, 29 output tokens, 339 total tokens
### 8: Handoff from Start Agent to Multiply Agent. Usage: 2 requests, 310 input tokens, 29 output tokens, 339 total tokens
### 9: Agent Multiply Agent started. Usage: 2 requests, 310 input tokens, 29 output tokens, 339 total tokens
### 10: LLM started. Usage: 2 requests, 310 input tokens, 29 output tokens, 339 total tokens
### 11: LLM ended. Usage: 3 requests, 472 input tokens, 45 output tokens, 517 total tokens
### 12: Tool multiply_by_two started. name=multiply_by_two, call_id=call_KhHvTfsgaosZsfi741QvzgYw, args={"x":107}. Usage: 3 requests, 472 input tokens, 45 output tokens, 517 total tokens
### 13: Tool multiply_by_two finished. result=214, name=multiply_by_two, call_id=call_KhHvTfsgaosZsfi741QvzgYw, args={"x":107}. Usage: 3 requests, 472 input tokens, 45 output tokens, 517 total tokens
### 14: LLM started. Usage: 3 requests, 472 input tokens, 45 output tokens, 517 total tokens
### 15: LLM ended. Usage: 4 requests, 660 input tokens, 56 output tokens, 716 total tokens
### 16: Agent Multiply Agent ended with output number=214. Usage: 4 requests, 660 input tokens, 56 output tokens, 716 total tokens
Done!
"""
+45
View File
@@ -0,0 +1,45 @@
import asyncio
import base64
import os
from agents import Agent, Runner
FILEPATH = os.path.join(os.path.dirname(__file__), "media/partial_o3-and-o4-mini-system-card.pdf")
def file_to_base64(file_path: str) -> str:
with open(file_path, "rb") as f:
return base64.b64encode(f.read()).decode("utf-8")
async def main():
agent = Agent(
name="Assistant",
instructions="You are a helpful assistant.",
)
b64_file = file_to_base64(FILEPATH)
result = await Runner.run(
agent,
[
{
"role": "user",
"content": [
{
"type": "input_file",
"file_data": f"data:application/pdf;base64,{b64_file}",
"filename": "partial_o3-and-o4-mini-system-card.pdf",
}
],
},
{
"role": "user",
"content": "What is the first sentence of the introduction?",
},
],
)
print(result.final_output)
if __name__ == "__main__":
asyncio.run(main())
+48
View File
@@ -0,0 +1,48 @@
import asyncio
import base64
import os
from agents import Agent, Runner
FILEPATH = os.path.join(os.path.dirname(__file__), "media/image_bison.jpg")
def image_to_base64(image_path):
with open(image_path, "rb") as image_file:
encoded_string = base64.b64encode(image_file.read()).decode("utf-8")
return encoded_string
async def main():
# Print base64-encoded image
b64_image = image_to_base64(FILEPATH)
agent = Agent(
name="Assistant",
instructions="You are a helpful assistant.",
)
result = await Runner.run(
agent,
[
{
"role": "user",
"content": [
{
"type": "input_image",
"detail": "auto",
"image_url": f"data:image/jpeg;base64,{b64_image}",
}
],
},
{
"role": "user",
"content": "What do you see in this image?",
},
],
)
print(result.final_output)
if __name__ == "__main__":
asyncio.run(main())
Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

+84
View File
@@ -0,0 +1,84 @@
import asyncio
import json
from dataclasses import dataclass
from typing import Any
from agents import Agent, AgentOutputSchema, AgentOutputSchemaBase, ModelBehaviorError, Runner
"""This example demonstrates how to use an output type that is not in strict mode. Strict mode
allows us to guarantee valid JSON output, but some schemas are not strict-compatible.
In this example, we define an output type that is not strict-compatible, and then we run the
agent with strict_json_schema=False.
We also demonstrate a custom output type.
To understand which schemas are strict-compatible, see:
https://platform.openai.com/docs/guides/structured-outputs?api-mode=responses#supported-schemas
"""
@dataclass
class OutputType:
jokes: dict[int, str]
"""A list of jokes, indexed by joke number."""
class CustomOutputSchema(AgentOutputSchemaBase):
"""A demonstration of a custom output schema."""
def is_plain_text(self) -> bool:
return False
def name(self) -> str:
return "CustomOutputSchema"
def json_schema(self) -> dict[str, Any]:
return {
"type": "object",
"properties": {"jokes": {"type": "object", "properties": {"joke": {"type": "string"}}}},
}
def is_strict_json_schema(self) -> bool:
return False
def validate_json(self, json_str: str) -> Any:
json_obj = json.loads(json_str)
# Just for demonstration, we'll return a list.
return list(json_obj["jokes"].values())
async def main():
agent = Agent(
name="Assistant",
instructions="You are a helpful assistant.",
output_type=OutputType,
)
input = "Tell me 3 short jokes."
# First, let's try with a strict output type. This should raise an exception.
try:
result = await Runner.run(agent, input)
raise AssertionError("Should have raised an exception")
except Exception as e:
print(f"Error (expected): {e}")
# Now let's try again with a non-strict output type. This should work.
# In some cases, it will raise an error - the schema isn't strict, so the model may
# produce an invalid JSON object.
agent.output_type = AgentOutputSchema(OutputType, strict_json_schema=False)
try:
result = await Runner.run(agent, input)
print(result.final_output)
except ModelBehaviorError as e:
print(f"Non-strict output validation failed (expected possibility): {e}")
# Finally, let's try a custom output type.
agent.output_type = CustomOutputSchema()
result = await Runner.run(agent, input)
print(result.final_output)
if __name__ == "__main__":
asyncio.run(main())
+74
View File
@@ -0,0 +1,74 @@
import asyncio
from agents import Agent, Runner
from examples.auto_mode import input_with_fallback, is_auto_mode
"""This demonstrates usage of the `previous_response_id` parameter to continue a conversation.
The second run passes the previous response ID to the model, which allows it to continue the
conversation without re-sending the previous messages.
Notes:
1. This only applies to the OpenAI Responses API. Other models will ignore this parameter.
2. Responses are only stored for 30 days as of this writing, so in production you should
store the response ID along with an expiration date; if the response is no longer valid,
you'll need to re-send the previous conversation history.
"""
async def main():
print("=== Non-streaming Example ===")
agent = Agent(
name="Assistant",
instructions="You are a helpful assistant. be VERY concise.",
)
result = await Runner.run(agent, "What is the largest country in South America?")
print(result.final_output)
# Brazil
result = await Runner.run(
agent,
"What is the capital of that country?",
previous_response_id=result.last_response_id,
)
print(result.final_output)
# Brasilia
async def main_stream():
print("=== Streaming Example ===")
agent = Agent(
name="Assistant",
instructions="You are a helpful assistant. be VERY concise.",
)
result = Runner.run_streamed(agent, "What is the largest country in South America?")
async for event in result.stream_events():
if event.type == "raw_response_event" and event.data.type == "response.output_text.delta":
print(event.data.delta, end="", flush=True)
print()
result = Runner.run_streamed(
agent,
"What is the capital of that country?",
previous_response_id=result.last_response_id,
)
async for event in result.stream_events():
if event.type == "raw_response_event" and event.data.type == "response.output_text.delta":
print(event.data.delta, end="", flush=True)
if __name__ == "__main__":
if is_auto_mode():
asyncio.run(main())
print()
asyncio.run(main_stream())
else:
is_stream = input_with_fallback("Run in stream mode? (y/n): ", "n")
if is_stream == "y":
asyncio.run(main_stream())
else:
asyncio.run(main())
+79
View File
@@ -0,0 +1,79 @@
import argparse
import asyncio
import random
from agents import Agent, GenerateDynamicPromptData, Runner
"""
NOTE: This example will not work out of the box, because the default prompt ID will not be available
in your project.
To use it, please:
1. Go to https://platform.openai.com/playground/prompts
2. Create a new prompt variable, `poem_style`.
3. Create a system prompt with the content:
```
Write a poem in {{poem_style}}
```
4. Run the example with the `--prompt-id` flag.
"""
DEFAULT_PROMPT_ID = "pmpt_6965a984c7ac8194a8f4e79b00f838840118c1e58beb3332"
class DynamicContext:
def __init__(self, prompt_id: str):
self.prompt_id = prompt_id
self.poem_style = random.choice(["limerick", "haiku", "ballad"])
print(f"[debug] DynamicContext initialized with poem_style: {self.poem_style}")
async def _get_dynamic_prompt(data: GenerateDynamicPromptData):
ctx: DynamicContext = data.context.context
return {
"id": ctx.prompt_id,
"version": "1",
"variables": {
"poem_style": ctx.poem_style,
},
}
async def dynamic_prompt(prompt_id: str):
context = DynamicContext(prompt_id)
agent = Agent(
name="Assistant",
prompt=_get_dynamic_prompt,
)
result = await Runner.run(agent, "Tell me about recursion in programming.", context=context)
print(result.final_output)
async def static_prompt(prompt_id: str):
agent = Agent(
name="Assistant",
prompt={
"id": prompt_id,
"version": "1",
"variables": {
"poem_style": "limerick",
},
},
)
result = await Runner.run(agent, "Tell me about recursion in programming.")
print(result.final_output)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--dynamic", action="store_true")
parser.add_argument("--prompt-id", type=str, default=DEFAULT_PROMPT_ID)
args = parser.parse_args()
if args.dynamic:
asyncio.run(dynamic_prompt(args.prompt_id))
else:
asyncio.run(static_prompt(args.prompt_id))
+31
View File
@@ -0,0 +1,31 @@
import asyncio
from agents import Agent, Runner
URL = "https://images.unsplash.com/photo-1505761671935-60b3a7427bad?auto=format&fit=crop&w=400&q=80"
async def main():
agent = Agent(
name="Assistant",
instructions="You are a helpful assistant.",
)
result = await Runner.run(
agent,
[
{
"role": "user",
"content": [{"type": "input_image", "detail": "auto", "image_url": URL}],
},
{
"role": "user",
"content": "What do you see in this image?",
},
],
)
print(result.final_output)
if __name__ == "__main__":
asyncio.run(main())
+31
View File
@@ -0,0 +1,31 @@
import asyncio
from agents import Agent, Runner
URL = "https://www.berkshirehathaway.com/letters/2024ltr.pdf"
async def main():
agent = Agent(
name="Assistant",
instructions="You are a helpful assistant.",
)
result = await Runner.run(
agent,
[
{
"role": "user",
"content": [{"type": "input_file", "file_url": URL}],
},
{
"role": "user",
"content": "Can you summarize the letter?",
},
],
)
print(result.final_output)
if __name__ == "__main__":
asyncio.run(main())
+112
View File
@@ -0,0 +1,112 @@
import asyncio
import inspect
from agents import (
Agent,
ModelRetrySettings,
ModelSettings,
RetryDecision,
RunConfig,
Runner,
retry_policies,
)
def format_error(error: object) -> str:
if not isinstance(error, BaseException):
return "Unknown error"
return str(error) or error.__class__.__name__
async def main() -> None:
apply_policies = retry_policies.any(
# On OpenAI-backed models, provider_suggested() follows provider retry advice,
# including fallback retryable statuses when x-should-retry is absent
# (for example 408/409/429/5xx).
retry_policies.provider_suggested(),
retry_policies.retry_after(),
retry_policies.network_error(),
retry_policies.http_status([408, 409, 429, 500, 502, 503, 504]),
)
async def policy(context) -> bool | RetryDecision:
raw_decision = apply_policies(context)
decision: bool | RetryDecision
if inspect.isawaitable(raw_decision):
decision = await raw_decision
else:
decision = raw_decision
if isinstance(decision, RetryDecision):
if not decision.retry:
print(
f"[retry] stop after attempt {context.attempt}/{context.max_retries + 1}: "
f"{format_error(context.error)}"
)
return False
print(
" | ".join(
part
for part in [
f"[retry] retry attempt {context.attempt}/{context.max_retries + 1}",
(
f"waiting {decision.delay:.2f}s"
if decision.delay is not None
else "using default backoff"
),
f"reason: {decision.reason}" if decision.reason else None,
f"error: {format_error(context.error)}",
]
if part is not None
)
)
return decision
if not decision:
print(
f"[retry] stop after attempt {context.attempt}/{context.max_retries + 1}: "
f"{format_error(context.error)}"
)
return decision
retry = ModelRetrySettings(
max_retries=4,
backoff={
"initial_delay": 0.5,
"max_delay": 5.0,
"multiplier": 2.0,
"jitter": True,
},
policy=policy,
)
# RunConfig-level model_settings are shared defaults for the run.
# If an Agent also defines model_settings, the Agent wins for overlapping
# keys, while nested objects like retry/backoff are merged.
run_config = RunConfig(model_settings=ModelSettings(retry=retry))
agent = Agent(
name="Assistant",
instructions="You are a concise assistant. Answer in 3 short bullet points at most.",
# This Agent repeats the same retry config for clarity. In real code you
# can keep shared defaults in RunConfig and only put per-agent overrides
# here when you need different retry behavior.
model_settings=ModelSettings(retry=retry),
)
print(
"Retry support is configured. You will only see [retry] logs if a transient failure happens."
)
result = await Runner.run(
agent,
"Explain exponential backoff for API retries in plain English.",
run_config=run_config,
)
print("\nFinal output:\n")
print(result.final_output)
if __name__ == "__main__":
asyncio.run(main())
+114
View File
@@ -0,0 +1,114 @@
import asyncio
import inspect
from agents import (
Agent,
ModelRetrySettings,
ModelSettings,
RetryDecision,
RunConfig,
Runner,
retry_policies,
)
def format_error(error: object) -> str:
if not isinstance(error, BaseException):
return "Unknown error"
return str(error) or error.__class__.__name__
async def main() -> None:
apply_policies = retry_policies.any(
# On OpenAI-backed models, provider_suggested() follows provider retry advice,
# including fallback retryable statuses when x-should-retry is absent
# (for example 408/409/429/5xx).
retry_policies.provider_suggested(),
retry_policies.retry_after(),
retry_policies.network_error(),
retry_policies.http_status([408, 409, 429, 500, 502, 503, 504]),
)
async def policy(context) -> bool | RetryDecision:
raw_decision = apply_policies(context)
decision: bool | RetryDecision
if inspect.isawaitable(raw_decision):
decision = await raw_decision
else:
decision = raw_decision
if isinstance(decision, RetryDecision):
if not decision.retry:
print(
f"[retry] stop after attempt {context.attempt}/{context.max_retries + 1}: "
f"{format_error(context.error)}"
)
return False
print(
" | ".join(
part
for part in [
f"[retry] retry attempt {context.attempt}/{context.max_retries + 1}",
(
f"waiting {decision.delay:.2f}s"
if decision.delay is not None
else "using default backoff"
),
f"reason: {decision.reason}" if decision.reason else None,
f"error: {format_error(context.error)}",
]
if part is not None
)
)
return decision
if not decision:
print(
f"[retry] stop after attempt {context.attempt}/{context.max_retries + 1}: "
f"{format_error(context.error)}"
)
return decision
retry = ModelRetrySettings(
max_retries=4,
backoff={
"initial_delay": 0.5,
"max_delay": 5.0,
"multiplier": 2.0,
"jitter": True,
},
policy=policy,
)
# RunConfig-level model_settings are shared defaults for the run.
# If an Agent also defines model_settings, the Agent wins for overlapping
# keys, while nested objects like retry/backoff are merged.
run_config = RunConfig(model_settings=ModelSettings(retry=retry))
agent = Agent(
name="Assistant",
instructions="You are a concise assistant. Answer in 3 short bullet points at most.",
# Prefix with litellm/ to route this request through the LiteLLM adapter.
model="litellm/openai/gpt-4o-mini",
# This Agent repeats the same retry config for clarity. In real code you
# can keep shared defaults in RunConfig and only put per-agent overrides
# here when you need different retry behavior.
model_settings=ModelSettings(retry=retry),
)
print(
"Retry support is configured. You will only see [retry] logs if a transient failure happens."
)
result = await Runner.run(
agent,
"Explain exponential backoff for API retries in plain English.",
run_config=run_config,
)
print("\nFinal output:\n")
print(result.final_output)
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,87 @@
import asyncio
from typing import Annotated, Any
from openai.types.responses import ResponseFunctionCallArgumentsDeltaEvent
from agents import Agent, Runner, function_tool
@function_tool
def write_file(filename: Annotated[str, "Name of the file"], content: str) -> str:
"""Write content to a file."""
return f"File {filename} written successfully"
@function_tool
def create_config(
project_name: Annotated[str, "Project name"],
version: Annotated[str, "Project version"],
dependencies: Annotated[list[str] | None, "Dependencies (list of packages)"],
) -> str:
"""Generate a project configuration file."""
return f"Config for {project_name} v{version} created"
async def main():
"""
Demonstrates real-time streaming of function call arguments.
Function arguments are streamed incrementally as they are generated,
providing immediate feedback during parameter generation.
"""
agent = Agent(
name="CodeGenerator",
instructions="You are a helpful coding assistant. Use the provided tools to create files and configurations.",
tools=[write_file, create_config],
)
print("🚀 Function Call Arguments Streaming Demo")
result = Runner.run_streamed(
agent,
input="Create a Python web project called 'my-app' with FastAPI. Version 1.0.0, dependencies: fastapi, uvicorn",
)
# Track function calls for detailed output
function_calls: dict[Any, dict[str, Any]] = {} # call_id -> {name, arguments}
current_active_call_id = None
async for event in result.stream_events():
if event.type == "raw_response_event":
# Function call started
if event.data.type == "response.output_item.added":
if getattr(event.data.item, "type", None) == "function_call":
function_name = getattr(event.data.item, "name", "unknown")
call_id = getattr(event.data.item, "call_id", "unknown")
function_calls[call_id] = {"name": function_name, "arguments": ""}
current_active_call_id = call_id
print(f"\n📞 Function call streaming started: {function_name}()")
print("📝 Arguments building...")
# Real-time argument streaming
elif isinstance(event.data, ResponseFunctionCallArgumentsDeltaEvent):
if current_active_call_id and current_active_call_id in function_calls:
function_calls[current_active_call_id]["arguments"] += event.data.delta
print(event.data.delta, end="", flush=True)
# Function call completed
elif event.data.type == "response.output_item.done":
if hasattr(event.data.item, "call_id"):
call_id = getattr(event.data.item, "call_id", "unknown")
if call_id in function_calls:
function_info = function_calls[call_id]
print(f"\n✅ Function call streaming completed: {function_info['name']}")
print()
if current_active_call_id == call_id:
current_active_call_id = None
print("Summary of all function calls:")
for call_id, info in function_calls.items():
print(f" - #{call_id}: {info['name']}({info['arguments']})")
print(f"\nResult: {result.final_output}")
if __name__ == "__main__":
asyncio.run(main())
+66
View File
@@ -0,0 +1,66 @@
import asyncio
import random
from agents import Agent, ItemHelpers, Runner, function_tool
@function_tool
def how_many_jokes() -> int:
"""Return a random integer of jokes to tell between 1 and 10 (inclusive)."""
return random.randint(1, 10)
async def main():
agent = Agent(
name="Joker",
instructions="First call the `how_many_jokes` tool, then tell that many jokes.",
tools=[how_many_jokes],
)
result = Runner.run_streamed(
agent,
input="Hello",
)
print("=== Run starting ===")
async for event in result.stream_events():
# We'll ignore the raw responses event deltas
if event.type == "raw_response_event":
continue
elif event.type == "agent_updated_stream_event":
print(f"Agent updated: {event.new_agent.name}")
continue
elif event.type == "run_item_stream_event":
if event.item.type == "tool_call_item":
print(f"-- Tool was called: {getattr(event.item.raw_item, 'name', 'Unknown Tool')}")
elif event.item.type == "tool_call_output_item":
print(f"-- Tool output: {event.item.output}")
elif event.item.type == "message_output_item":
print(f"-- Message output:\n {ItemHelpers.text_message_output(event.item)}")
else:
pass # Ignore other event types
print("=== Run complete ===")
if __name__ == "__main__":
asyncio.run(main())
# === Run starting ===
# Agent updated: Joker
# -- Tool was called: how_many_jokes
# -- Tool output: 4
# -- Message output:
# Sure, here are four jokes for you:
# 1. **Why don't skeletons fight each other?**
# They don't have the guts!
# 2. **What do you call fake spaghetti?**
# An impasta!
# 3. **Why did the scarecrow win an award?**
# Because he was outstanding in his field!
# 4. **Why did the bicycle fall over?**
# Because it was two-tired!
# === Run complete ===
+21
View File
@@ -0,0 +1,21 @@
import asyncio
from openai.types.responses import ResponseTextDeltaEvent
from agents import Agent, Runner
async def main():
agent = Agent(
name="Joker",
instructions="You are a helpful assistant.",
)
result = Runner.run_streamed(agent, input="Please tell me 5 jokes.")
async for event in result.stream_events():
if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent):
print(event.data.delta, end="", flush=True)
if __name__ == "__main__":
asyncio.run(main())
+236
View File
@@ -0,0 +1,236 @@
"""Responses websocket streaming example with function tools, agent-as-tool, and approval.
This example shows a user-facing websocket workflow using
`responses_websocket_session(...)`:
- Streaming output (including reasoning summary deltas when available)
- Regular function tools
- An `Agent.as_tool(...)` specialist agent
- HITL approval for a sensitive tool call
- A follow-up turn using `previous_response_id` on the same trace
Required environment variable:
- `OPENAI_API_KEY`
Optional environment variables:
- `OPENAI_MODEL` (defaults to `gpt-5.6-sol`)
- `OPENAI_BASE_URL`
- `OPENAI_WEBSOCKET_BASE_URL`
- `EXAMPLES_INTERACTIVE_MODE=auto` (auto-approve HITL prompts for scripted runs)
"""
import asyncio
import os
from typing import Any
from openai.types.shared import Reasoning
from agents import (
Agent,
ModelSettings,
ResponsesWebSocketSession,
function_tool,
responses_websocket_session,
trace,
)
from examples.auto_mode import confirm_with_fallback
@function_tool
def lookup_order(order_id: str) -> dict[str, Any]:
"""Return deterministic order data for the demo."""
orders = {
"ORD-1001": {
"order_id": "ORD-1001",
"status": "delivered",
"delivered_days_ago": 3,
"amount": 49.99,
"currency": "USD",
"item": "Wireless Mouse",
},
"ORD-2002": {
"order_id": "ORD-2002",
"status": "delivered",
"delivered_days_ago": 12,
"amount": 129.0,
"currency": "USD",
"item": "Keyboard",
},
}
return orders.get(
order_id,
{
"order_id": order_id,
"status": "unknown",
"delivered_days_ago": 999,
"amount": 0.0,
"currency": "USD",
"item": "unknown",
},
)
@function_tool(needs_approval=True)
def submit_refund(order_id: str, amount: float, reason: str) -> dict[str, Any]:
"""Create a refund request. This tool requires approval."""
ticket = "RF-1001" if order_id == "ORD-1001" else f"RF-{order_id[-4:]}"
return {
"refund_ticket": ticket,
"order_id": order_id,
"amount": amount,
"reason": reason,
"status": "approved_pending_processing",
}
def ask_approval(question: str) -> bool:
"""Prompt for approval (or auto-approve in examples auto mode)."""
return confirm_with_fallback(f"[approval] {question} [y/N]: ", default=True)
async def run_streamed_turn(
ws: ResponsesWebSocketSession,
agent: Agent[Any],
prompt: str,
*,
previous_response_id: str | None = None,
) -> tuple[str, str]:
"""Run one streamed turn and handle HITL approvals if needed."""
print(f"\nUser: {prompt}\n")
result = ws.run_streamed(
agent,
prompt,
previous_response_id=previous_response_id,
)
printed_reasoning = False
printed_output = False
while True:
async for event in result.stream_events():
if event.type == "raw_response_event":
raw = event.data
if raw.type == "response.reasoning_summary_text.delta":
if not printed_reasoning:
print("Reasoning:")
printed_reasoning = True
print(raw.delta, end="", flush=True)
elif raw.type == "response.output_text.delta":
if printed_reasoning and not printed_output:
print("\n")
if not printed_output:
print("Assistant:")
printed_output = True
print(raw.delta, end="", flush=True)
continue
if event.type != "run_item_stream_event":
continue
item = event.item
if item.type == "tool_call_item":
tool_name = getattr(item.raw_item, "name", "unknown")
tool_args = getattr(item.raw_item, "arguments", "")
print(f"\n[tool call] {tool_name}({tool_args})")
elif item.type == "tool_call_output_item":
print(f"[tool result] {item.output}")
if printed_reasoning or printed_output:
print("\n")
if not result.interruptions:
break
state = result.to_state()
for interruption in result.interruptions:
question = f"Approve {interruption.name} with args {interruption.arguments}?"
if ask_approval(question):
state.approve(interruption)
else:
state.reject(interruption)
result = ws.run_streamed(agent, state)
if result.last_response_id is None:
raise RuntimeError("The streamed run completed without a response_id.")
final_output = str(result.final_output)
print(f"response_id: {result.last_response_id}")
print(f"final_output: {final_output}\n")
return result.last_response_id, final_output
async def main() -> None:
model_name = os.getenv("OPENAI_MODEL", "gpt-5.6-sol")
policy_agent = Agent(
name="RefundPolicySpecialist",
instructions=(
"You are a refund policy specialist. The policy is simple: orders delivered "
"within 7 days are eligible for a full refund, and older delivered orders "
"are not. Return a short answer with eligibility and a one-line reason."
),
model=model_name,
model_settings=ModelSettings(max_tokens=120),
)
support_agent = Agent(
name="SupportAgent",
instructions=(
"You are a support agent. For refund requests, do this in order: "
"1) call lookup_order, 2) call refund_policy_specialist, 3) if the user "
"asked to proceed and the order is eligible, call submit_refund. "
"When asked for only the refund ticket, return only the ticket token "
"(for example RF-1001)."
),
tools=[
lookup_order,
policy_agent.as_tool(
tool_name="refund_policy_specialist",
tool_description="Check refund eligibility and explain the policy decision.",
),
submit_refund,
],
model=model_name,
model_settings=ModelSettings(
max_tokens=200,
reasoning=Reasoning(effort="medium", summary="detailed"),
),
)
try:
# You can skip this helper and call Runner.run_streamed(...) directly.
# It will still work, but each run will create/connect again unless you manually
# reuse the same RunConfig/provider. This helper makes that reuse easy across turns
# (and nested agent-as-tool runs) so the websocket connection can stay warm.
async with responses_websocket_session() as ws:
with trace("Responses WS support example") as current_trace:
print(f"Using model={model_name}")
print(f"trace_id={current_trace.trace_id}")
first_response_id, _ = await run_streamed_turn(
ws,
support_agent,
(
"Customer wants a refund for order ORD-1001 because the mouse arrived "
"damaged. Please check the order, ask the refund policy specialist, and "
"if it is eligible submit the refund. Reply with only the refund ticket."
),
)
await run_streamed_turn(
ws,
support_agent,
"What refund ticket did you just create? Reply with only the ticket.",
previous_response_id=first_response_id,
)
except RuntimeError as exc:
if "closed before any response events" in str(exc):
print(
"\nWebsocket mode closed before sending events. This usually means the "
"feature is not enabled for this account/model yet."
)
return
raise
if __name__ == "__main__":
asyncio.run(main())
+181
View File
@@ -0,0 +1,181 @@
import asyncio
import json
from agents import (
Agent,
Runner,
ToolGuardrailFunctionOutput,
ToolInputGuardrailData,
ToolOutputGuardrailData,
ToolOutputGuardrailTripwireTriggered,
function_tool,
tool_input_guardrail,
tool_output_guardrail,
)
@function_tool
def send_email(to: str, subject: str, body: str) -> str:
"""Send an email to the specified recipient."""
return f"Email sent to {to} with subject '{subject}'"
@function_tool
def get_user_data(user_id: str) -> dict[str, str]:
"""Get user data by ID."""
# Simulate returning sensitive data
return {
"user_id": user_id,
"name": "John Doe",
"email": "john@example.com",
"ssn": "123-45-6789", # Sensitive data that should be blocked!
"phone": "555-1234",
}
@function_tool
def get_contact_info(user_id: str) -> dict[str, str]:
"""Get contact info by ID."""
return {
"user_id": user_id,
"name": "Jane Smith",
"email": "jane@example.com",
"phone": "555-1234",
}
@tool_input_guardrail
def reject_sensitive_words(data: ToolInputGuardrailData) -> ToolGuardrailFunctionOutput:
"""Reject tool calls that contain sensitive words in arguments."""
try:
args = json.loads(data.context.tool_arguments) if data.context.tool_arguments else {}
except json.JSONDecodeError:
return ToolGuardrailFunctionOutput(output_info="Invalid JSON arguments")
# Check for suspicious content
sensitive_words = [
"password",
"hack",
"exploit",
"malware",
"ACME",
]
for key, value in args.items():
value_str = str(value).lower()
for word in sensitive_words:
if word.lower() in value_str:
# Reject tool call and inform the model the function was not called
return ToolGuardrailFunctionOutput.reject_content(
message=f"🚨 Tool call blocked: contains '{word}'",
output_info={"blocked_word": word, "argument": key},
)
return ToolGuardrailFunctionOutput(output_info="Input validated")
@tool_output_guardrail
def block_sensitive_output(data: ToolOutputGuardrailData) -> ToolGuardrailFunctionOutput:
"""Block tool outputs that contain sensitive data."""
output_str = str(data.output).lower()
# Check for sensitive data patterns
if "ssn" in output_str or "123-45-6789" in output_str:
# Use raise_exception to halt execution completely for sensitive data
return ToolGuardrailFunctionOutput.raise_exception(
output_info={"blocked_pattern": "SSN", "tool": data.context.tool_name},
)
return ToolGuardrailFunctionOutput(output_info="Output validated")
@tool_output_guardrail
def reject_phone_numbers(data: ToolOutputGuardrailData) -> ToolGuardrailFunctionOutput:
"""Reject function output containing phone numbers."""
output_str = str(data.output)
if "555-1234" in output_str:
return ToolGuardrailFunctionOutput.reject_content(
message="User data not retrieved as it contains a phone number which is restricted.",
output_info={"redacted": "phone_number"},
)
return ToolGuardrailFunctionOutput(output_info="Phone number check passed")
# Apply guardrails to tools
send_email.tool_input_guardrails = [reject_sensitive_words]
get_user_data.tool_output_guardrails = [block_sensitive_output]
get_contact_info.tool_output_guardrails = [reject_phone_numbers]
agent = Agent(
name="Secure Assistant",
instructions=(
"You are a helpful assistant with access to email and user data tools. "
"When the user provides all required arguments for a requested tool, call it instead of "
"asking a follow-up question."
),
tools=[send_email, get_user_data, get_contact_info],
)
async def main():
print("=== Tool Guardrails Example ===\n")
try:
# Example 1: Normal operation - should work fine
print("1. Normal email sending:")
result = await Runner.run(
agent,
"Send an email to john@example.com with subject 'Welcome' and body "
"'Welcome to our service.'",
)
print(f"✅ Successful tool execution: {result.final_output}\n")
# Example 2: Input guardrail triggers - function tool call is rejected but execution continues
print("2. Attempting to send email with suspicious content:")
result = await Runner.run(
agent,
"Send an email to john@example.com with subject 'Introduction' and body "
"'Introducing ACME corp.'",
)
print(f"❌ Guardrail rejected function tool call: {result.final_output}\n")
except Exception as e:
print(f"Error: {e}\n")
try:
# Example 3: Output guardrail triggers - should raise exception for sensitive data
print("3. Attempting to get user data (contains SSN). Execution blocked:")
result = await Runner.run(agent, "Get the data for user ID user123")
print(f"✅ Successful tool execution: {result.final_output}\n")
except ToolOutputGuardrailTripwireTriggered as e:
print("🚨 Output guardrail triggered: Execution halted for sensitive data")
print(f"Details: {e.output.output_info}\n")
try:
# Example 4: Output guardrail triggers - reject returning function tool output but continue execution
print("4. Rejecting function tool output containing phone numbers:")
result = await Runner.run(agent, "Get contact info for user456")
print(f"❌ Guardrail rejected function tool output: {result.final_output}\n")
except Exception as e:
print(f"Error: {e}\n")
if __name__ == "__main__":
asyncio.run(main())
"""
Example output:
=== Tool Guardrails Example ===
1. Normal email sending:
✅ Successful tool execution: I've sent a welcome email to john@example.com with an appropriate subject and greeting message.
2. Attempting to send email with suspicious content:
❌ Guardrail rejected function tool call: I'm unable to send the email as mentioning ACME Corp. is restricted.
3. Attempting to get user data (contains SSN). Execution blocked:
🚨 Output guardrail triggered: Execution halted for sensitive data
Details: {'blocked_pattern': 'SSN', 'tool': 'get_user_data'}
4. Rejecting function tool output containing sensitive data:
❌ Guardrail rejected function tool output: I'm unable to retrieve the contact info for user456 because it contains restricted information.
"""
+36
View File
@@ -0,0 +1,36 @@
import asyncio
from typing import Annotated
from pydantic import BaseModel, Field
from agents import Agent, Runner, function_tool
class Weather(BaseModel):
city: str = Field(description="The city name")
temperature_range: str = Field(description="The temperature range in Celsius")
conditions: str = Field(description="The weather conditions")
@function_tool
def get_weather(city: Annotated[str, "The city to get the weather for"]) -> Weather:
"""Get the current weather information for a specified city."""
print("[debug] get_weather called")
return Weather(city=city, temperature_range="14-20C", conditions="Sunny with wind.")
agent = Agent(
name="Hello world",
instructions="You are a helpful agent.",
tools=[get_weather],
)
async def main():
result = await Runner.run(agent, input="What's the weather in Tokyo?")
print(result.final_output)
# The weather in Tokyo is sunny.
if __name__ == "__main__":
asyncio.run(main())
+47
View File
@@ -0,0 +1,47 @@
import asyncio
from pydantic import BaseModel
from agents import Agent, Runner, Usage, function_tool
class Weather(BaseModel):
city: str
temperature_range: str
conditions: str
@function_tool
def get_weather(city: str) -> Weather:
"""Get the current weather information for a specified city."""
return Weather(city=city, temperature_range="14-20C", conditions="Sunny with wind.")
def print_usage(usage: Usage) -> None:
print("\n=== Usage ===")
print(f"Input tokens: {usage.input_tokens}")
print(f"Output tokens: {usage.output_tokens}")
print(f"Total tokens: {usage.total_tokens}")
print(f"Requests: {usage.requests}")
for i, request in enumerate(usage.request_usage_entries):
print(f" {i + 1}: {request.input_tokens} input, {request.output_tokens} output")
async def main() -> None:
agent = Agent(
name="Usage Demo",
instructions="You are a concise assistant. Use tools if needed.",
tools=[get_weather],
)
result = await Runner.run(agent, "What's the weather in Tokyo?")
print("\nFinal output:")
print(result.final_output)
# Access usage from the run context
print_usage(result.context_wrapper.usage)
if __name__ == "__main__":
asyncio.run(main())