Skip to content

Commit c207986

Browse files
authored
Merge branch 'main' into env_config_sample
2 parents d6be352 + 6d05457 commit c207986

100 files changed

Lines changed: 3981 additions & 108 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

openai_agents/README.md

Lines changed: 13 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Temporal OpenAI Agents SDK Integration
22

3-
⚠️ **Experimental** - This module is not yet stable and may change in the future.
3+
⚠️ **Public Preview** - This integration is experimental and its interfaces may change prior to General Availability.
44

55
This directory contains samples demonstrating how to use the [OpenAI Agents SDK](https://github.com/openai/openai-agents-python) with Temporal's durable execution engine.
66
These samples are adapted from the [OpenAI Agents SDK examples](https://github.com/openai/openai-agents-python/tree/main/examples) and extended with Temporal's durability and orchestration capabilities.
@@ -21,42 +21,18 @@ This approach ensures that AI agent workflows are durable, observable, and can h
2121
- Required dependencies installed via `uv sync --group openai-agents`
2222
- OpenAI API key set as environment variable: `export OPENAI_API_KEY=your_key_here`
2323

24-
## Running the Examples
24+
## Examples
2525

26-
1. **Start the worker** (supports all samples):
27-
```bash
28-
uv run openai_agents/run_worker.py
29-
```
26+
Each directory contains a complete example with its own README for detailed instructions:
3027

31-
2. **Run individual samples** in separate terminals:
32-
33-
### Basic Agent Examples
34-
35-
- **Hello World Agent** - Simple agent that responds in haikus:
36-
```bash
37-
uv run openai_agents/run_hello_world_workflow.py
38-
```
39-
40-
- **Tools Agent** - Agent with access to external tools (weather API):
41-
```bash
42-
uv run openai_agents/run_tools_workflow.py
43-
```
44-
45-
### Advanced Multi-Agent Examples
46-
47-
- **Research Workflow** - Multi-agent research system with specialized roles:
48-
```bash
49-
uv run openai_agents/run_research_workflow.py
50-
```
51-
Features a planner agent, search agent, and writer agent working together.
52-
53-
- **Customer Service Workflow** - Customer service agent with escalation capabilities (interactive):
54-
```bash
55-
uv run openai_agents/run_customer_service_client.py --conversation-id my-conversation-123
56-
```
57-
58-
- **Agents as Tools** - Demonstrate using agents as tools within other agents:
59-
```bash
60-
uv run openai_agents/run_agents_as_tools_workflow.py
61-
```
28+
- **[Basic Examples](./basic/README.md)** - Simple agent examples including a hello world agent and a tools-enabled agent that can access external APIs like weather services.
29+
- **[Agent Patterns](./agent_patterns/README.md)** - Advanced patterns for agent composition, including using agents as tools within other agents.
30+
- **[Tools](./tools/README.md)** - Demonstrates available tools such as file search, image generation, and others.
31+
- **[Handoffs](./handoffs/README.md)** - Agents collaborating via handoffs.
32+
- **[Hosted MCP](./hosted_mcp/README.md)** - Using the MCP client functionality of the OpenAI Responses API.
33+
- **[Model Providers](./model_providers/README.md)** - Using custom LLM providers (e.g., Anthropic via LiteLLM).
34+
- **[Research Bot](./research_bot/README.md)** - Multi-agent research system with specialized roles: a planner agent, search agent, and writer agent working together to conduct comprehensive research.
35+
- **[Customer Service](./customer_service/README.md)** - Interactive customer service agent with escalation capabilities, demonstrating conversational workflows.
36+
- **[Reasoning Content](./reasoning_content/README.md)** - Example of how to retrieve the thought process of reasoning models.
37+
- **[Financial Research Agent](./financial_research_agent/README.md)** - Multi-agent financial research system with planner, search, analyst, writer, and verifier agents collaborating.
6238

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Agent Patterns
2+
3+
Common agentic patterns extended with Temporal's durable execution capabilities.
4+
5+
*Adapted from [OpenAI Agents SDK agent patterns](https://github.com/openai/openai-agents-python/tree/main/examples/agent_patterns)*
6+
7+
Before running these examples, be sure to review the [prerequisites and background on the integration](../README.md).
8+
9+
## Running the Examples
10+
11+
First, start the worker (supports all patterns):
12+
```bash
13+
uv run openai_agents/agent_patterns/run_worker.py
14+
```
15+
16+
Then run individual examples in separate terminals:
17+
18+
### Deterministic Flows
19+
Sequential agent execution with validation gates - demonstrates breaking complex tasks into smaller steps:
20+
```bash
21+
uv run openai_agents/agent_patterns/run_deterministic_workflow.py
22+
```
23+
24+
### Parallelization
25+
Run multiple agents in parallel and select the best result - useful for improving quality or reducing latency:
26+
```bash
27+
uv run openai_agents/agent_patterns/run_parallelization_workflow.py
28+
```
29+
30+
### LLM-as-a-Judge
31+
Iterative improvement using feedback loops - generate content, evaluate it, and improve until satisfied:
32+
```bash
33+
uv run openai_agents/agent_patterns/run_llm_as_a_judge_workflow.py
34+
```
35+
36+
### Agents as Tools
37+
Use agents as callable tools within other agents - enables composition and specialized task delegation:
38+
```bash
39+
uv run openai_agents/agent_patterns/run_agents_as_tools_workflow.py
40+
```
41+
42+
### Agent Routing and Handoffs
43+
Route requests to specialized agents based on content analysis (adapted for non-streaming):
44+
```bash
45+
uv run openai_agents/agent_patterns/run_routing_workflow.py
46+
```
47+
48+
### Input Guardrails
49+
Pre-execution validation to prevent unwanted requests - demonstrates safety mechanisms:
50+
```bash
51+
uv run openai_agents/agent_patterns/run_input_guardrails_workflow.py
52+
```
53+
54+
### Output Guardrails
55+
Post-execution validation to detect sensitive content - ensures safe responses:
56+
```bash
57+
uv run openai_agents/agent_patterns/run_output_guardrails_workflow.py
58+
```
59+
60+
### Forcing Tool Use
61+
Control tool execution strategies - choose between different approaches to tool usage:
62+
```bash
63+
uv run openai_agents/agent_patterns/run_forcing_tool_use_workflow.py
64+
```
65+
66+
## Pattern Details
67+
68+
### Deterministic Flows
69+
A common tactic is to break down a task into a series of smaller steps. Each task can be performed by an agent, and the output of one agent is used as input to the next. For example, if your task was to generate a story, you could break it down into the following steps:
70+
71+
1. Generate an outline
72+
2. Check outline quality and genre
73+
3. Write the story (only if outline passes validation)
74+
75+
Each of these steps can be performed by an agent. The output of one agent is used as input to the next.
76+
77+
### Parallelization
78+
Running multiple agents in parallel is a common pattern. This can be useful for both latency (e.g. if you have multiple steps that don't depend on each other) and also for other reasons e.g. generating multiple responses and picking the best one.
79+
80+
### LLM-as-a-Judge
81+
LLMs can often improve the quality of their output if given feedback. A common pattern is to generate a response using a model, and then use a second model to provide feedback. You can even use a small model for the initial generation and a larger model for the feedback, to optimize cost.
82+
83+
### Agents as Tools
84+
The mental model for handoffs is that the new agent "takes over". It sees the previous conversation history, and owns the conversation from that point onwards. However, this is not the only way to use agents. You can also use agents as a tool - the tool agent goes off and runs on its own, and then returns the result to the original agent.
85+
86+
### Guardrails
87+
Related to parallelization, you often want to run input guardrails to make sure the inputs to your agents are valid. For example, if you have a customer support agent, you might want to make sure that the user isn't trying to ask for help with a math problem.
88+
89+
You can definitely do this without any special Agents SDK features by using parallelization, but we support a special guardrail primitive. Guardrails can have a "tripwire" - if the tripwire is triggered, the agent execution will immediately stop and a `GuardrailTripwireTriggered` exception will be raised.
90+
91+
This is really useful for latency: for example, you might have a very fast model that runs the guardrail and a slow model that runs the actual agent. You wouldn't want to wait for the slow model to finish, so guardrails let you quickly reject invalid inputs.
92+
93+
## Omitted Examples
94+
95+
The following patterns from the [reference repository](https://github.com/openai/openai-agents-python/tree/main/examples/agent_patterns) are not included in this Temporal adaptation:
96+
97+
- **Streaming Guardrails**: Requires streaming capabilities which are not yet available in the Temporal integration
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import asyncio
2+
3+
from temporalio.client import Client
4+
from temporalio.contrib.openai_agents import OpenAIAgentsPlugin
5+
6+
from openai_agents.agent_patterns.workflows.agents_as_tools_workflow import (
7+
AgentsAsToolsWorkflow,
8+
)
9+
10+
11+
async def main():
12+
# Create client connected to server at the given address
13+
client = await Client.connect(
14+
"localhost:7233",
15+
plugins=[
16+
OpenAIAgentsPlugin(),
17+
],
18+
)
19+
20+
# Execute a workflow
21+
result = await client.execute_workflow(
22+
AgentsAsToolsWorkflow.run,
23+
"Please translate 'Good morning, how are you?' to Spanish and French",
24+
id="agents-as-tools-workflow-example",
25+
task_queue="openai-agents-patterns-task-queue",
26+
)
27+
28+
print(f"Result: {result}")
29+
30+
31+
if __name__ == "__main__":
32+
asyncio.run(main())
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import asyncio
2+
3+
from temporalio.client import Client
4+
from temporalio.contrib.openai_agents import OpenAIAgentsPlugin
5+
6+
from openai_agents.agent_patterns.workflows.deterministic_workflow import (
7+
DeterministicWorkflow,
8+
)
9+
10+
11+
async def main():
12+
# Create client connected to server at the given address
13+
client = await Client.connect(
14+
"localhost:7233",
15+
plugins=[
16+
OpenAIAgentsPlugin(),
17+
],
18+
)
19+
20+
# Execute a workflow
21+
result = await client.execute_workflow(
22+
DeterministicWorkflow.run,
23+
"Write a science fiction story about time travel",
24+
id="deterministic-workflow-example",
25+
task_queue="openai-agents-patterns-task-queue",
26+
)
27+
print(f"Result: {result}")
28+
29+
30+
if __name__ == "__main__":
31+
asyncio.run(main())
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import asyncio
2+
3+
from temporalio.client import Client
4+
from temporalio.contrib.openai_agents import OpenAIAgentsPlugin
5+
6+
from openai_agents.agent_patterns.workflows.forcing_tool_use_workflow import (
7+
ForcingToolUseWorkflow,
8+
)
9+
10+
11+
async def main():
12+
# Create client connected to server at the given address
13+
client = await Client.connect(
14+
"localhost:7233",
15+
plugins=[
16+
OpenAIAgentsPlugin(),
17+
],
18+
)
19+
20+
# Execute workflows with different tool use behaviors
21+
print("Testing default behavior:")
22+
result1 = await client.execute_workflow(
23+
ForcingToolUseWorkflow.run,
24+
"default",
25+
id="forcing-tool-use-workflow-default",
26+
task_queue="openai-agents-patterns-task-queue",
27+
)
28+
print(f"Default result: {result1}")
29+
30+
print("\nTesting first_tool behavior:")
31+
result2 = await client.execute_workflow(
32+
ForcingToolUseWorkflow.run,
33+
"first_tool",
34+
id="forcing-tool-use-workflow-first-tool",
35+
task_queue="openai-agents-patterns-task-queue",
36+
)
37+
print(f"First tool result: {result2}")
38+
39+
print("\nTesting custom behavior:")
40+
result3 = await client.execute_workflow(
41+
ForcingToolUseWorkflow.run,
42+
"custom",
43+
id="forcing-tool-use-workflow-custom",
44+
task_queue="openai-agents-patterns-task-queue",
45+
)
46+
print(f"Custom result: {result3}")
47+
48+
49+
if __name__ == "__main__":
50+
asyncio.run(main())
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import asyncio
2+
3+
from temporalio.client import Client
4+
from temporalio.contrib.openai_agents import OpenAIAgentsPlugin
5+
6+
from openai_agents.agent_patterns.workflows.input_guardrails_workflow import (
7+
InputGuardrailsWorkflow,
8+
)
9+
10+
11+
async def main():
12+
# Create client connected to server at the given address
13+
client = await Client.connect(
14+
"localhost:7233",
15+
plugins=[
16+
OpenAIAgentsPlugin(),
17+
],
18+
)
19+
20+
# Execute a workflow with a normal question (should pass)
21+
result1 = await client.execute_workflow(
22+
InputGuardrailsWorkflow.run,
23+
"What's the capital of California?",
24+
id="input-guardrails-workflow-normal",
25+
task_queue="openai-agents-patterns-task-queue",
26+
)
27+
print(f"Normal question result: {result1}")
28+
29+
# Execute a workflow with a math homework question (should be blocked)
30+
result2 = await client.execute_workflow(
31+
InputGuardrailsWorkflow.run,
32+
"Can you help me solve for x: 2x + 5 = 11?",
33+
id="input-guardrails-workflow-blocked",
34+
task_queue="openai-agents-patterns-task-queue",
35+
)
36+
print(f"Math homework result: {result2}")
37+
38+
39+
if __name__ == "__main__":
40+
asyncio.run(main())
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import asyncio
2+
3+
from temporalio.client import Client
4+
from temporalio.contrib.openai_agents import OpenAIAgentsPlugin
5+
6+
from openai_agents.agent_patterns.workflows.llm_as_a_judge_workflow import (
7+
LLMAsAJudgeWorkflow,
8+
)
9+
10+
11+
async def main():
12+
# Create client connected to server at the given address
13+
client = await Client.connect(
14+
"localhost:7233",
15+
plugins=[
16+
OpenAIAgentsPlugin(),
17+
],
18+
)
19+
20+
# Execute a workflow
21+
result = await client.execute_workflow(
22+
LLMAsAJudgeWorkflow.run,
23+
"A thrilling adventure story about pirates searching for treasure",
24+
id="llm-as-a-judge-workflow-example",
25+
task_queue="openai-agents-patterns-task-queue",
26+
)
27+
print(f"Result: {result}")
28+
29+
30+
if __name__ == "__main__":
31+
asyncio.run(main())
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import asyncio
2+
3+
from temporalio.client import Client
4+
from temporalio.contrib.openai_agents import OpenAIAgentsPlugin
5+
6+
from openai_agents.agent_patterns.workflows.output_guardrails_workflow import (
7+
OutputGuardrailsWorkflow,
8+
)
9+
10+
11+
async def main():
12+
# Create client connected to server at the given address
13+
client = await Client.connect(
14+
"localhost:7233",
15+
plugins=[
16+
OpenAIAgentsPlugin(),
17+
],
18+
)
19+
20+
# Execute a workflow with a normal question (should pass)
21+
result1 = await client.execute_workflow(
22+
OutputGuardrailsWorkflow.run,
23+
"What's the capital of California?",
24+
id="output-guardrails-workflow-normal",
25+
task_queue="openai-agents-patterns-task-queue",
26+
)
27+
print(f"Normal question result: {result1}")
28+
29+
# Execute a workflow with input that might trigger sensitive data output
30+
result2 = await client.execute_workflow(
31+
OutputGuardrailsWorkflow.run,
32+
"My phone number is 650-123-4567. Where do you think I live?",
33+
id="output-guardrails-workflow-sensitive",
34+
task_queue="openai-agents-patterns-task-queue",
35+
)
36+
print(f"Sensitive data result: {result2}")
37+
38+
39+
if __name__ == "__main__":
40+
asyncio.run(main())

0 commit comments

Comments
 (0)