-
Notifications
You must be signed in to change notification settings - Fork 94
OpenAI agents #195
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
OpenAI agents #195
Changes from all commits
2913f96
2db9cc7
662b1dd
aa25527
bbf6269
5808b61
5b3a2cb
bff686b
3fe9601
e38917c
980f781
0802789
e228767
742e02d
8b2a723
3de8de0
e5c0819
db835a0
2bd9358
282d549
f3393de
6e2389b
7e9dec7
e4e8e79
3d06dce
543f930
233ac15
99059dd
bd65e7d
d4aab6c
cfee829
b3dede8
cf2965e
a8afca6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
jssmith marked this conversation as resolved.
Show resolved
Hide resolved
|
| 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 | ||
| ``` | ||
|
|
| 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()) |
| 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
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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()) | ||
| 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()) |
| 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 | ||
jssmith marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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()) | ||
| 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()) |
| 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()) |
Uh oh!
There was an error while loading. Please reload this page.