From 61057235b8b70368f905c475ea8adf4ed5c4c7a4 Mon Sep 17 00:00:00 2001 From: Brandon Allen Date: Sat, 7 Nov 2026 09:05:36 -0500 Subject: [PATCH 1/8] fix(deps): allow temporalio 1.35 and openai-agents 0.20 temporalio 1.35 moves the OpenAI Agents integration into the temporalio-openai-agents package, which needs openai-agents 0.20+. Depend on it directly, raise openai-agents to >=0.20,<0.23 and openai to >=2.45,<4, and pass the now-required cache_write_tokens to InputTokensDetails. Update the templates and examples capped in #556. This lets consumers pick up temporalio 1.35's bundled Rust bridge with fixed rustls (>=0.23.46) and zip (>=9.0.5). Co-Authored-By: Claude Opus 5.5 --- adk/pyproject.toml | 19 ++--- .../demos/procurement_agent/pyproject.toml | 4 +- .../pyproject.toml | 4 +- .../pyproject.toml | 4 +- .../pyproject.toml | 4 +- .../090_claude_agents_sdk_mvp/pyproject.toml | 2 +- .../100_gemini_litellm/pyproject.toml | 4 +- .../pyproject.toml.j2 | 2 +- .../requirements.txt.j2 | 2 +- .../temporal-openai-agents/pyproject.toml.j2 | 4 +- .../requirements.txt.j2 | 4 +- .../models/temporal_streaming_model.py | 6 +- tests/lib/adk/providers/test_openai_turn.py | 8 +- .../plugins/openai_agents/test_model_usage.py | 2 +- uv.lock | 75 ++++++++++++------- 15 files changed, 83 insertions(+), 61 deletions(-) diff --git a/adk/pyproject.toml b/adk/pyproject.toml index a5f393374..9a17493b6 100644 --- a/adk/pyproject.toml +++ b/adk/pyproject.toml @@ -35,21 +35,22 @@ dependencies = [ "aiohttp>=3.10.10,<4", # Temporal workflows "azure-identity>=1.16.1,<2.0.0", - "temporalio>=1.26.0,<1.35", + # temporalio 1.35 moved the OpenAI Agents integration out of the core + # wheel into `temporalio-openai-agents`; `temporalio.contrib.openai_agents` + # is now a shim that re-exports it. The SDK imports that module in every + # Temporal worker, so the standalone package is a hard dependency. + "temporalio>=1.35.0,<2", + "temporalio-openai-agents>=1.0.0,<2", "cloudpickle>=3.1.1", # Async streaming infra "redis>=5.2.0,<8", # LLM provider integrations "litellm>=1.83.7,<2", - "openai-agents>=0.14.3,<0.15", - # Cap <2.45: openai 2.45.0 makes InputTokensDetails.cache_write_tokens a - # required field, but openai-agents 0.14.x still builds - # InputTokensDetails(cached_tokens=0) (agents/usage.py), so every - # Runner.run_streamed raises a pydantic ValidationError at context setup. - # openai-agents 0.14.8 is the latest release, so there is no newer version - # to bump to; drop this ceiling once openai-agents ships a fix. + # temporalio-openai-agents 1.0 needs openai-agents 0.20.x (openai 2.45+); + # 1.1 needs openai-agents 0.22.x (openai 3.x). + "openai-agents>=0.20,<0.23", # litellm now supports openai 2.x (issue #13711 resolved: https://github.com/BerriAI/litellm/issues/13711) - "openai>=2.2,<2.45", + "openai>=2.45,<4", "claude-agent-sdk>=0.1.0", "pydantic-ai-slim>=1.0,<2", "langgraph-checkpoint>=2.0.0", diff --git a/examples/demos/procurement_agent/pyproject.toml b/examples/demos/procurement_agent/pyproject.toml index 3ebb33d94..c1c2548dc 100644 --- a/examples/demos/procurement_agent/pyproject.toml +++ b/examples/demos/procurement_agent/pyproject.toml @@ -9,8 +9,8 @@ description = "An Agentex agent that manages procurement for building constructi requires-python = ">=3.12" dependencies = [ "agentex-sdk>=0.6.5", - "openai-agents>=0.4.2", - "temporalio>=1.26.0,<1.35", + "openai-agents>=0.20,<0.23", + "temporalio[openai-agents]>=1.35.0,<2", "scale-gp", "aiosqlite", "pytest-html>=4.2.0", diff --git a/examples/tutorials/10_async/10_temporal/060_open_ai_agents_sdk_hello_world/pyproject.toml b/examples/tutorials/10_async/10_temporal/060_open_ai_agents_sdk_hello_world/pyproject.toml index 6ac7b15f1..b7354d654 100644 --- a/examples/tutorials/10_async/10_temporal/060_open_ai_agents_sdk_hello_world/pyproject.toml +++ b/examples/tutorials/10_async/10_temporal/060_open_ai_agents_sdk_hello_world/pyproject.toml @@ -9,8 +9,8 @@ description = "An AgentEx agent" requires-python = ">=3.12" dependencies = [ "agentex-sdk>=0.6.0", - "openai-agents>=0.4.2", - "temporalio>=1.26.0,<1.35", + "openai-agents>=0.20,<0.23", + "temporalio[openai-agents]>=1.35.0,<2", "scale-gp", ] diff --git a/examples/tutorials/10_async/10_temporal/070_open_ai_agents_sdk_tools/pyproject.toml b/examples/tutorials/10_async/10_temporal/070_open_ai_agents_sdk_tools/pyproject.toml index d73970401..fe5f28b2f 100644 --- a/examples/tutorials/10_async/10_temporal/070_open_ai_agents_sdk_tools/pyproject.toml +++ b/examples/tutorials/10_async/10_temporal/070_open_ai_agents_sdk_tools/pyproject.toml @@ -9,8 +9,8 @@ description = "An AgentEx agent" requires-python = ">=3.12" dependencies = [ "agentex-sdk>=0.6.0", - "openai-agents>=0.4.2", - "temporalio>=1.26.0,<1.35", + "openai-agents>=0.20,<0.23", + "temporalio[openai-agents]>=1.35.0,<2", "scale-gp", ] diff --git a/examples/tutorials/10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop/pyproject.toml b/examples/tutorials/10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop/pyproject.toml index 6c19938bd..9460743d1 100644 --- a/examples/tutorials/10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop/pyproject.toml +++ b/examples/tutorials/10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop/pyproject.toml @@ -9,8 +9,8 @@ description = "An AgentEx agent" requires-python = ">=3.12" dependencies = [ "agentex-sdk>=0.6.0", - "openai-agents>=0.4.2", - "temporalio>=1.26.0,<1.35", + "openai-agents>=0.20,<0.23", + "temporalio[openai-agents]>=1.35.0,<2", "scale-gp", ] diff --git a/examples/tutorials/10_async/10_temporal/090_claude_agents_sdk_mvp/pyproject.toml b/examples/tutorials/10_async/10_temporal/090_claude_agents_sdk_mvp/pyproject.toml index 8f7e29361..007f69c47 100644 --- a/examples/tutorials/10_async/10_temporal/090_claude_agents_sdk_mvp/pyproject.toml +++ b/examples/tutorials/10_async/10_temporal/090_claude_agents_sdk_mvp/pyproject.toml @@ -10,7 +10,7 @@ requires-python = ">=3.12" dependencies = [ "agentex-sdk>=0.6.0", "claude-agent-sdk>=0.1.0", - "temporalio>=1.26.0,<1.35", + "temporalio>=1.35.0,<2", "anthropic>=0.40.0", ] diff --git a/examples/tutorials/10_async/10_temporal/100_gemini_litellm/pyproject.toml b/examples/tutorials/10_async/10_temporal/100_gemini_litellm/pyproject.toml index 82d7ddd32..44454a674 100644 --- a/examples/tutorials/10_async/10_temporal/100_gemini_litellm/pyproject.toml +++ b/examples/tutorials/10_async/10_temporal/100_gemini_litellm/pyproject.toml @@ -9,8 +9,8 @@ description = "An AgentEx agent using Gemini via LiteLLM" requires-python = ">=3.12" dependencies = [ "agentex-sdk>=0.6.0", - "openai-agents>=0.4.2", - "temporalio>=1.26.0,<1.35", + "openai-agents>=0.20,<0.23", + "temporalio[openai-agents]>=1.35.0,<2", "scale-gp", "litellm>=1.52.0", ] diff --git a/src/agentex/lib/cli/templates/sync-openai-agents-local-sandbox/pyproject.toml.j2 b/src/agentex/lib/cli/templates/sync-openai-agents-local-sandbox/pyproject.toml.j2 index 79e35cf0b..82c901861 100644 --- a/src/agentex/lib/cli/templates/sync-openai-agents-local-sandbox/pyproject.toml.j2 +++ b/src/agentex/lib/cli/templates/sync-openai-agents-local-sandbox/pyproject.toml.j2 @@ -10,7 +10,7 @@ requires-python = ">=3.12" dependencies = [ "agentex-sdk", "scale-gp", - "openai-agents>=0.14.3,<0.15", + "openai-agents>=0.20,<0.23", "python-dotenv", ] diff --git a/src/agentex/lib/cli/templates/sync-openai-agents-local-sandbox/requirements.txt.j2 b/src/agentex/lib/cli/templates/sync-openai-agents-local-sandbox/requirements.txt.j2 index 6f73c3ae3..a6e23197f 100644 --- a/src/agentex/lib/cli/templates/sync-openai-agents-local-sandbox/requirements.txt.j2 +++ b/src/agentex/lib/cli/templates/sync-openai-agents-local-sandbox/requirements.txt.j2 @@ -5,7 +5,7 @@ agentex-sdk scale-gp # OpenAI Agents SDK (provides agents.sandbox + UnixLocalSandboxClient) -openai-agents>=0.14.3,<0.15 +openai-agents>=0.20,<0.23 # Loads .env for local development python-dotenv diff --git a/src/agentex/lib/cli/templates/temporal-openai-agents/pyproject.toml.j2 b/src/agentex/lib/cli/templates/temporal-openai-agents/pyproject.toml.j2 index 3344fc687..6382f6790 100644 --- a/src/agentex/lib/cli/templates/temporal-openai-agents/pyproject.toml.j2 +++ b/src/agentex/lib/cli/templates/temporal-openai-agents/pyproject.toml.j2 @@ -10,8 +10,8 @@ requires-python = ">=3.12" dependencies = [ "agentex-sdk", "scale-gp", - "temporalio>=1.26.0,<1.35", - "openai-agents>=0.4.2", + "temporalio[openai-agents]>=1.35.0,<2", + "openai-agents>=0.20,<0.23", ] [project.optional-dependencies] diff --git a/src/agentex/lib/cli/templates/temporal-openai-agents/requirements.txt.j2 b/src/agentex/lib/cli/templates/temporal-openai-agents/requirements.txt.j2 index ff64fb550..091dc834d 100644 --- a/src/agentex/lib/cli/templates/temporal-openai-agents/requirements.txt.j2 +++ b/src/agentex/lib/cli/templates/temporal-openai-agents/requirements.txt.j2 @@ -1,4 +1,4 @@ agentex-sdk scale-gp -temporalio>=1.26.0,<1.35 -openai-agents>=0.4.2 +temporalio[openai-agents]>=1.35.0,<2 +openai-agents>=0.20,<0.23 diff --git a/src/agentex/lib/core/temporal/plugins/openai_agents/models/temporal_streaming_model.py b/src/agentex/lib/core/temporal/plugins/openai_agents/models/temporal_streaming_model.py index c985d5e65..888caea06 100644 --- a/src/agentex/lib/core/temporal/plugins/openai_agents/models/temporal_streaming_model.py +++ b/src/agentex/lib/core/temporal/plugins/openai_agents/models/temporal_streaming_model.py @@ -1179,6 +1179,10 @@ async def get_response( cached_tokens=getattr( captured_usage.input_tokens_details, "cached_tokens", 0 ), + cache_write_tokens=getattr( + captured_usage.input_tokens_details, "cache_write_tokens", 0 + ) + or 0, ), output_tokens_details=OutputTokensDetails( reasoning_tokens=getattr( @@ -1191,7 +1195,7 @@ async def get_response( input_tokens=0, output_tokens=0, total_tokens=0, - input_tokens_details=InputTokensDetails(cached_tokens=0), + input_tokens_details=InputTokensDetails(cached_tokens=0, cache_write_tokens=0), output_tokens_details=OutputTokensDetails(reasoning_tokens=0), ) diff --git a/tests/lib/adk/providers/test_openai_turn.py b/tests/lib/adk/providers/test_openai_turn.py index d5ad2b5c8..c160b33ba 100644 --- a/tests/lib/adk/providers/test_openai_turn.py +++ b/tests/lib/adk/providers/test_openai_turn.py @@ -44,7 +44,7 @@ def test_usage_mapping_full(): usage = Usage( requests=3, input_tokens=100, - input_tokens_details=InputTokensDetails(cached_tokens=20), + input_tokens_details=InputTokensDetails(cached_tokens=20, cache_write_tokens=0), output_tokens=50, output_tokens_details=OutputTokensDetails(reasoning_tokens=10), total_tokens=150, @@ -80,7 +80,7 @@ def test_usage_mapping_real_zeros_are_preserved(): usage = Usage( requests=1, input_tokens=0, - input_tokens_details=InputTokensDetails(cached_tokens=0), + input_tokens_details=InputTokensDetails(cached_tokens=0, cache_write_tokens=0), output_tokens=0, output_tokens_details=OutputTokensDetails(reasoning_tokens=0), total_tokens=0, @@ -126,7 +126,7 @@ def test_aggregate_usage_multiple(): u1 = Usage( requests=1, input_tokens=10, - input_tokens_details=InputTokensDetails(cached_tokens=2), + input_tokens_details=InputTokensDetails(cached_tokens=2, cache_write_tokens=0), output_tokens=5, output_tokens_details=OutputTokensDetails(reasoning_tokens=1), total_tokens=15, @@ -134,7 +134,7 @@ def test_aggregate_usage_multiple(): u2 = Usage( requests=2, input_tokens=20, - input_tokens_details=InputTokensDetails(cached_tokens=3), + input_tokens_details=InputTokensDetails(cached_tokens=3, cache_write_tokens=0), output_tokens=7, output_tokens_details=OutputTokensDetails(reasoning_tokens=4), total_tokens=27, diff --git a/tests/lib/core/temporal/plugins/openai_agents/test_model_usage.py b/tests/lib/core/temporal/plugins/openai_agents/test_model_usage.py index bf3dc8006..f579a5c57 100644 --- a/tests/lib/core/temporal/plugins/openai_agents/test_model_usage.py +++ b/tests/lib/core/temporal/plugins/openai_agents/test_model_usage.py @@ -107,7 +107,7 @@ async def test_streaming_model_captures_final_response_usage(self, tracing_conte input_tokens=120, output_tokens=80, total_tokens=200, - input_tokens_details=InputTokensDetails(cached_tokens=30), + input_tokens_details=InputTokensDetails(cached_tokens=30, cache_write_tokens=0), output_tokens_details=OutputTokensDetails(reasoning_tokens=40), ) completed = ResponseCompletedEvent.model_construct( diff --git a/uv.lock b/uv.lock index 41c325f25..2de29a989 100644 --- a/uv.lock +++ b/uv.lock @@ -123,6 +123,7 @@ dependencies = [ { name = "scale-gp-beta" }, { name = "starlette" }, { name = "temporalio" }, + { name = "temporalio-openai-agents" }, { name = "typer" }, { name = "uvicorn" }, { name = "watchfiles" }, @@ -146,8 +147,8 @@ requires-dist = [ { name = "langgraph-checkpoint", specifier = ">=2.0.0" }, { name = "litellm", specifier = ">=1.83.7,<2" }, { name = "mcp", specifier = ">=1.4.1" }, - { name = "openai", specifier = ">=2.2,<2.45" }, - { name = "openai-agents", specifier = ">=0.14.3,<0.15" }, + { name = "openai", specifier = ">=2.45,<4" }, + { name = "openai-agents", specifier = ">=0.20,<0.23" }, { name = "opentelemetry-api", specifier = ">=1.20.0" }, { name = "opentelemetry-sdk", specifier = ">=1.20.0" }, { name = "pydantic-ai-slim", specifier = ">=1.0,<2" }, @@ -159,7 +160,8 @@ requires-dist = [ { name = "scale-gp", specifier = ">=0.1.0a59" }, { name = "scale-gp-beta", specifier = ">=0.5.0" }, { name = "starlette", specifier = ">=0.49.1" }, - { name = "temporalio", specifier = ">=1.26.0,<1.35" }, + { name = "temporalio", specifier = ">=1.35.0,<2" }, + { name = "temporalio-openai-agents", specifier = ">=1.0.0,<2" }, { name = "typer", specifier = ">=0.16,<0.17" }, { name = "uvicorn", specifier = ">=0.31.1" }, { name = "watchfiles", specifier = ">=0.24.0,<1.0" }, @@ -1796,7 +1798,7 @@ wheels = [ [[package]] name = "openai" -version = "2.40.0" +version = "2.54.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1808,14 +1810,14 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f9/9f/136562ec6c3b1a50fe06eb0bb34ed21f0d7426ec0140e5cc43ac785b69a5/openai-2.40.0.tar.gz", hash = "sha256:9a756f91f274a24ad6026cbcb2042fd356c8d4a10e8f347b08d34465e585f7a2", size = 781177, upload-time = "2026-06-01T21:48:23.878Z" } +sdist = { url = "https://files.pythonhosted.org/packages/50/9a/8c75e8c8a5b407a0586faeb2afac91674ff955c191ecc1d6d3b6669f6788/openai-2.54.0.tar.gz", hash = "sha256:e3e6f8bc1ba30ddf381ace1a14340eed381cb984a1a59bd0f34b5be3b5d49cfa", size = 1100285, upload-time = "2026-08-11T18:46:59.035Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/46/180e14be801a75bc13f234cb1b594b232adeb9c84e60a9ab1832e8333591/openai-2.40.0-py3-none-any.whl", hash = "sha256:2b205637ff214477f9ce9ab035e9f494db0e3fa8f1e599008953735fbf6ff1ff", size = 1350935, upload-time = "2026-06-01T21:48:21.462Z" }, + { url = "https://files.pythonhosted.org/packages/64/a8/bb76c7356de8ad57f59d5ff993d434df0607f07f08bcc9c9a5c275e399c0/openai-2.54.0-py3-none-any.whl", hash = "sha256:89089789197ccdb87f173a03145ed1598d00795220c93e96cf712b1cbf5e5f2b", size = 1660351, upload-time = "2026-08-11T18:46:56.684Z" }, ] [[package]] name = "openai-agents" -version = "0.14.8" +version = "0.20.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "griffelib" }, @@ -1823,13 +1825,12 @@ dependencies = [ { name = "openai" }, { name = "pydantic" }, { name = "requests" }, - { name = "types-requests" }, { name = "typing-extensions" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d5/8a/d36ab647f05e790ec97dda9e4c0eb39d8840269d6a5194887b5dec92bd0d/openai_agents-0.14.8.tar.gz", hash = "sha256:fe1cb58b4150a07292a94f15d8fd5217ee9195bd6bcd8a6a46fdb1d9b08a70b7", size = 5314520, upload-time = "2026-04-29T03:40:07.6Z" } +sdist = { url = "https://files.pythonhosted.org/packages/57/18/1204bde976436ad85498b759d5297843ebd2e0a214f729eb2dc4423cdfc7/openai_agents-0.20.0.tar.gz", hash = "sha256:a6b37954877868ac233876a27dab1e2a461c53ecbd5fcf6e19e2d276d3e848cd", size = 6146452, upload-time = "2026-08-11T03:12:49.556Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/af/6e/1e9adcedcde7b163579b88a68f765a4915be4ead0713270386d9432cfd2f/openai_agents-0.14.8-py3-none-any.whl", hash = "sha256:2937ef582ccaa45d59e89839ed8948cb2a6d808bc9940f0881793c21f37f7776", size = 817332, upload-time = "2026-04-29T03:40:05.68Z" }, + { url = "https://files.pythonhosted.org/packages/9f/8b/18528f00cb1a5d0b8e00f457f7dc88e7b17439bc520861aae2b638b88710/openai_agents-0.20.0-py3-none-any.whl", hash = "sha256:aaff662b802fa90762ad539e131b9ea387e12e3664b87bc75157ad1b3fc88850", size = 1064338, upload-time = "2026-08-11T03:12:47.615Z" }, ] [[package]] @@ -2932,7 +2933,7 @@ wheels = [ [[package]] name = "temporalio" -version = "1.27.2" +version = "1.35.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nexus-rpc" }, @@ -2940,13 +2941,41 @@ dependencies = [ { name = "types-protobuf" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ca/62/2bc1a9ad29382a3a99f088907ef2024a94420cfef340be1b33026c632828/temporalio-1.27.2.tar.gz", hash = "sha256:633bf2379492f3db1e887d1e64fdac00d9c2ddc3e9382b831d5af68256912e92", size = 2503041, upload-time = "2026-05-14T02:17:57.565Z" } +sdist = { url = "https://files.pythonhosted.org/packages/69/38/03641a02eb18e2b16f58c1c136deafdffeb30828dcd83ebd8ac41eb39fd7/temporalio-1.35.0.tar.gz", hash = "sha256:c7a14f29909f8d847f78de49305af89c0ec5aea39272cd4050124386ac3ca295", size = 2879693, upload-time = "2026-10-28T20:13:38.726Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/d9/8bda464beda3adb897d8d9f0e5fa26b28f2bf63c831c6800cbd73f7728cb/temporalio-1.35.0-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:ac3ab920cc05e24a1d420688a49aabed7b2be8f25dd275200dfa839be30980fd", size = 13989719, upload-time = "2026-10-28T20:13:16.897Z" }, + { url = "https://files.pythonhosted.org/packages/26/c1/94861d72cbe5e1778fcd4685db63231af01d40ad99f0375b4df6d4e60d06/temporalio-1.35.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:51bae017d484450084daa19afafad12fa0af1a00af351ba304104d7b97b44f9a", size = 13552491, upload-time = "2026-10-28T20:13:19.725Z" }, + { url = "https://files.pythonhosted.org/packages/3c/ed/a776afec1033f749ec8f4c0c5264167aedc56a108a5adebfa4084af8aa82/temporalio-1.35.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b93845a2200d23b8edc9a8b0bf78b58bdb5e78721602b3f64072867e07e28504", size = 13927445, upload-time = "2026-10-28T20:13:21.984Z" }, + { url = "https://files.pythonhosted.org/packages/ca/0b/bf0ed1f1b623e1ffd5c21c9e65a331a5c1c4f77f3829dc99ceae99b69f39/temporalio-1.35.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45b5438f41ab154eda4eea40069df2fa8779cb8f5c27f266b917a700bfba8bb8", size = 14386743, upload-time = "2026-10-28T20:13:28.201Z" }, + { url = "https://files.pythonhosted.org/packages/76/d6/373d5ffe28263fc56ea0013bdd45715b4ff84f97d1052519b7c0d9ecd437/temporalio-1.35.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:8a5ce8b1e9c9e2df5fb9f17a345c0ef0a132c787cef8cf5a6593b5d707648253", size = 13988280, upload-time = "2026-10-28T20:13:31.667Z" }, + { url = "https://files.pythonhosted.org/packages/9c/41/47ad6f26002f84d8293d99098c414f75e9e8ff7955d034b7121bd211649b/temporalio-1.35.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:96310ae91b098c9fe2194f0892cef25381e8f870e48d5c069230312bbd1c6262", size = 14494297, upload-time = "2026-10-28T20:13:33.988Z" }, + { url = "https://files.pythonhosted.org/packages/db/33/30942b2d568df2c50089e381ee63b2d141f2ebfb7b4787bac3540f5cf617/temporalio-1.35.0-cp310-abi3-win_amd64.whl", hash = "sha256:6f434c0d3b6d049f4473f9f5d3e80b5e80c986440fd0490f2ee70cc631cbcddd", size = 15317073, upload-time = "2026-10-28T20:13:36.195Z" }, +] + +[package.optional-dependencies] +opentelemetry = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-sdk" }, +] +pydantic = [ + { name = "pydantic" }, +] + +[[package]] +name = "temporalio-openai-agents" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mcp" }, + { name = "openai" }, + { name = "openai-agents" }, + { name = "pydantic" }, + { name = "temporalio", extra = ["opentelemetry", "pydantic"] }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0f/5c/394834735b6987ab398bc37f22483d0e3239c509ba39e31eb56f3267584f/temporalio_openai_agents-1.0.0.tar.gz", hash = "sha256:ae49add8d91f40762b3cd7f4bbc13b93fc667b738b063212c59378d03733e623", size = 72601, upload-time = "2026-09-16T20:54:40.938Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/64/85/9da14f9fbdfae95435d29353bb1c55891581ad6b23c86ca56e72d83035ed/temporalio-1.27.2-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:860f706380faafec8f183f9194d0883c8033a4211c5d19c2c962c45b06cf99e9", size = 14602829, upload-time = "2026-05-14T02:17:45.624Z" }, - { url = "https://files.pythonhosted.org/packages/24/51/b7437991e71eea082dc53222da11f064974917cd59063ba57e13e5895fbc/temporalio-1.27.2-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:a8dc0c680e351f3132809861888d8326dbd5030dd4e570663597e7d4768d9502", size = 13997680, upload-time = "2026-05-14T02:17:53.968Z" }, - { url = "https://files.pythonhosted.org/packages/8c/5d/358065040e6f0cedbf669acd333622999eec737ff868ca7829d727b77746/temporalio-1.27.2-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:805f3de4d193dec52e040e41dbfc9ab44be0206d2e81142ceefaf7b7208058d1", size = 14252199, upload-time = "2026-05-14T02:17:36.972Z" }, - { url = "https://files.pythonhosted.org/packages/72/8a/85d2eab07c3e23fc1124203e76857c69ab9b22d8ccebad0835e294edb754/temporalio-1.27.2-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5bc996cb501b8a918f50037ccee6facb05bb70984acada4c2a3e01f5e7957a38", size = 14779945, upload-time = "2026-05-14T02:18:05.513Z" }, - { url = "https://files.pythonhosted.org/packages/67/81/c9b08609e2a92ecf62c97c59cabfa0608337c8d5cc9941eed5d9a7778840/temporalio-1.27.2-cp310-abi3-win_amd64.whl", hash = "sha256:62a84ae9a60c17932971e4ca3b0f3cd6f32f173b8183e759989376503fb95af6", size = 14981897, upload-time = "2026-05-14T02:17:27.333Z" }, + { url = "https://files.pythonhosted.org/packages/76/e8/d1f31f0ce6f50dac5ed669127a1408da87362678ce63489a9e0b328697df/temporalio_openai_agents-1.0.0-py3-none-any.whl", hash = "sha256:451944e7b2b02f32c7f9035d027f95b26be43913a705a757a8e2bf112dcbd5da", size = 71619, upload-time = "2026-09-16T20:54:39.561Z" }, ] [[package]] @@ -3158,18 +3187,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2e/e8/1fd38926f9cf031188fbc5a96694203ea6f24b0e34bd64a225ec6f6291ba/types_protobuf-6.32.1.20260221-py3-none-any.whl", hash = "sha256:da7cdd947975964a93c30bfbcc2c6841ee646b318d3816b033adc2c4eb6448e4", size = 77956, upload-time = "2026-02-21T03:55:12.894Z" }, ] -[[package]] -name = "types-requests" -version = "2.33.0.20260518" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e0/01/c5a19253fe1ac159159ddf9a3a07cec8bb5e486ec4d9002ad2821da0e5d2/types_requests-2.33.0.20260518.tar.gz", hash = "sha256:df7bd3bfe0ca8402dfb841e7d9be714bb5578203283d66d7dc4ef69343449a5e", size = 24752, upload-time = "2026-05-18T06:07:37.966Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/bc/b139710a3b6018f7fb2b9508b35c8af564e61bf2bf4fa619d088f3e16f85/types_requests-2.33.0.20260518-py3-none-any.whl", hash = "sha256:626d697d1adaaff76e2044dc8c5c051d8f21abc157bdfe204a75558076fe0bf0", size = 21391, upload-time = "2026-05-18T06:07:37.044Z" }, -] - [[package]] name = "typing-extensions" version = "4.15.0" From 3151b01b44642d72457ec9819c41402e3e7bd772 Mon Sep 17 00:00:00 2001 From: Daniel Miller Date: Mon, 9 Nov 2026 11:30:20 -0500 Subject: [PATCH 2/8] fix(ci): resolve tutorials against the local SDK in one pass Tutorial CI failed on this branch for two reasons: - uv run --with resolved each tutorial against the released agentex-sdk first, then overlaid the local wheel. Every released SDK caps openai-agents below 0.15, so the >=0.20 pins were unsatisfiable. The overlay also dropped tutorial extras, so 130_langgraph lost temporalio-langgraph on temporalio 1.35. - The Docker builds install agentex-sdk from PyPI, where no release allows temporalio 1.35 yet. run_agent_test.sh now resolves the tutorial and both wheels together (--no-project --with-editable .). The tutorial pins are wide again, so agentex-sdk picks the temporalio and openai-agents versions: the released SDK today, 1.35 once this ships. Co-Authored-By: Claude Opus 5.5 --- examples/demos/procurement_agent/pyproject.toml | 4 ++-- .../pyproject.toml | 4 ++-- .../070_open_ai_agents_sdk_tools/pyproject.toml | 4 ++-- .../pyproject.toml | 4 ++-- .../090_claude_agents_sdk_mvp/pyproject.toml | 2 +- .../10_temporal/100_gemini_litellm/pyproject.toml | 4 ++-- examples/tutorials/run_agent_test.sh | 12 +++++++++--- 7 files changed, 20 insertions(+), 14 deletions(-) diff --git a/examples/demos/procurement_agent/pyproject.toml b/examples/demos/procurement_agent/pyproject.toml index c1c2548dc..8f5f23df0 100644 --- a/examples/demos/procurement_agent/pyproject.toml +++ b/examples/demos/procurement_agent/pyproject.toml @@ -9,8 +9,8 @@ description = "An Agentex agent that manages procurement for building constructi requires-python = ">=3.12" dependencies = [ "agentex-sdk>=0.6.5", - "openai-agents>=0.20,<0.23", - "temporalio[openai-agents]>=1.35.0,<2", + "openai-agents>=0.14.3,<0.23", + "temporalio>=1.26.0,<2", "scale-gp", "aiosqlite", "pytest-html>=4.2.0", diff --git a/examples/tutorials/10_async/10_temporal/060_open_ai_agents_sdk_hello_world/pyproject.toml b/examples/tutorials/10_async/10_temporal/060_open_ai_agents_sdk_hello_world/pyproject.toml index b7354d654..2e6e31d0f 100644 --- a/examples/tutorials/10_async/10_temporal/060_open_ai_agents_sdk_hello_world/pyproject.toml +++ b/examples/tutorials/10_async/10_temporal/060_open_ai_agents_sdk_hello_world/pyproject.toml @@ -9,8 +9,8 @@ description = "An AgentEx agent" requires-python = ">=3.12" dependencies = [ "agentex-sdk>=0.6.0", - "openai-agents>=0.20,<0.23", - "temporalio[openai-agents]>=1.35.0,<2", + "openai-agents>=0.14.3,<0.23", + "temporalio>=1.26.0,<2", "scale-gp", ] diff --git a/examples/tutorials/10_async/10_temporal/070_open_ai_agents_sdk_tools/pyproject.toml b/examples/tutorials/10_async/10_temporal/070_open_ai_agents_sdk_tools/pyproject.toml index fe5f28b2f..03238c6e0 100644 --- a/examples/tutorials/10_async/10_temporal/070_open_ai_agents_sdk_tools/pyproject.toml +++ b/examples/tutorials/10_async/10_temporal/070_open_ai_agents_sdk_tools/pyproject.toml @@ -9,8 +9,8 @@ description = "An AgentEx agent" requires-python = ">=3.12" dependencies = [ "agentex-sdk>=0.6.0", - "openai-agents>=0.20,<0.23", - "temporalio[openai-agents]>=1.35.0,<2", + "openai-agents>=0.14.3,<0.23", + "temporalio>=1.26.0,<2", "scale-gp", ] diff --git a/examples/tutorials/10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop/pyproject.toml b/examples/tutorials/10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop/pyproject.toml index 9460743d1..71b57ad0c 100644 --- a/examples/tutorials/10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop/pyproject.toml +++ b/examples/tutorials/10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop/pyproject.toml @@ -9,8 +9,8 @@ description = "An AgentEx agent" requires-python = ">=3.12" dependencies = [ "agentex-sdk>=0.6.0", - "openai-agents>=0.20,<0.23", - "temporalio[openai-agents]>=1.35.0,<2", + "openai-agents>=0.14.3,<0.23", + "temporalio>=1.26.0,<2", "scale-gp", ] diff --git a/examples/tutorials/10_async/10_temporal/090_claude_agents_sdk_mvp/pyproject.toml b/examples/tutorials/10_async/10_temporal/090_claude_agents_sdk_mvp/pyproject.toml index 007f69c47..18bab2338 100644 --- a/examples/tutorials/10_async/10_temporal/090_claude_agents_sdk_mvp/pyproject.toml +++ b/examples/tutorials/10_async/10_temporal/090_claude_agents_sdk_mvp/pyproject.toml @@ -10,7 +10,7 @@ requires-python = ">=3.12" dependencies = [ "agentex-sdk>=0.6.0", "claude-agent-sdk>=0.1.0", - "temporalio>=1.35.0,<2", + "temporalio>=1.26.0,<2", "anthropic>=0.40.0", ] diff --git a/examples/tutorials/10_async/10_temporal/100_gemini_litellm/pyproject.toml b/examples/tutorials/10_async/10_temporal/100_gemini_litellm/pyproject.toml index 44454a674..e9ee12085 100644 --- a/examples/tutorials/10_async/10_temporal/100_gemini_litellm/pyproject.toml +++ b/examples/tutorials/10_async/10_temporal/100_gemini_litellm/pyproject.toml @@ -9,8 +9,8 @@ description = "An AgentEx agent using Gemini via LiteLLM" requires-python = ">=3.12" dependencies = [ "agentex-sdk>=0.6.0", - "openai-agents>=0.20,<0.23", - "temporalio[openai-agents]>=1.35.0,<2", + "openai-agents>=0.14.3,<0.23", + "temporalio>=1.26.0,<2", "scale-gp", "litellm>=1.52.0", ] diff --git a/examples/tutorials/run_agent_test.sh b/examples/tutorials/run_agent_test.sh index ead49363e..68ea8738f 100755 --- a/examples/tutorials/run_agent_test.sh +++ b/examples/tutorials/run_agent_test.sh @@ -144,8 +144,13 @@ start_agent() { return 1 fi - # Pass both wheels so the local heavy resolves its slim dep locally - uv run --with "$heavy_wheel" --with "$slim_wheel" agentex agents run --manifest "$manifest_path" > "$logfile" 2>&1 & + # Resolve the tutorial and both local wheels in ONE pass. Plain + # `uv run --with` first resolves the tutorial's own deps against PyPI's + # released agentex-sdk, then overlays the local wheel without re-applying + # the tutorial's extras (e.g. temporalio[langgraph]). That breaks whenever + # the local SDK moves a shared dep (temporalio 1.35 split its plugins into + # temporalio-openai-agents / temporalio-langgraph). + uv run --no-project --with "$heavy_wheel" --with "$slim_wheel" --with-editable . agentex agents run --manifest "$manifest_path" > "$logfile" 2>&1 & else uv run agentex agents run --manifest manifest.yaml > "$logfile" 2>&1 & fi @@ -290,7 +295,8 @@ run_test() { echo -e "${RED}❌ BUILD_CLI=true but a wheel is missing (heavy='${heavy_wheel}' slim='${slim_wheel}'); refusing to test against the pre-installed SDK${NC}" return 1 fi - pytest_cmd=("uv" "run" "--with" "$heavy_wheel" "--with" "$slim_wheel" "--with" "pytest" "--with" "pytest-asyncio" "pytest") + # Same single-pass resolution as the agent start (see start_agent). + pytest_cmd=("uv" "run" "--no-project" "--with" "$heavy_wheel" "--with" "$slim_wheel" "--with-editable" "." "--with" "pytest" "--with" "pytest-asyncio" "pytest") fi local max_retries=5 From b1c41ffe7a19d97796ab90019b9c645a414e6560 Mon Sep 17 00:00:00 2001 From: Daniel Miller Date: Mon, 9 Nov 2026 11:32:12 -0500 Subject: [PATCH 3/8] fix(tutorials): pin gpt-4.1 in openai-agents tutorials openai-agents changed its implicit default model from gpt-4.1 (0.14) to gpt-5.6-luna (0.20+). The new default writes a text preamble before calling tools, so 040_other_sdks stopped seeing the tool request message before the first agent text. Pin the model these tutorials were written and tested against. Co-Authored-By: Claude Opus 5.5 --- .../tutorials/10_async/00_base/040_other_sdks/project/acp.py | 2 ++ .../060_open_ai_agents_sdk_hello_world/project/workflow.py | 2 ++ .../070_open_ai_agents_sdk_tools/project/workflow.py | 2 ++ .../project/workflow.py | 2 ++ 4 files changed, 8 insertions(+) diff --git a/examples/tutorials/10_async/00_base/040_other_sdks/project/acp.py b/examples/tutorials/10_async/00_base/040_other_sdks/project/acp.py index d06d39f22..a4d9dddea 100644 --- a/examples/tutorials/10_async/00_base/040_other_sdks/project/acp.py +++ b/examples/tutorials/10_async/00_base/040_other_sdks/project/acp.py @@ -236,6 +236,8 @@ async def run_openai_agent_with_custom_streaming( agent = Agent( name=agent_name, instructions=agent_instructions, + # Pin the model: openai-agents' implicit default changes between releases. + model="gpt-4.1", mcp_servers=servers, ) diff --git a/examples/tutorials/10_async/10_temporal/060_open_ai_agents_sdk_hello_world/project/workflow.py b/examples/tutorials/10_async/10_temporal/060_open_ai_agents_sdk_hello_world/project/workflow.py index e01f40ce6..29141482e 100644 --- a/examples/tutorials/10_async/10_temporal/060_open_ai_agents_sdk_hello_world/project/workflow.py +++ b/examples/tutorials/10_async/10_temporal/060_open_ai_agents_sdk_hello_world/project/workflow.py @@ -163,6 +163,8 @@ async def on_task_event_send(self, params: SendEventParams) -> None: name="Haiku Assistant", instructions="You are a friendly assistant who always responds in the form of a haiku. " "Each response should be exactly 3 lines following the 5-7-5 syllable pattern.", + # Pin the model: openai-agents' implicit default changes between releases. + model="gpt-4.1", ) # ============================================================================ diff --git a/examples/tutorials/10_async/10_temporal/070_open_ai_agents_sdk_tools/project/workflow.py b/examples/tutorials/10_async/10_temporal/070_open_ai_agents_sdk_tools/project/workflow.py index 2204d3a05..f22011c21 100644 --- a/examples/tutorials/10_async/10_temporal/070_open_ai_agents_sdk_tools/project/workflow.py +++ b/examples/tutorials/10_async/10_temporal/070_open_ai_agents_sdk_tools/project/workflow.py @@ -197,6 +197,8 @@ async def on_task_event_send(self, params: SendEventParams) -> None: weather_agent = Agent( name="Weather Assistant", instructions="You are a helpful weather agent. Use the get_weather tool to get the weather for a given city.", + # Pin the model: openai-agents' implicit default changes between releases. + model="gpt-4.1", tools=[ # activity_as_tool() converts a Temporal activity into an agent tool # The get_weather activity will be executed with durability guarantees diff --git a/examples/tutorials/10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop/project/workflow.py b/examples/tutorials/10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop/project/workflow.py index 4f11ac4c0..3428aa836 100644 --- a/examples/tutorials/10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop/project/workflow.py +++ b/examples/tutorials/10_async/10_temporal/080_open_ai_agents_sdk_human_in_the_loop/project/workflow.py @@ -178,6 +178,8 @@ async def on_task_event_send(self, params: SendEventParams) -> None: confirm_order_agent = Agent( name="Confirm Order", instructions="You are a helpful confirm order agent. When a user asks you to confirm an order, use the wait_for_confirmation tool to wait for confirmation.", + # Pin the model: openai-agents' implicit default changes between releases. + model="gpt-4.1", tools=[ wait_for_confirmation, ], From 2675ec7a79bf1f37f33f1e1b4fdc507c742703a4 Mon Sep 17 00:00:00 2001 From: Daniel Miller Date: Mon, 9 Nov 2026 11:54:08 -0500 Subject: [PATCH 4/8] fix(deps): keep openai-agents on 0.20 and openai on 2.x temporalio-openai-agents ships two incompatible sets: 1.0 needs openai-agents 0.20 with openai 2.x, and 1.1 needs openai-agents 0.22 with openai 3.x. The old ranges let a fresh install pick the 1.1 set, which neither uv.lock nor the test suite covers, and the 010_agent_chat tutorial stopped responding on it. Narrow the ranges to the 1.0 set. The locked versions don't change. Co-Authored-By: Claude Opus 5.5 --- adk/pyproject.toml | 10 ++++++---- uv.lock | 6 +++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/adk/pyproject.toml b/adk/pyproject.toml index 9a17493b6..200cee181 100644 --- a/adk/pyproject.toml +++ b/adk/pyproject.toml @@ -40,17 +40,19 @@ dependencies = [ # is now a shim that re-exports it. The SDK imports that module in every # Temporal worker, so the standalone package is a hard dependency. "temporalio>=1.35.0,<2", - "temporalio-openai-agents>=1.0.0,<2", + "temporalio-openai-agents>=1.0.0,<1.1", "cloudpickle>=3.1.1", # Async streaming infra "redis>=5.2.0,<8", # LLM provider integrations "litellm>=1.83.7,<2", # temporalio-openai-agents 1.0 needs openai-agents 0.20.x (openai 2.45+); - # 1.1 needs openai-agents 0.22.x (openai 3.x). - "openai-agents>=0.20,<0.23", + # 1.1 needs openai-agents 0.22.x (openai 3.x). Stay on the 1.0 / 0.20 / + # openai 2.x set: it's what uv.lock and the test suite cover. A fresh + # install that picked 0.22 + openai 3 broke the 010_agent_chat tutorial. + "openai-agents>=0.20,<0.21", # litellm now supports openai 2.x (issue #13711 resolved: https://github.com/BerriAI/litellm/issues/13711) - "openai>=2.45,<4", + "openai>=2.45,<3", "claude-agent-sdk>=0.1.0", "pydantic-ai-slim>=1.0,<2", "langgraph-checkpoint>=2.0.0", diff --git a/uv.lock b/uv.lock index 2de29a989..86b18f432 100644 --- a/uv.lock +++ b/uv.lock @@ -147,8 +147,8 @@ requires-dist = [ { name = "langgraph-checkpoint", specifier = ">=2.0.0" }, { name = "litellm", specifier = ">=1.83.7,<2" }, { name = "mcp", specifier = ">=1.4.1" }, - { name = "openai", specifier = ">=2.45,<4" }, - { name = "openai-agents", specifier = ">=0.20,<0.23" }, + { name = "openai", specifier = ">=2.45,<3" }, + { name = "openai-agents", specifier = ">=0.20,<0.21" }, { name = "opentelemetry-api", specifier = ">=1.20.0" }, { name = "opentelemetry-sdk", specifier = ">=1.20.0" }, { name = "pydantic-ai-slim", specifier = ">=1.0,<2" }, @@ -161,7 +161,7 @@ requires-dist = [ { name = "scale-gp-beta", specifier = ">=0.5.0" }, { name = "starlette", specifier = ">=0.49.1" }, { name = "temporalio", specifier = ">=1.35.0,<2" }, - { name = "temporalio-openai-agents", specifier = ">=1.0.0,<2" }, + { name = "temporalio-openai-agents", specifier = ">=1.0.0,<1.1" }, { name = "typer", specifier = ">=0.16,<0.17" }, { name = "uvicorn", specifier = ">=0.31.1" }, { name = "watchfiles", specifier = ">=0.24.0,<1.0" }, From 2b7f1025530fda95c085a29edcac13f5b5cfc063 Mon Sep 17 00:00:00 2001 From: Daniel Miller Date: Mon, 9 Nov 2026 12:12:13 -0500 Subject: [PATCH 5/8] ci(tutorials): print the agent log after the first failed test attempt Five slow retries can hit the 15-minute job timeout before the workflow's "Print agent logs on failure" step runs, so a timed-out tutorial left no agent logs to debug from. Co-Authored-By: Claude Opus 5.5 --- examples/tutorials/run_agent_test.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/tutorials/run_agent_test.sh b/examples/tutorials/run_agent_test.sh index 68ea8738f..3c0fe2188 100755 --- a/examples/tutorials/run_agent_test.sh +++ b/examples/tutorials/run_agent_test.sh @@ -315,6 +315,12 @@ run_test() { if [ $exit_code -eq 0 ]; then break else + # Print the agent log after the first failure. Five slow retries can + # hit the job timeout before the workflow's "Print agent logs" step runs. + if [ $retry_count -eq 0 ] && [[ -f "/tmp/agentex-${name}.log" ]]; then + echo -e "${YELLOW}📋 Agent log after first failed attempt (last 300 lines):${NC}" + tail -300 "/tmp/agentex-${name}.log" + fi retry_count=$((retry_count + 1)) if [ $retry_count -lt $max_retries ]; then sleep 5 From ef1abb66e6db85177030967281c4bf0c5b89b216 Mon Sep 17 00:00:00 2001 From: Daniel Miller Date: Mon, 9 Nov 2026 12:32:22 -0500 Subject: [PATCH 6/8] fix(openai): drop unset Reasoning fields before calling OpenAI ModelSettings.to_oai_model_settings() used model_dump(), which writes every unset Reasoning field as an explicit None. openai 2.45+ added Reasoning.mode, context and generate_summary. After a Temporal activity round-trip, the Responses API got {"reasoning": {"mode": null}} and returned 400 ("Invalid type for 'reasoning.mode'"). That is why 010_agent_chat stopped responding. Rebuild Reasoning from its set fields only, and add a regression test. Co-Authored-By: Claude Opus 5.5 --- .../adk/providers/openai_activities.py | 8 ++++- .../adk/providers/test_openai_activities.py | 33 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/agentex/lib/core/temporal/activities/adk/providers/openai_activities.py b/src/agentex/lib/core/temporal/activities/adk/providers/openai_activities.py index 5f81b20d5..84b17a672 100644 --- a/src/agentex/lib/core/temporal/activities/adk/providers/openai_activities.py +++ b/src/agentex/lib/core/temporal/activities/adk/providers/openai_activities.py @@ -417,7 +417,13 @@ class ModelSettings(BaseModelWithTraceParams): extra_args: dict[str, Any] | None = None def to_oai_model_settings(self) -> OAIModelSettings: - return OAIModelSettings(**self.model_dump(exclude=["trace_id", "parent_span_id"])) + settings = self.model_dump(exclude={"trace_id", "parent_span_id"}) + if self.reasoning is not None: + # model_dump() writes every unset Reasoning field as an explicit None. + # openai >= 2.45 added Reasoning.mode/context/generate_summary, and the + # Responses API rejects {"mode": null}. Keep only the fields that are set. + settings["reasoning"] = Reasoning(**self.reasoning.model_dump(exclude_none=True)) + return OAIModelSettings(**settings) class RunAgentParams(BaseModelWithTraceParams): diff --git a/tests/lib/adk/providers/test_openai_activities.py b/tests/lib/adk/providers/test_openai_activities.py index 964b24545..9d8e751d9 100644 --- a/tests/lib/adk/providers/test_openai_activities.py +++ b/tests/lib/adk/providers/test_openai_activities.py @@ -841,3 +841,36 @@ def _create_common_agent_params(self, **overrides): } defaults.update(overrides) return defaults + + +def test_model_settings_reasoning_omits_unset_fields_after_temporal_roundtrip(): + """Unset Reasoning fields must not reach OpenAI as explicit nulls. + + openai >= 2.45 added Reasoning.mode/context/generate_summary. The Responses + API rejects {"reasoning": {"mode": null}} with a 400, which broke the + 010_agent_chat tutorial once ModelSettings crossed a Temporal activity. + """ + from openai.types.shared import Reasoning + from temporalio.contrib.pydantic import pydantic_data_converter + + from agentex.lib.core.temporal.activities.adk.providers.openai_activities import ModelSettings + + settings = ModelSettings(reasoning=Reasoning(effort="medium", summary="detailed"), temperature=0.2) + converter = pydantic_data_converter.payload_converter + settings = converter.from_payloads(converter.to_payloads([settings]), [ModelSettings])[0] + + oai_settings = settings.to_oai_model_settings() + + assert oai_settings.reasoning is not None + # The OpenAI client serializes request models with exclude_unset. + assert oai_settings.reasoning.model_dump(exclude_unset=True) == {"effort": "medium", "summary": "detailed"} + assert oai_settings.temperature == 0.2 + + +def test_model_settings_without_reasoning_is_unchanged(): + from agentex.lib.core.temporal.activities.adk.providers.openai_activities import ModelSettings + + oai_settings = ModelSettings(temperature=0.5).to_oai_model_settings() + + assert oai_settings.reasoning is None + assert oai_settings.temperature == 0.5 From 02322c3c89767ad9134a0176db21ca5a308e5a5b Mon Sep 17 00:00:00 2001 From: Daniel Miller Date: Mon, 9 Nov 2026 12:32:27 -0500 Subject: [PATCH 7/8] Revert "fix(deps): keep openai-agents on 0.20 and openai on 2.x" The narrowing wasn't the cause of the 010_agent_chat failure: the Reasoning null-field bug was (fixed in the previous commit). It also blocked consumers that need openai-agents 0.21+, so restore the >=0.20,<0.23 / openai <4 ranges. This reverts commit 2675ec7a. Co-Authored-By: Claude Opus 5.5 --- adk/pyproject.toml | 10 ++++------ uv.lock | 6 +++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/adk/pyproject.toml b/adk/pyproject.toml index 200cee181..9a17493b6 100644 --- a/adk/pyproject.toml +++ b/adk/pyproject.toml @@ -40,19 +40,17 @@ dependencies = [ # is now a shim that re-exports it. The SDK imports that module in every # Temporal worker, so the standalone package is a hard dependency. "temporalio>=1.35.0,<2", - "temporalio-openai-agents>=1.0.0,<1.1", + "temporalio-openai-agents>=1.0.0,<2", "cloudpickle>=3.1.1", # Async streaming infra "redis>=5.2.0,<8", # LLM provider integrations "litellm>=1.83.7,<2", # temporalio-openai-agents 1.0 needs openai-agents 0.20.x (openai 2.45+); - # 1.1 needs openai-agents 0.22.x (openai 3.x). Stay on the 1.0 / 0.20 / - # openai 2.x set: it's what uv.lock and the test suite cover. A fresh - # install that picked 0.22 + openai 3 broke the 010_agent_chat tutorial. - "openai-agents>=0.20,<0.21", + # 1.1 needs openai-agents 0.22.x (openai 3.x). + "openai-agents>=0.20,<0.23", # litellm now supports openai 2.x (issue #13711 resolved: https://github.com/BerriAI/litellm/issues/13711) - "openai>=2.45,<3", + "openai>=2.45,<4", "claude-agent-sdk>=0.1.0", "pydantic-ai-slim>=1.0,<2", "langgraph-checkpoint>=2.0.0", diff --git a/uv.lock b/uv.lock index 86b18f432..2de29a989 100644 --- a/uv.lock +++ b/uv.lock @@ -147,8 +147,8 @@ requires-dist = [ { name = "langgraph-checkpoint", specifier = ">=2.0.0" }, { name = "litellm", specifier = ">=1.83.7,<2" }, { name = "mcp", specifier = ">=1.4.1" }, - { name = "openai", specifier = ">=2.45,<3" }, - { name = "openai-agents", specifier = ">=0.20,<0.21" }, + { name = "openai", specifier = ">=2.45,<4" }, + { name = "openai-agents", specifier = ">=0.20,<0.23" }, { name = "opentelemetry-api", specifier = ">=1.20.0" }, { name = "opentelemetry-sdk", specifier = ">=1.20.0" }, { name = "pydantic-ai-slim", specifier = ">=1.0,<2" }, @@ -161,7 +161,7 @@ requires-dist = [ { name = "scale-gp-beta", specifier = ">=0.5.0" }, { name = "starlette", specifier = ">=0.49.1" }, { name = "temporalio", specifier = ">=1.35.0,<2" }, - { name = "temporalio-openai-agents", specifier = ">=1.0.0,<1.1" }, + { name = "temporalio-openai-agents", specifier = ">=1.0.0,<2" }, { name = "typer", specifier = ">=0.16,<0.17" }, { name = "uvicorn", specifier = ">=0.31.1" }, { name = "watchfiles", specifier = ">=0.24.0,<1.0" }, From 1695895d8e0558f94d18f29f0ebfd0f5b2145bda Mon Sep 17 00:00:00 2001 From: Daniel Miller Date: Mon, 9 Nov 2026 12:47:27 -0500 Subject: [PATCH 8/8] test: assert cache_write_tokens is copied and defaults to 0 Use a nonzero cache_write_tokens in the usage fixture and assert it on the response, so a hardcoded zero would fail. Cover payloads where the field is missing or None. Co-Authored-By: Claude Opus 5.5 --- .../plugins/openai_agents/test_model_usage.py | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/tests/lib/core/temporal/plugins/openai_agents/test_model_usage.py b/tests/lib/core/temporal/plugins/openai_agents/test_model_usage.py index f579a5c57..a553b5ec0 100644 --- a/tests/lib/core/temporal/plugins/openai_agents/test_model_usage.py +++ b/tests/lib/core/temporal/plugins/openai_agents/test_model_usage.py @@ -107,7 +107,7 @@ async def test_streaming_model_captures_final_response_usage(self, tracing_conte input_tokens=120, output_tokens=80, total_tokens=200, - input_tokens_details=InputTokensDetails(cached_tokens=30, cache_write_tokens=0), + input_tokens_details=InputTokensDetails(cached_tokens=30, cache_write_tokens=15), output_tokens_details=OutputTokensDetails(reasoning_tokens=40), ) completed = ResponseCompletedEvent.model_construct( @@ -138,6 +138,7 @@ async def test_streaming_model_captures_final_response_usage(self, tracing_conte assert response.usage.output_tokens == 80 assert response.usage.total_tokens == 200 assert response.usage.input_tokens_details.cached_tokens == 30 + assert response.usage.input_tokens_details.cache_write_tokens == 15 assert response.usage.output_tokens_details.reasoning_tokens == 40 # And on the span output for billing @@ -145,6 +146,52 @@ async def test_streaming_model_captures_final_response_usage(self, tracing_conte span = fake_tracer.trace_obj.spans[0] assert _output_dict(span)["usage"] == EXPECTED_USAGE_BLOB + @pytest.mark.parametrize( + "input_tokens_details", + [ + # Older payloads that predate the field + InputTokensDetails.model_construct(cached_tokens=30), + # Field present but null + InputTokensDetails.model_construct(cached_tokens=30, cache_write_tokens=None), + ], + ids=["missing", "none"], + ) + async def test_streaming_model_defaults_cache_write_tokens_to_zero( + self, tracing_contextvars, input_tokens_details + ): + usage = ResponseUsage.model_construct( + input_tokens=120, + output_tokens=80, + total_tokens=200, + input_tokens_details=input_tokens_details, + output_tokens_details=OutputTokensDetails(reasoning_tokens=40), + ) + completed = ResponseCompletedEvent.model_construct( + type="response.completed", + response=Response.model_construct(output=[], usage=usage), + ) + + fake_tracer = FakeTracer() + openai_client = MagicMock() + openai_client.responses.create = AsyncMock(return_value=FakeStream([completed])) + + with patch.object(tsm, "create_async_agentex_client", return_value=MagicMock()): + with patch.object(tsm, "AsyncTracer", return_value=fake_tracer): + model = tsm.TemporalStreamingModel(model_name="gpt-4o", openai_client=openai_client) + + response = await model.get_response( + system_instructions=None, + input="hello", + model_settings=ModelSettings(), + tools=[], + output_schema=None, + handoffs=[], + tracing=None, + ) + + assert response.usage.input_tokens_details.cached_tokens == 30 + assert response.usage.input_tokens_details.cache_write_tokens == 0 + async def test_streaming_model_writes_zero_usage_when_api_reports_none(self, tracing_contextvars): completed = ResponseCompletedEvent.model_construct( type="response.completed",