Skip to content

Commit 14d1e61

Browse files
jssmithmfateevdandavisonGSmithAppscretz
authored
OpenAI agents (temporalio#195)
Add samples using OpenAI Agents SDK --------- Co-authored-by: Maxim Fateev <mfateev@gmail.com> Co-authored-by: Dan Davison <dan.davison@temporal.io> Co-authored-by: Maxim Fateev <mfateev@users.noreply.github.com> Co-authored-by: Grant <14.gsmith.14@gmail.com> Co-authored-by: Chad Retz <chad@temporal.io>
1 parent fd57a62 commit 14d1e61

23 files changed

Lines changed: 3035 additions & 1976 deletions

openai_agents/README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Temporal OpenAI Agents SDK Integration
2+
3+
⚠️ **Experimental** - This module is not yet stable and may change in the future.
4+
5+
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.
6+
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.
7+
8+
See the [module documentation](https://github.com/temporalio/sdk-python/blob/main/temporalio/contrib/openai_agents/README.md) for more information.
9+
10+
## Overview
11+
12+
The integration combines:
13+
- **Temporal workflows** for orchestrating agent control flow and state management
14+
- **OpenAI Agents SDK** for AI agent creation and tool interactions
15+
16+
This approach ensures that AI agent workflows are durable, observable, and can handle failures gracefully.
17+
18+
## Prerequisites
19+
20+
- Temporal server [running locally](https://docs.temporal.io/cli/server#start-dev)
21+
- Required dependencies installed via `uv sync --group openai-agents`
22+
- OpenAI API key set as environment variable: `export OPENAI_API_KEY=your_key_here`
23+
24+
## Running the Examples
25+
26+
1. **Start the worker** (supports all samples):
27+
```bash
28+
uv run openai_agents/run_worker.py
29+
```
30+
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+
```
62+

openai_agents/__init__.py

Whitespace-only changes.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import asyncio
2+
3+
from temporalio.client import Client
4+
from temporalio.contrib.openai_agents.open_ai_data_converter import (
5+
open_ai_data_converter,
6+
)
7+
8+
from openai_agents.workflows.agents_as_tools_workflow import AgentsAsToolsWorkflow
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+
data_converter=open_ai_data_converter,
16+
)
17+
18+
# Execute a workflow
19+
result = await client.execute_workflow(
20+
AgentsAsToolsWorkflow.run,
21+
"Translate to English: '¿Cómo estás?'",
22+
id="my-workflow-id",
23+
task_queue="openai-agents-task-queue",
24+
)
25+
26+
print(f"Result: {result}")
27+
28+
29+
if __name__ == "__main__":
30+
asyncio.run(main())
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import argparse
2+
import asyncio
3+
4+
from temporalio.client import (
5+
Client,
6+
WorkflowQueryRejectedError,
7+
WorkflowUpdateFailedError,
8+
)
9+
from temporalio.common import QueryRejectCondition
10+
from temporalio.contrib.openai_agents.open_ai_data_converter import (
11+
open_ai_data_converter,
12+
)
13+
from temporalio.service import RPCError, RPCStatusCode
14+
15+
from openai_agents.workflows.customer_service_workflow import (
16+
CustomerServiceWorkflow,
17+
ProcessUserMessageInput,
18+
)
19+
20+
21+
async def main():
22+
parser = argparse.ArgumentParser()
23+
parser.add_argument("--conversation-id", type=str, required=True)
24+
args = parser.parse_args()
25+
26+
# Create client connected to server at the given address
27+
client = await Client.connect(
28+
"localhost:7233",
29+
data_converter=open_ai_data_converter,
30+
)
31+
32+
handle = client.get_workflow_handle(args.conversation_id)
33+
34+
# Query the workflow for the chat history
35+
# If the workflow is not open, start a new one
36+
start = False
37+
try:
38+
history = await handle.query(
39+
CustomerServiceWorkflow.get_chat_history,
40+
reject_condition=QueryRejectCondition.NOT_OPEN,
41+
)
42+
except WorkflowQueryRejectedError as e:
43+
start = True
44+
except RPCError as e:
45+
if e.status == RPCStatusCode.NOT_FOUND:
46+
start = True
47+
else:
48+
raise e
49+
if start:
50+
await client.start_workflow(
51+
CustomerServiceWorkflow.run,
52+
id=args.conversation_id,
53+
task_queue="openai-agents-task-queue",
54+
)
55+
history = []
56+
print(*history, sep="\n")
57+
58+
# Loop to send messages to the workflow
59+
while True:
60+
user_input = input("Enter your message: ")
61+
message_input = ProcessUserMessageInput(
62+
user_input=user_input, chat_length=len(history)
63+
)
64+
try:
65+
new_history = await handle.execute_update(
66+
CustomerServiceWorkflow.process_user_message, message_input
67+
)
68+
history.extend(new_history)
69+
print(*new_history, sep="\n")
70+
except WorkflowUpdateFailedError:
71+
print("** Stale conversation. Reloading...")
72+
length = len(history)
73+
history = await handle.query(
74+
CustomerServiceWorkflow.get_chat_history,
75+
reject_condition=QueryRejectCondition.NOT_OPEN,
76+
)
77+
print(*history[length:], sep="\n")
78+
79+
80+
if __name__ == "__main__":
81+
asyncio.run(main())
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import asyncio
2+
3+
from temporalio.client import Client
4+
from temporalio.contrib.openai_agents.open_ai_data_converter import (
5+
open_ai_data_converter,
6+
)
7+
8+
from openai_agents.workflows.hello_world_workflow import HelloWorldAgent
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+
data_converter=open_ai_data_converter,
16+
)
17+
18+
# Execute a workflow
19+
result = await client.execute_workflow(
20+
HelloWorldAgent.run,
21+
"Tell me about recursion in programming.",
22+
id="my-workflow-id",
23+
task_queue="openai-agents-task-queue",
24+
)
25+
print(f"Result: {result}")
26+
27+
28+
if __name__ == "__main__":
29+
asyncio.run(main())
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import asyncio
2+
3+
from temporalio.client import Client
4+
from temporalio.contrib.openai_agents.open_ai_data_converter import (
5+
open_ai_data_converter,
6+
)
7+
8+
from openai_agents.workflows.research_bot_workflow import ResearchWorkflow
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+
data_converter=open_ai_data_converter,
16+
)
17+
18+
# Execute a workflow
19+
result = await client.execute_workflow(
20+
ResearchWorkflow.run,
21+
"Caribbean vacation spots in April, optimizing for surfing, hiking and water sports",
22+
id="research-workflow",
23+
task_queue="openai-agents-task-queue",
24+
)
25+
26+
print(f"Result: {result}")
27+
28+
29+
if __name__ == "__main__":
30+
asyncio.run(main())
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import asyncio
2+
3+
from temporalio.client import Client
4+
from temporalio.contrib.openai_agents.open_ai_data_converter import (
5+
open_ai_data_converter,
6+
)
7+
8+
from openai_agents.workflows.tools_workflow import ToolsWorkflow
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+
data_converter=open_ai_data_converter,
16+
)
17+
18+
# Execute a workflow
19+
result = await client.execute_workflow(
20+
ToolsWorkflow.run,
21+
"What is the weather in Tokio?",
22+
id="tools-workflow",
23+
task_queue="openai-agents-task-queue",
24+
)
25+
26+
print(f"Result: {result}")
27+
28+
29+
if __name__ == "__main__":
30+
asyncio.run(main())

openai_agents/run_worker.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
from __future__ import annotations
2+
3+
import asyncio
4+
import concurrent.futures
5+
from datetime import timedelta
6+
7+
from temporalio import workflow
8+
from temporalio.client import Client
9+
from temporalio.contrib.openai_agents.invoke_model_activity import ModelActivity
10+
from temporalio.contrib.openai_agents.open_ai_data_converter import (
11+
open_ai_data_converter,
12+
)
13+
from temporalio.contrib.openai_agents.temporal_openai_agents import (
14+
set_open_ai_agent_temporal_overrides,
15+
)
16+
from temporalio.worker import Worker
17+
18+
from openai_agents.workflows.agents_as_tools_workflow import AgentsAsToolsWorkflow
19+
from openai_agents.workflows.customer_service_workflow import CustomerServiceWorkflow
20+
from openai_agents.workflows.get_weather_activity import get_weather
21+
from openai_agents.workflows.hello_world_workflow import HelloWorldAgent
22+
from openai_agents.workflows.research_bot_workflow import ResearchWorkflow
23+
from openai_agents.workflows.tools_workflow import ToolsWorkflow
24+
25+
26+
async def main():
27+
with set_open_ai_agent_temporal_overrides(
28+
start_to_close_timeout=timedelta(seconds=60),
29+
):
30+
# Create client connected to server at the given address
31+
client = await Client.connect(
32+
"localhost:7233",
33+
data_converter=open_ai_data_converter,
34+
)
35+
36+
model_activity = ModelActivity(model_provider=None)
37+
worker = Worker(
38+
client,
39+
task_queue="openai-agents-task-queue",
40+
workflows=[
41+
HelloWorldAgent,
42+
ToolsWorkflow,
43+
ResearchWorkflow,
44+
CustomerServiceWorkflow,
45+
AgentsAsToolsWorkflow,
46+
],
47+
activities=[
48+
model_activity.invoke_model_activity,
49+
get_weather,
50+
],
51+
)
52+
await worker.run()
53+
54+
55+
if __name__ == "__main__":
56+
asyncio.run(main())

openai_agents/workflows/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)