Skip to content

Commit 091d942

Browse files
committed
Merge remote-tracking branch 'origin/main' into update-readme-oai-agents
2 parents 285ff8c + 20bb869 commit 091d942

27 files changed

+2216
-197
lines changed

.github/workflows/build-binaries.yml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,3 @@ jobs:
7474
with:
7575
name: packages-${{ matrix.package-suffix }}
7676
path: dist
77-
78-
- name: Deliberately fail to prevent releasing nexus-rpc w/ GitHub link in pyproject.toml
79-
run: |
80-
echo "This is a deliberate failure to prevent releasing nexus-rpc with a GitHub link in pyproject.toml"
81-
exit 1

pyproject.toml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "temporalio"
3-
version = "1.14.1"
3+
version = "1.15.0"
44
description = "Temporal.io Python SDK"
55
authors = [{ name = "Temporal Technologies Inc", email = "[email protected]" }]
66
requires-python = ">=3.9"
@@ -11,7 +11,7 @@ keywords = [
1111
"workflow",
1212
]
1313
dependencies = [
14-
"nexus-rpc>=1.1.0",
14+
"nexus-rpc==1.1.0",
1515
"protobuf>=3.20,<6",
1616
"python-dateutil>=2.8.2,<3 ; python_version < '3.11'",
1717
"types-protobuf>=3.20",
@@ -57,6 +57,7 @@ dev = [
5757
"pytest-cov>=6.1.1",
5858
"httpx>=0.28.1",
5959
"pytest-pretty>=1.3.0",
60+
"openai-agents[litellm] >= 0.2.3,<0.3"
6061
]
6162

6263
[tool.poe.tasks]
@@ -230,6 +231,3 @@ exclude = [
230231
[tool.uv]
231232
# Prevent uv commands from building the package by default
232233
package = false
233-
234-
[tool.uv.sources]
235-
nexus-rpc = { git = "https://github.com/nexus-rpc/sdk-python.git", rev = "35f574c711193a6e2560d3e6665732a5bb7ae92c" }

temporalio/client.py

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -528,11 +528,12 @@ async def start_workflow(
528528
retries and continue as new.
529529
run_timeout: Timeout of a single workflow run.
530530
task_timeout: Timeout of a single workflow task.
531-
id_reuse_policy: How already-existing IDs are treated.
532-
id_conflict_policy: How already-running workflows of the same ID are
533-
treated. Default is unspecified which effectively means fail the
534-
start attempt. This cannot be set if ``id_reuse_policy`` is set
535-
to terminate if running.
531+
id_conflict_policy: Behavior when a workflow is currently running with the same ID.
532+
Default is UNSPECIFIED, which effectively means fail the start attempt.
533+
Set to USE_EXISTING for idempotent deduplication on workflow ID.
534+
Cannot be set if ``id_reuse_policy`` is set to TERMINATE_IF_RUNNING.
535+
id_reuse_policy: Behavior when a closed workflow with the same ID exists.
536+
Default is ALLOW_DUPLICATE.
536537
retry_policy: Retry policy for the workflow.
537538
cron_schedule: See https://docs.temporal.io/docs/content/what-is-a-temporal-cron-job/
538539
memo: Memo for the workflow.
@@ -2487,9 +2488,6 @@ class WithStartWorkflowOperation(Generic[SelfType, ReturnType]):
24872488
24882489
Update-With-Start allows you to send an update to a workflow, while starting the
24892490
workflow if necessary.
2490-
2491-
.. warning::
2492-
This API is experimental
24932491
"""
24942492

24952493
# Overload for no-param workflow, with_start
@@ -2657,9 +2655,6 @@ def __init__(
26572655
) -> None:
26582656
"""Create a WithStartWorkflowOperation.
26592657
2660-
.. warning::
2661-
This API is experimental
2662-
26632658
See :py:meth:`temporalio.client.Client.start_workflow` for documentation of the
26642659
arguments.
26652660
"""
@@ -2700,11 +2695,7 @@ def __init__(
27002695
self._used = False
27012696

27022697
async def workflow_handle(self) -> WorkflowHandle[SelfType, ReturnType]:
2703-
"""Wait until workflow is running and return a WorkflowHandle.
2704-
2705-
.. warning::
2706-
This API is experimental
2707-
"""
2698+
"""Wait until workflow is running and return a WorkflowHandle."""
27082699
return await self._workflow_handle
27092700

27102701

temporalio/contrib/openai_agents/_invoke_model_activity.py

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@
77
import json
88
from dataclasses import dataclass
99
from datetime import timedelta
10-
from typing import Any, Optional, Union, cast
10+
from typing import Any, Optional, Union
1111

1212
from agents import (
1313
AgentOutputSchemaBase,
14+
CodeInterpreterTool,
1415
FileSearchTool,
1516
FunctionTool,
1617
Handoff,
18+
HostedMCPTool,
19+
ImageGenerationTool,
1720
ModelProvider,
1821
ModelResponse,
1922
ModelSettings,
@@ -25,13 +28,12 @@
2528
UserError,
2629
WebSearchTool,
2730
)
28-
from agents.models.multi_provider import MultiProvider
2931
from openai import (
3032
APIStatusError,
3133
AsyncOpenAI,
32-
AuthenticationError,
33-
PermissionDeniedError,
3434
)
35+
from openai.types.responses.tool_param import Mcp
36+
from pydantic_core import to_json
3537
from typing_extensions import Required, TypedDict
3638

3739
from temporalio import activity
@@ -41,7 +43,9 @@
4143

4244
@dataclass
4345
class HandoffInput:
44-
"""Data conversion friendly representation of a Handoff."""
46+
"""Data conversion friendly representation of a Handoff. Contains only the fields which are needed by the model
47+
execution to determine what to handoff to, not the actual handoff invocation, which remains in the workflow context.
48+
"""
4549

4650
tool_name: str
4751
tool_description: str
@@ -52,15 +56,33 @@ class HandoffInput:
5256

5357
@dataclass
5458
class FunctionToolInput:
55-
"""Data conversion friendly representation of a FunctionTool."""
59+
"""Data conversion friendly representation of a FunctionTool. Contains only the fields which are needed by the model
60+
execution to determine what tool to call, not the actual tool invocation, which remains in the workflow context.
61+
"""
5662

5763
name: str
5864
description: str
5965
params_json_schema: dict[str, Any]
6066
strict_json_schema: bool = True
6167

6268

63-
ToolInput = Union[FunctionToolInput, FileSearchTool, WebSearchTool]
69+
@dataclass
70+
class HostedMCPToolInput:
71+
"""Data conversion friendly representation of a HostedMCPTool. Contains only the fields which are needed by the model
72+
execution to determine what tool to call, not the actual tool invocation, which remains in the workflow context.
73+
"""
74+
75+
tool_config: Mcp
76+
77+
78+
ToolInput = Union[
79+
FunctionToolInput,
80+
FileSearchTool,
81+
WebSearchTool,
82+
ImageGenerationTool,
83+
CodeInterpreterTool,
84+
HostedMCPToolInput,
85+
]
6486

6587

6688
@dataclass
@@ -152,22 +174,31 @@ async def empty_on_invoke_handoff(
152174

153175
# workaround for https://github.com/pydantic/pydantic/issues/9541
154176
# ValidatorIterator returned
155-
input_json = json.dumps(input["input"], default=str)
177+
input_json = to_json(input["input"])
156178
input_input = json.loads(input_json)
157179

158180
def make_tool(tool: ToolInput) -> Tool:
159-
if isinstance(tool, FileSearchTool):
160-
return cast(FileSearchTool, tool)
161-
elif isinstance(tool, WebSearchTool):
162-
return cast(WebSearchTool, tool)
181+
if isinstance(
182+
tool,
183+
(
184+
FileSearchTool,
185+
WebSearchTool,
186+
ImageGenerationTool,
187+
CodeInterpreterTool,
188+
),
189+
):
190+
return tool
191+
elif isinstance(tool, HostedMCPToolInput):
192+
return HostedMCPTool(
193+
tool_config=tool.tool_config,
194+
)
163195
elif isinstance(tool, FunctionToolInput):
164-
t = cast(FunctionToolInput, tool)
165196
return FunctionTool(
166-
name=t.name,
167-
description=t.description,
168-
params_json_schema=t.params_json_schema,
197+
name=tool.name,
198+
description=tool.description,
199+
params_json_schema=tool.params_json_schema,
169200
on_invoke_tool=empty_on_invoke_tool,
170-
strict_json_schema=t.strict_json_schema,
201+
strict_json_schema=tool.strict_json_schema,
171202
)
172203
else:
173204
raise UserError(f"Unknown tool type: {tool.name}")

temporalio/contrib/openai_agents/_openai_runner.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import json
12
import typing
23
from dataclasses import replace
34
from typing import Any, Union
@@ -7,11 +8,13 @@
78
RunConfig,
89
RunResult,
910
RunResultStreaming,
11+
SQLiteSession,
1012
TContext,
1113
Tool,
1214
TResponseInputItem,
1315
)
1416
from agents.run import DEFAULT_AGENT_RUNNER, DEFAULT_MAX_TURNS, AgentRunner
17+
from pydantic_core import to_json
1518

1619
from temporalio import workflow
1720
from temporalio.contrib.openai_agents._model_parameters import ModelActivityParameters
@@ -51,23 +54,38 @@ async def run(
5154
"Provided tool is not a tool type. If using an activity, make sure to wrap it with openai_agents.workflow.activity_as_tool."
5255
)
5356

57+
if starting_agent.mcp_servers:
58+
raise ValueError(
59+
"Temporal OpenAI agent does not support on demand MCP servers."
60+
)
61+
62+
# workaround for https://github.com/pydantic/pydantic/issues/9541
63+
# ValidatorIterator returned
64+
input_json = to_json(input)
65+
input = json.loads(input_json)
66+
5467
context = kwargs.get("context")
5568
max_turns = kwargs.get("max_turns", DEFAULT_MAX_TURNS)
5669
hooks = kwargs.get("hooks")
5770
run_config = kwargs.get("run_config")
5871
previous_response_id = kwargs.get("previous_response_id")
72+
session = kwargs.get("session")
73+
74+
if isinstance(session, SQLiteSession):
75+
raise ValueError("Temporal workflows don't support SQLite sessions.")
5976

6077
if run_config is None:
6178
run_config = RunConfig()
6279

63-
if run_config.model is not None and not isinstance(run_config.model, str):
80+
model_name = run_config.model or starting_agent.model
81+
if model_name is not None and not isinstance(model_name, str):
6482
raise ValueError(
65-
"Temporal workflows require a model name to be a string in the run config."
83+
"Temporal workflows require a model name to be a string in the run config and/or agent."
6684
)
6785
updated_run_config = replace(
6886
run_config,
6987
model=_TemporalModelStub(
70-
run_config.model,
88+
model_name=model_name,
7189
model_params=self.model_params,
7290
),
7391
)
@@ -80,6 +98,7 @@ async def run(
8098
hooks=hooks,
8199
run_config=updated_run_config,
82100
previous_response_id=previous_response_id,
101+
session=session,
83102
)
84103

85104
def run_sync(

0 commit comments

Comments
 (0)