Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
2913f96
Added openai-agents samples.
mfateev May 6, 2025
2db9cc7
Added openai-agents samples.
mfateev May 6, 2025
662b1dd
Cleanup of data converter.
mfateev May 6, 2025
aa25527
Cleanup of data converter.
mfateev May 6, 2025
bbf6269
removed DEBUG logging
mfateev May 6, 2025
5808b61
Moved CustomerServiceWorkflow logic into the update handler for clarity.
mfateev May 7, 2025
5b3a2cb
Added README
mfateev May 7, 2025
bff686b
Naming and imports cleanup.
mfateev May 8, 2025
3fe9601
Bump SDK (#185)
jssmith Jun 17, 2025
e38917c
Bump SDK (#189)
dandavison Jun 1, 2025
980f781
Added support for LangSmith tracing across workflows and activities (…
mfateev Jun 2, 2025
0802789
Convert hello activities to sync (#173)
GSmithApps Jun 9, 2025
e228767
replace local adapters with python sdk
jssmith Jun 13, 2025
742e02d
turn on workflow sandboxing
jssmith Jun 15, 2025
8b2a723
formatting
jssmith Jun 15, 2025
3de8de0
lint fixes
jssmith Jun 15, 2025
e5c0819
update README
jssmith Jun 17, 2025
db835a0
fix test issues
jssmith Jun 18, 2025
2bd9358
Merge remote-tracking branch 'origin/main' into openai-agents
jssmith Jun 18, 2025
282d549
revert auto_heartbeater annotation change
jssmith Jun 18, 2025
f3393de
remove unused context mananager
jssmith Jun 19, 2025
6e2389b
update to released python sdk, cleanup
jssmith Jun 20, 2025
7e9dec7
formatting
jssmith Jun 20, 2025
e4e8e79
Merge remote-tracking branch 'origin/main' into openai-agents
jssmith Jun 20, 2025
3d06dce
Update pyproject.toml
jssmith Jun 20, 2025
543f930
consistency for data converter use
jssmith Jun 20, 2025
233ac15
uv sync
jssmith Jun 20, 2025
99059dd
update tasks queues and executor
jssmith Jun 20, 2025
bd65e7d
cleanup id_reuse_policy and imports
jssmith Jun 20, 2025
d4aab6c
unused imports
jssmith Jun 20, 2025
cfee829
separate customer service workflow from helper functions
jssmith Jun 20, 2025
b3dede8
formatting
jssmith Jun 20, 2025
cf2965e
Delete .claude/settings.local.json
jssmith Jun 20, 2025
a8afca6
comment cleanup
jssmith Jun 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions openai_agents/README.md
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I know some samples don't and we've been burned by this in the past, is there any way for us to add any kind of tests? The testing story with the OpenAI agents is a bit rough I understand, and can also understand if it should be a separate issue because it requires design/thought.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to see integration tests. Unit tests would basically just duplicate what's in the Python SDK.

I've been testing manually and it's getting old.

Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Temporal OpenAI Agents SDK Integration

⚠️ **Experimental** - This module is not yet stable and may change in the future.

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.
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.

See the [module documentation](https://github.com/temporalio/sdk-python/blob/main/temporalio/contrib/openai_agents/README.md) for more information.

## Overview

The integration combines:
- **Temporal workflows** for orchestrating agent control flow and state management
- **OpenAI Agents SDK** for AI agent creation and tool interactions

This approach ensures that AI agent workflows are durable, observable, and can handle failures gracefully.

## Prerequisites

- Temporal server [running locally](https://docs.temporal.io/cli/server#start-dev)
- Required dependencies installed via `uv sync --group openai-agents`
- OpenAI API key set as environment variable: `export OPENAI_API_KEY=your_key_here`

## Running the Examples

1. **Start the worker** (supports all samples):
```bash
uv run openai_agents/run_worker.py
```

2. **Run individual samples** in separate terminals:

### Basic Agent Examples

- **Hello World Agent** - Simple agent that responds in haikus:
```bash
uv run openai_agents/run_hello_world_workflow.py
```

- **Tools Agent** - Agent with access to external tools (weather API):
```bash
uv run openai_agents/run_tools_workflow.py
```

### Advanced Multi-Agent Examples

- **Research Workflow** - Multi-agent research system with specialized roles:
```bash
uv run openai_agents/run_research_workflow.py
```
Features a planner agent, search agent, and writer agent working together.

- **Customer Service Workflow** - Customer service agent with escalation capabilities (interactive):
```bash
uv run openai_agents/run_customer_service_client.py --conversation-id my-conversation-123
```

- **Agents as Tools** - Demonstrate using agents as tools within other agents:
```bash
uv run openai_agents/run_agents_as_tools_workflow.py
```

Empty file added openai_agents/__init__.py
Empty file.
30 changes: 30 additions & 0 deletions openai_agents/run_agents_as_tools_workflow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import asyncio

from temporalio.client import Client
from temporalio.contrib.openai_agents.open_ai_data_converter import (
open_ai_data_converter,
)

from openai_agents.workflows.agents_as_tools_workflow import AgentsAsToolsWorkflow


async def main():
# Create client connected to server at the given address
client = await Client.connect(
"localhost:7233",
data_converter=open_ai_data_converter,
)

# Execute a workflow
result = await client.execute_workflow(
AgentsAsToolsWorkflow.run,
"Translate to English: '¿Cómo estás?'",
id="my-workflow-id",
task_queue="openai-agents-task-queue",
)

print(f"Result: {result}")


if __name__ == "__main__":
asyncio.run(main())
81 changes: 81 additions & 0 deletions openai_agents/run_customer_service_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import argparse
import asyncio

from temporalio.client import (
Client,
WorkflowQueryRejectedError,
WorkflowUpdateFailedError,
)
from temporalio.common import QueryRejectCondition
from temporalio.contrib.openai_agents.open_ai_data_converter import (
open_ai_data_converter,
)
from temporalio.service import RPCError, RPCStatusCode

from openai_agents.workflows.customer_service_workflow import (
CustomerServiceWorkflow,
ProcessUserMessageInput,
)


async def main():
parser = argparse.ArgumentParser()
parser.add_argument("--conversation-id", type=str, required=True)
args = parser.parse_args()

# Create client connected to server at the given address
client = await Client.connect(
"localhost:7233",
data_converter=open_ai_data_converter,
)

handle = client.get_workflow_handle(args.conversation_id)

# Query the workflow for the chat history
# If the workflow is not open, start a new one
Comment on lines +34 to +35
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good use case for update-with-start if we are willing to tolerate the history event

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did some work on this but it's not 100% of the way there. I'm going to leave it alone for now.

start = False
try:
history = await handle.query(
CustomerServiceWorkflow.get_chat_history,
reject_condition=QueryRejectCondition.NOT_OPEN,
)
except WorkflowQueryRejectedError as e:
start = True
except RPCError as e:
if e.status == RPCStatusCode.NOT_FOUND:
start = True
else:
raise e
if start:
await client.start_workflow(
CustomerServiceWorkflow.run,
id=args.conversation_id,
task_queue="openai-agents-task-queue",
)
history = []
print(*history, sep="\n")

# Loop to send messages to the workflow
while True:
user_input = input("Enter your message: ")
message_input = ProcessUserMessageInput(
user_input=user_input, chat_length=len(history)
)
try:
new_history = await handle.execute_update(
CustomerServiceWorkflow.process_user_message, message_input
)
history.extend(new_history)
print(*new_history, sep="\n")
except WorkflowUpdateFailedError:
print("** Stale conversation. Reloading...")
length = len(history)
history = await handle.query(
CustomerServiceWorkflow.get_chat_history,
reject_condition=QueryRejectCondition.NOT_OPEN,
)
print(*history[length:], sep="\n")


if __name__ == "__main__":
asyncio.run(main())
29 changes: 29 additions & 0 deletions openai_agents/run_hello_world_workflow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import asyncio

from temporalio.client import Client
from temporalio.contrib.openai_agents.open_ai_data_converter import (
open_ai_data_converter,
)

from openai_agents.workflows.hello_world_workflow import HelloWorldAgent


async def main():
# Create client connected to server at the given address
client = await Client.connect(
"localhost:7233",
data_converter=open_ai_data_converter,
)

# Execute a workflow
result = await client.execute_workflow(
HelloWorldAgent.run,
"Tell me about recursion in programming.",
id="my-workflow-id",
task_queue="openai-agents-task-queue",
)
print(f"Result: {result}")


if __name__ == "__main__":
asyncio.run(main())
30 changes: 30 additions & 0 deletions openai_agents/run_research_workflow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import asyncio

from temporalio.client import Client
from temporalio.contrib.openai_agents.open_ai_data_converter import (
open_ai_data_converter,
)

from openai_agents.workflows.research_bot_workflow import ResearchWorkflow


async def main():
# Create client connected to server at the given address
client = await Client.connect(
"localhost:7233",
data_converter=open_ai_data_converter,
)

# Execute a workflow
result = await client.execute_workflow(
ResearchWorkflow.run,
"Caribbean vacation spots in April, optimizing for surfing, hiking and water sports",
id="research-workflow",
task_queue="openai-agents-task-queue",
)

print(f"Result: {result}")


if __name__ == "__main__":
asyncio.run(main())
30 changes: 30 additions & 0 deletions openai_agents/run_tools_workflow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import asyncio

from temporalio.client import Client
from temporalio.contrib.openai_agents.open_ai_data_converter import (
open_ai_data_converter,
)

from openai_agents.workflows.tools_workflow import ToolsWorkflow


async def main():
# Create client connected to server at the given address
client = await Client.connect(
"localhost:7233",
data_converter=open_ai_data_converter,
)

# Execute a workflow
result = await client.execute_workflow(
ToolsWorkflow.run,
"What is the weather in Tokio?",
id="tools-workflow",
task_queue="openai-agents-task-queue",
)

print(f"Result: {result}")


if __name__ == "__main__":
asyncio.run(main())
56 changes: 56 additions & 0 deletions openai_agents/run_worker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from __future__ import annotations

import asyncio
import concurrent.futures
from datetime import timedelta

from temporalio import workflow
from temporalio.client import Client
from temporalio.contrib.openai_agents.invoke_model_activity import ModelActivity
from temporalio.contrib.openai_agents.open_ai_data_converter import (
open_ai_data_converter,
)
from temporalio.contrib.openai_agents.temporal_openai_agents import (
set_open_ai_agent_temporal_overrides,
)
from temporalio.worker import Worker

from openai_agents.workflows.agents_as_tools_workflow import AgentsAsToolsWorkflow
from openai_agents.workflows.customer_service_workflow import CustomerServiceWorkflow
from openai_agents.workflows.get_weather_activity import get_weather
from openai_agents.workflows.hello_world_workflow import HelloWorldAgent
from openai_agents.workflows.research_bot_workflow import ResearchWorkflow
from openai_agents.workflows.tools_workflow import ToolsWorkflow


async def main():
with set_open_ai_agent_temporal_overrides(
start_to_close_timeout=timedelta(seconds=60),
):
# Create client connected to server at the given address
client = await Client.connect(
"localhost:7233",
data_converter=open_ai_data_converter,
)

model_activity = ModelActivity(model_provider=None)
worker = Worker(
client,
task_queue="openai-agents-task-queue",
workflows=[
HelloWorldAgent,
ToolsWorkflow,
ResearchWorkflow,
CustomerServiceWorkflow,
AgentsAsToolsWorkflow,
],
activities=[
model_activity.invoke_model_activity,
get_weather,
],
)
await worker.run()


if __name__ == "__main__":
asyncio.run(main())
Empty file.
Loading
Loading