temporalio-openai-codex 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 temporal.io
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,227 @@
1
+ Metadata-Version: 2.4
2
+ Name: temporalio-openai-codex
3
+ Version: 0.0.1
4
+ Summary: Durable OpenAI Codex sessions on Temporal
5
+ Author: Temporal Technologies Inc
6
+ Author-email: Temporal Technologies Inc <sdk@temporal.io>
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Classifier: Typing :: Typed
16
+ Requires-Dist: temporalio>=1.34.0,<2
17
+ Requires-Dist: openai-codex>=0.161.0,<0.164.0
18
+ Requires-Dist: pydantic>=2.12,<3
19
+ Requires-Python: >=3.10
20
+ Project-URL: Homepage, https://github.com/temporalio/ai-integrations/tree/main/python/openai_codex
21
+ Project-URL: Repository, https://github.com/temporalio/ai-integrations
22
+ Project-URL: Documentation, https://docs.temporal.io/develop/python/integrations/openai-codex
23
+ Description-Content-Type: text/markdown
24
+
25
+ # Temporal OpenAI Codex integration
26
+
27
+ **Pre-release.** Run the [Python Codex SDK](https://github.com/openai/codex/tree/main/sdk/python)
28
+ in Temporal Workflows. Install with
29
+ `uv add temporalio-openai-codex`. Import the integration as
30
+ `temporalio.openai_codex` and the upstream SDK as `openai_codex`.
31
+
32
+ Each SDK keeps its exact `openai-codex-cli-bin` dependency. The
33
+ default connector uses that bundled runtime and its companion PATH entries,
34
+ including when another Codex executable is installed on the Worker.
35
+
36
+ ## Use AsyncCodex
37
+
38
+ Register the plugin on the Worker (or its Client):
39
+
40
+ ```python
41
+ from temporalio.openai_codex import OpenAICodexPlugin
42
+ from temporalio.worker import Worker
43
+
44
+ worker = Worker(
45
+ client,
46
+ task_queue="codex",
47
+ workflows=[CodingWorkflow],
48
+ plugins=[OpenAICodexPlugin()],
49
+ )
50
+ ```
51
+
52
+ Use the upstream async API in the Workflow:
53
+
54
+ ```python
55
+ from openai_codex import AsyncCodex, Sandbox
56
+ from temporalio import workflow
57
+ from temporalio.openai_codex import TemporalCodexConfig
58
+
59
+
60
+ @workflow.defn
61
+ class CodingWorkflow:
62
+ @workflow.run
63
+ async def run(self, prompt: str) -> str:
64
+ async with AsyncCodex(TemporalCodexConfig(cwd="/workspace")) as codex:
65
+ thread = await codex.thread_start(sandbox=Sandbox.read_only)
66
+ result = await thread.run(prompt)
67
+ return result.final_response or ""
68
+ ```
69
+
70
+ The plugin configures passthrough imports for `openai_codex` and
71
+ `temporalio.openai_codex`, so Workflow code can import both directly.
72
+
73
+ The plugin adapts construction and asynchronous turn streams inside Workflows,
74
+ including replay. Threads, turn handles, login handles, notifications, and
75
+ `TurnResult` are the upstream types. Ordinary SDK calls outside Workflows keep
76
+ using upstream implementations. Synchronous `Codex` is for Activity or application
77
+ code, not Workflow code.
78
+
79
+ In Workflows, collected failed turns raise `CodexError` with the upstream error
80
+ message, so an uncaught provider failure fails the Workflow. Streaming still yields
81
+ the upstream turn-completion notification, including its failure status and error.
82
+
83
+ ## Approvals and Activity options
84
+
85
+ Pass `TemporalCodexConfig`, a subclass of the upstream `CodexConfig`, to
86
+ `AsyncCodex` for Workflow-specific options:
87
+
88
+ ```python
89
+ from datetime import timedelta
90
+ from openai_codex import AsyncCodex
91
+ from temporalio.openai_codex import CodexActivityOptions, TemporalCodexConfig
92
+ from temporalio.workflow import ActivityConfig
93
+
94
+ async def approve(method, params):
95
+ # Runs in Workflow code. It can wait for a Signal or Update, or call an Activity.
96
+ return {"decision": "accept"}
97
+
98
+ async with AsyncCodex(TemporalCodexConfig(
99
+ cwd="/workspace",
100
+ approval_handler=approve,
101
+ activity_options=CodexActivityOptions(
102
+ connect=ActivityConfig(start_to_close_timeout=timedelta(minutes=2)),
103
+ ),
104
+ )) as codex:
105
+ thread = await codex.thread_start()
106
+ result = await thread.run("Describe the project.")
107
+ ```
108
+
109
+ Synchronous and asynchronous approval handlers receive `(method, params)` as in
110
+ upstream `CodexClient`. Keep callbacks deterministic; put I/O in Activities. Without
111
+ a handler, command and file-change approval requests receive `{"decision": "accept"}`
112
+ and other server requests receive `{}`, matching the SDK default. Set Codex's
113
+ approval and sandbox policies deliberately for your application. These additional
114
+ options apply inside Workflows; ordinary SDK calls use upstream behavior.
115
+
116
+ Only the base `CodexConfig` fields are passed to the connect Activity. Approval
117
+ callbacks, Activity options, and stream topic handles stay in Workflow code.
118
+
119
+ ## Token-by-Token Output
120
+
121
+ Use the optional `stream` topic to expose generated output to callers outside the
122
+ Workflow. [Workflow Streams](https://docs.temporal.io/develop/python/workflows/workflow-streams)
123
+ is currently in Public Preview. Create the stream in `@workflow.init`, then pass
124
+ its topic in `TemporalCodexConfig`:
125
+
126
+ ```python
127
+ from openai_codex import AsyncCodex
128
+ from temporalio import workflow
129
+ from temporalio.contrib.workflow_streams import WorkflowStream
130
+ from temporalio.openai_codex import TemporalCodexConfig
131
+
132
+
133
+ @workflow.defn
134
+ class StreamingWorkflow:
135
+ @workflow.init
136
+ def __init__(self, prompt: str) -> None:
137
+ self.events = WorkflowStream().topic("codex")
138
+
139
+ @workflow.run
140
+ async def run(self, prompt: str) -> str:
141
+ async with AsyncCodex(TemporalCodexConfig(stream=self.events)) as codex:
142
+ thread = await codex.thread_start()
143
+ result = await thread.run(prompt)
144
+ return result.final_response or ""
145
+ ```
146
+
147
+ An external caller subscribes using the Workflow ID:
148
+
149
+ ```python
150
+ from temporalio.contrib.workflow_streams import WorkflowStreamClient
151
+
152
+ stream = WorkflowStreamClient.create(client, workflow_id)
153
+ async for item in stream.subscribe(["codex"], result_type=dict):
154
+ notification = item.data
155
+ print(notification["method"], notification["params"])
156
+ ```
157
+
158
+ Each item is a JSON-RPC delta notification with `method` and `params`, including
159
+ thread, turn, and item IDs when provided by Codex. Notifications whose method ends
160
+ in `delta` (case insensitive) are published once by the connection's reader,
161
+ including agent text, reasoning, and command output. They remain available through
162
+ upstream `turn.stream()` as well. Activity retries and multiple turn subscribers
163
+ do not duplicate these publications. Explicitly repeating a turn can generate
164
+ new output; consumers should use the IDs to associate it with the correct turn.
165
+ Streaming adds Workflow memory use; apply the Workflow Streams retention guidance
166
+ for long conversations.
167
+
168
+ ## Connectors and launch configuration
169
+
170
+ `OpenAICodexPlugin(connector=...)` accepts a `Connector` implementing
171
+ `async connect(ConnectActivityInput) -> CodexProcess`. The input contains the upstream
172
+ `CodexConfig`. `CodexProcess` provides `write(message)`, `read() -> list[dict]`, and
173
+ `close()`. A read returns an ordered batch or an empty list at EOF. Close must be
174
+ idempotent, tolerate overlap, support retries after cancellation, and unblock
175
+ pending reads and writes. A transient
176
+ write failure must guarantee that the message was not committed; report ambiguous
177
+ or permanent failures with `CodexError` so they are not blindly retried.
178
+
179
+ The default `SubprocessConnector` honors `codex_bin`, `launch_args_override`,
180
+ `config_overrides`, `cwd`, and `env`. It merges `env` into the Worker's environment.
181
+ Launch paths and runtime discovery are resolved on the Activity Worker. Custom
182
+ connectors can reach a remote process; the returned connection object still lives
183
+ on the Worker that created it. The connector owns cleanup of remote resources
184
+ that outlive the Worker. No Nexus service or tool-registration API is needed.
185
+
186
+ `CodexActivityOptions` overrides `connect`, `write`, `read`, and `close`, each using
187
+ the Temporal SDK's `workflow.ActivityConfig`. Only
188
+ `connect.task_queue` selects the queue where Codex starts. The other Activities use
189
+ the private queue polled by the Worker holding the connection. Activities default
190
+ to five-minute start-to-close timeouts and ten-second heartbeat timeouts. Operations
191
+ on an established connection also have a thirty-second schedule-to-start timeout
192
+ for detecting a lost Worker.
193
+
194
+ ## Recovery and operational limits
195
+
196
+ Completed writes stay cached for the life of the connection, so Activity retries
197
+ and lost completion results do not resend prompts, approvals, or notifications.
198
+ Read batches stay buffered until a later read acknowledges them. Heartbeats let
199
+ Temporal cancel blocked operations; context exit closes the process, and orderly
200
+ Worker shutdown closes its connections.
201
+ If a connect result is lost, its idle connection remains with the owning Worker
202
+ until shutdown. Cleanup failures do not replace a Workflow's result or original error.
203
+
204
+ A process connection is not durable storage. Losing its Worker produces
205
+ `TransportClosedError`; the plugin does not automatically start a replacement
206
+ session. Keep persisted thread IDs in Workflow state. Handle the error by creating
207
+ a new client and explicitly calling `thread_resume` with the saved ID, using a
208
+ Worker with access to the same Codex home and workspace. Ephemeral threads and
209
+ in-flight process state cannot be recovered this way. Retrying a Workflow or
210
+ resetting its history can create new sessions and repeat application side effects.
211
+
212
+ Configure credentials on Workers, such as an authenticated `CODEX_HOME` or an
213
+ appropriate model provider. Avoid passing secrets through Workflow inputs,
214
+ `TemporalCodexConfig.env`, or login API-key calls: Activity inputs/results and
215
+ notification content are recorded in Temporal history. Use an appropriate payload
216
+ codec if sensitive content must pass through history. Tests require no real credentials.
217
+
218
+ Each protocol write and output batch adds Activity events to Workflow history.
219
+ Long streams and long-lived sessions grow history; bound conversations and close
220
+ connections before continuing as new. Worker memory also holds unacknowledged
221
+ output and write-deduplication records until close. Reads batch near Temporal's
222
+ payload warning threshold; a single oversized message is not split and can exceed
223
+ the server's payload limit. Use upstream options to limit output and an external
224
+ storage/payload strategy where needed. The subprocess reader accepts lines up to
225
+ 16 MiB; the Temporal payload limit is usually smaller. Exceeding that line limit
226
+ or another terminal stdout read failure raises `TransportClosedError` without
227
+ retrying the failed reader.
@@ -0,0 +1,203 @@
1
+ # Temporal OpenAI Codex integration
2
+
3
+ **Pre-release.** Run the [Python Codex SDK](https://github.com/openai/codex/tree/main/sdk/python)
4
+ in Temporal Workflows. Install with
5
+ `uv add temporalio-openai-codex`. Import the integration as
6
+ `temporalio.openai_codex` and the upstream SDK as `openai_codex`.
7
+
8
+ Each SDK keeps its exact `openai-codex-cli-bin` dependency. The
9
+ default connector uses that bundled runtime and its companion PATH entries,
10
+ including when another Codex executable is installed on the Worker.
11
+
12
+ ## Use AsyncCodex
13
+
14
+ Register the plugin on the Worker (or its Client):
15
+
16
+ ```python
17
+ from temporalio.openai_codex import OpenAICodexPlugin
18
+ from temporalio.worker import Worker
19
+
20
+ worker = Worker(
21
+ client,
22
+ task_queue="codex",
23
+ workflows=[CodingWorkflow],
24
+ plugins=[OpenAICodexPlugin()],
25
+ )
26
+ ```
27
+
28
+ Use the upstream async API in the Workflow:
29
+
30
+ ```python
31
+ from openai_codex import AsyncCodex, Sandbox
32
+ from temporalio import workflow
33
+ from temporalio.openai_codex import TemporalCodexConfig
34
+
35
+
36
+ @workflow.defn
37
+ class CodingWorkflow:
38
+ @workflow.run
39
+ async def run(self, prompt: str) -> str:
40
+ async with AsyncCodex(TemporalCodexConfig(cwd="/workspace")) as codex:
41
+ thread = await codex.thread_start(sandbox=Sandbox.read_only)
42
+ result = await thread.run(prompt)
43
+ return result.final_response or ""
44
+ ```
45
+
46
+ The plugin configures passthrough imports for `openai_codex` and
47
+ `temporalio.openai_codex`, so Workflow code can import both directly.
48
+
49
+ The plugin adapts construction and asynchronous turn streams inside Workflows,
50
+ including replay. Threads, turn handles, login handles, notifications, and
51
+ `TurnResult` are the upstream types. Ordinary SDK calls outside Workflows keep
52
+ using upstream implementations. Synchronous `Codex` is for Activity or application
53
+ code, not Workflow code.
54
+
55
+ In Workflows, collected failed turns raise `CodexError` with the upstream error
56
+ message, so an uncaught provider failure fails the Workflow. Streaming still yields
57
+ the upstream turn-completion notification, including its failure status and error.
58
+
59
+ ## Approvals and Activity options
60
+
61
+ Pass `TemporalCodexConfig`, a subclass of the upstream `CodexConfig`, to
62
+ `AsyncCodex` for Workflow-specific options:
63
+
64
+ ```python
65
+ from datetime import timedelta
66
+ from openai_codex import AsyncCodex
67
+ from temporalio.openai_codex import CodexActivityOptions, TemporalCodexConfig
68
+ from temporalio.workflow import ActivityConfig
69
+
70
+ async def approve(method, params):
71
+ # Runs in Workflow code. It can wait for a Signal or Update, or call an Activity.
72
+ return {"decision": "accept"}
73
+
74
+ async with AsyncCodex(TemporalCodexConfig(
75
+ cwd="/workspace",
76
+ approval_handler=approve,
77
+ activity_options=CodexActivityOptions(
78
+ connect=ActivityConfig(start_to_close_timeout=timedelta(minutes=2)),
79
+ ),
80
+ )) as codex:
81
+ thread = await codex.thread_start()
82
+ result = await thread.run("Describe the project.")
83
+ ```
84
+
85
+ Synchronous and asynchronous approval handlers receive `(method, params)` as in
86
+ upstream `CodexClient`. Keep callbacks deterministic; put I/O in Activities. Without
87
+ a handler, command and file-change approval requests receive `{"decision": "accept"}`
88
+ and other server requests receive `{}`, matching the SDK default. Set Codex's
89
+ approval and sandbox policies deliberately for your application. These additional
90
+ options apply inside Workflows; ordinary SDK calls use upstream behavior.
91
+
92
+ Only the base `CodexConfig` fields are passed to the connect Activity. Approval
93
+ callbacks, Activity options, and stream topic handles stay in Workflow code.
94
+
95
+ ## Token-by-Token Output
96
+
97
+ Use the optional `stream` topic to expose generated output to callers outside the
98
+ Workflow. [Workflow Streams](https://docs.temporal.io/develop/python/workflows/workflow-streams)
99
+ is currently in Public Preview. Create the stream in `@workflow.init`, then pass
100
+ its topic in `TemporalCodexConfig`:
101
+
102
+ ```python
103
+ from openai_codex import AsyncCodex
104
+ from temporalio import workflow
105
+ from temporalio.contrib.workflow_streams import WorkflowStream
106
+ from temporalio.openai_codex import TemporalCodexConfig
107
+
108
+
109
+ @workflow.defn
110
+ class StreamingWorkflow:
111
+ @workflow.init
112
+ def __init__(self, prompt: str) -> None:
113
+ self.events = WorkflowStream().topic("codex")
114
+
115
+ @workflow.run
116
+ async def run(self, prompt: str) -> str:
117
+ async with AsyncCodex(TemporalCodexConfig(stream=self.events)) as codex:
118
+ thread = await codex.thread_start()
119
+ result = await thread.run(prompt)
120
+ return result.final_response or ""
121
+ ```
122
+
123
+ An external caller subscribes using the Workflow ID:
124
+
125
+ ```python
126
+ from temporalio.contrib.workflow_streams import WorkflowStreamClient
127
+
128
+ stream = WorkflowStreamClient.create(client, workflow_id)
129
+ async for item in stream.subscribe(["codex"], result_type=dict):
130
+ notification = item.data
131
+ print(notification["method"], notification["params"])
132
+ ```
133
+
134
+ Each item is a JSON-RPC delta notification with `method` and `params`, including
135
+ thread, turn, and item IDs when provided by Codex. Notifications whose method ends
136
+ in `delta` (case insensitive) are published once by the connection's reader,
137
+ including agent text, reasoning, and command output. They remain available through
138
+ upstream `turn.stream()` as well. Activity retries and multiple turn subscribers
139
+ do not duplicate these publications. Explicitly repeating a turn can generate
140
+ new output; consumers should use the IDs to associate it with the correct turn.
141
+ Streaming adds Workflow memory use; apply the Workflow Streams retention guidance
142
+ for long conversations.
143
+
144
+ ## Connectors and launch configuration
145
+
146
+ `OpenAICodexPlugin(connector=...)` accepts a `Connector` implementing
147
+ `async connect(ConnectActivityInput) -> CodexProcess`. The input contains the upstream
148
+ `CodexConfig`. `CodexProcess` provides `write(message)`, `read() -> list[dict]`, and
149
+ `close()`. A read returns an ordered batch or an empty list at EOF. Close must be
150
+ idempotent, tolerate overlap, support retries after cancellation, and unblock
151
+ pending reads and writes. A transient
152
+ write failure must guarantee that the message was not committed; report ambiguous
153
+ or permanent failures with `CodexError` so they are not blindly retried.
154
+
155
+ The default `SubprocessConnector` honors `codex_bin`, `launch_args_override`,
156
+ `config_overrides`, `cwd`, and `env`. It merges `env` into the Worker's environment.
157
+ Launch paths and runtime discovery are resolved on the Activity Worker. Custom
158
+ connectors can reach a remote process; the returned connection object still lives
159
+ on the Worker that created it. The connector owns cleanup of remote resources
160
+ that outlive the Worker. No Nexus service or tool-registration API is needed.
161
+
162
+ `CodexActivityOptions` overrides `connect`, `write`, `read`, and `close`, each using
163
+ the Temporal SDK's `workflow.ActivityConfig`. Only
164
+ `connect.task_queue` selects the queue where Codex starts. The other Activities use
165
+ the private queue polled by the Worker holding the connection. Activities default
166
+ to five-minute start-to-close timeouts and ten-second heartbeat timeouts. Operations
167
+ on an established connection also have a thirty-second schedule-to-start timeout
168
+ for detecting a lost Worker.
169
+
170
+ ## Recovery and operational limits
171
+
172
+ Completed writes stay cached for the life of the connection, so Activity retries
173
+ and lost completion results do not resend prompts, approvals, or notifications.
174
+ Read batches stay buffered until a later read acknowledges them. Heartbeats let
175
+ Temporal cancel blocked operations; context exit closes the process, and orderly
176
+ Worker shutdown closes its connections.
177
+ If a connect result is lost, its idle connection remains with the owning Worker
178
+ until shutdown. Cleanup failures do not replace a Workflow's result or original error.
179
+
180
+ A process connection is not durable storage. Losing its Worker produces
181
+ `TransportClosedError`; the plugin does not automatically start a replacement
182
+ session. Keep persisted thread IDs in Workflow state. Handle the error by creating
183
+ a new client and explicitly calling `thread_resume` with the saved ID, using a
184
+ Worker with access to the same Codex home and workspace. Ephemeral threads and
185
+ in-flight process state cannot be recovered this way. Retrying a Workflow or
186
+ resetting its history can create new sessions and repeat application side effects.
187
+
188
+ Configure credentials on Workers, such as an authenticated `CODEX_HOME` or an
189
+ appropriate model provider. Avoid passing secrets through Workflow inputs,
190
+ `TemporalCodexConfig.env`, or login API-key calls: Activity inputs/results and
191
+ notification content are recorded in Temporal history. Use an appropriate payload
192
+ codec if sensitive content must pass through history. Tests require no real credentials.
193
+
194
+ Each protocol write and output batch adds Activity events to Workflow history.
195
+ Long streams and long-lived sessions grow history; bound conversations and close
196
+ connections before continuing as new. Worker memory also holds unacknowledged
197
+ output and write-deduplication records until close. Reads batch near Temporal's
198
+ payload warning threshold; a single oversized message is not split and can exceed
199
+ the server's payload limit. Use upstream options to limit output and an external
200
+ storage/payload strategy where needed. The subprocess reader accepts lines up to
201
+ 16 MiB; the Temporal payload limit is usually smaller. Exceeding that line limit
202
+ or another terminal stdout read failure raises `TransportClosedError` without
203
+ retrying the failed reader.
@@ -0,0 +1,124 @@
1
+ [project]
2
+ name = "temporalio-openai-codex"
3
+ version = "0.0.1"
4
+ description = "Durable OpenAI Codex sessions on Temporal"
5
+ requires-python = ">=3.10"
6
+ readme = "README.md"
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ classifiers = [
10
+ "Development Status :: 3 - Alpha",
11
+ "Programming Language :: Python :: 3.10",
12
+ "Programming Language :: Python :: 3.11",
13
+ "Programming Language :: Python :: 3.12",
14
+ "Programming Language :: Python :: 3.13",
15
+ "Programming Language :: Python :: 3.14",
16
+ "Typing :: Typed",
17
+ ]
18
+ dependencies = [
19
+ "temporalio>=1.34.0,<2",
20
+ "openai-codex>=0.161.0,<0.164.0",
21
+ "pydantic>=2.12,<3",
22
+ ]
23
+
24
+ [[project.authors]]
25
+ name = "Temporal Technologies Inc"
26
+ email = "sdk@temporal.io"
27
+
28
+ [project.optional-dependencies]
29
+
30
+ [project.urls]
31
+ Homepage = "https://github.com/temporalio/ai-integrations/tree/main/python/openai_codex"
32
+ Repository = "https://github.com/temporalio/ai-integrations"
33
+ Documentation = "https://docs.temporal.io/develop/python/integrations/openai-codex"
34
+
35
+ [build-system]
36
+ requires = ["uv_build>=0.12.5,<0.13"]
37
+ build-backend = "uv_build"
38
+
39
+ [tool.uv]
40
+ default-groups = ["dev"]
41
+ required-version = ">=0.12.5,<0.13"
42
+ exclude-newer = "2 weeks"
43
+ link-mode = "copy"
44
+
45
+ [tool.uv.build-backend]
46
+ module-name = "temporalio.openai_codex"
47
+
48
+ [tool.uv.exclude-newer-package]
49
+ temporalio = false
50
+
51
+ [[tool.uv.cache-keys]]
52
+ file = "pyproject.toml"
53
+
54
+ [[tool.uv.cache-keys]]
55
+ file = "README.md"
56
+
57
+ [[tool.uv.cache-keys]]
58
+ file = "src/**/*"
59
+
60
+ [tool.pytest.ini_options]
61
+ asyncio_mode = "auto"
62
+ log_level = "DEBUG"
63
+ log_format = "%(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s)"
64
+ testpaths = ["tests"]
65
+ timeout = 60
66
+ timeout_func_only = true
67
+ filterwarnings = [
68
+ "error::temporalio.workflow.UnfinishedUpdateHandlersWarning",
69
+ "error::temporalio.workflow.UnfinishedSignalHandlersWarning",
70
+ "ignore::pytest.PytestDeprecationWarning",
71
+ "ignore::DeprecationWarning",
72
+ ]
73
+
74
+ [tool.mypy]
75
+ files = [
76
+ "src",
77
+ "tests",
78
+ ]
79
+ mypy_path = "src"
80
+ namespace_packages = true
81
+ explicit_package_bases = true
82
+ check_untyped_defs = true
83
+ ignore_missing_imports = true
84
+
85
+ [tool.pyright]
86
+ extends = "../_shared/pyrightconfig.json"
87
+ include = [
88
+ "src",
89
+ "tests",
90
+ ]
91
+
92
+ [[tool.pyright.executionEnvironments]]
93
+ root = "."
94
+ extraPaths = ["src"]
95
+
96
+ [tool.pydocstyle]
97
+ convention = "google"
98
+ add_ignore = [
99
+ "D205",
100
+ "D415",
101
+ ]
102
+
103
+ [tool.ruff]
104
+ extend = "../_shared/ruff.toml"
105
+ src = [
106
+ ".",
107
+ "src",
108
+ ]
109
+
110
+ [dependency-groups]
111
+ dev = [
112
+ "pyright>=1.1.414,<2",
113
+ "basedpyright>=1.40.2,<2",
114
+ "mypy>=1.20.2,<2",
115
+ "ruff>=0.15.12,<0.16",
116
+ "pydocstyle>=6.3,<7",
117
+ "pytest~=9.0",
118
+ "pytest-asyncio>=0.21.2,<0.22",
119
+ "pytest-timeout~=2.2",
120
+ "pytest-xdist>=3.6,<4",
121
+ "pytest-pretty>=1.3",
122
+ "pytest-rerunfailures>=16.1",
123
+ "tomli>=2.0; python_version < '3.11'",
124
+ ]
@@ -0,0 +1,108 @@
1
+ [project]
2
+ name = "temporalio-openai-codex"
3
+ version = "0.0.1" # Development placeholder; protected release tags inject the published version in CI.
4
+ description = "Durable OpenAI Codex sessions on Temporal"
5
+ authors = [{ name = "Temporal Technologies Inc", email = "sdk@temporal.io" }]
6
+ requires-python = ">=3.10"
7
+ readme = "README.md"
8
+ license = "MIT"
9
+ license-files = ["LICENSE"] # committed copy of the repository's root LICENSE
10
+ classifiers = [
11
+ "Development Status :: 3 - Alpha", # maturity; must agree with plugin.toml (see AGENTS.md for the mapping)
12
+ "Programming Language :: Python :: 3.10",
13
+ "Programming Language :: Python :: 3.11",
14
+ "Programming Language :: Python :: 3.12",
15
+ "Programming Language :: Python :: 3.13",
16
+ "Programming Language :: Python :: 3.14",
17
+ "Typing :: Typed",
18
+ ]
19
+ dependencies = [
20
+ "temporalio>=1.34.0,<2",
21
+ "openai-codex>=0.161.0,<0.164.0",
22
+ "pydantic>=2.12,<3",
23
+ ]
24
+
25
+ [project.optional-dependencies]
26
+
27
+ [project.urls]
28
+ Homepage = "https://github.com/temporalio/ai-integrations/tree/main/python/openai_codex"
29
+ Repository = "https://github.com/temporalio/ai-integrations"
30
+ Documentation = "https://docs.temporal.io/develop/python/integrations/openai-codex"
31
+
32
+ [build-system]
33
+ requires = ["uv_build>=0.12.5,<0.13"]
34
+ build-backend = "uv_build"
35
+
36
+ [tool.uv.build-backend]
37
+ # src/temporalio and src/temporalio/contrib must NOT contain __init__.py: the SDK wheel owns them.
38
+ module-name = "temporalio.openai_codex"
39
+
40
+ [dependency-groups]
41
+ dev = [
42
+ "pyright>=1.1.414,<2",
43
+ "basedpyright>=1.40.2,<2",
44
+ "mypy>=1.20.2,<2",
45
+ "ruff>=0.15.12,<0.16",
46
+ "pydocstyle>=6.3,<7",
47
+ "pytest~=9.0",
48
+ "pytest-asyncio>=0.21.2,<0.22",
49
+ "pytest-timeout~=2.2",
50
+ "pytest-xdist>=3.6,<4",
51
+ "pytest-pretty>=1.3",
52
+ "pytest-rerunfailures>=16.1",
53
+ "tomli>=2.0; python_version < '3.11'",
54
+ ]
55
+
56
+ [tool.uv]
57
+ default-groups = ["dev"]
58
+ required-version = ">=0.12.5,<0.13" # same value in scripts/pyproject.toml; CI reads it via setup-uv version-file
59
+ exclude-newer = "2 weeks" # org supply-chain cooldown for third-party packages
60
+ exclude-newer-package = { temporalio = false } # the SDK is exempt: releases must be adoptable on day one
61
+ link-mode = "copy"
62
+ cache-keys = [{ file = "pyproject.toml" }, { file = "README.md" }, { file = "src/**/*" }]
63
+
64
+ [tool.pytest.ini_options]
65
+ asyncio_mode = "auto"
66
+ # Do not use log_cli since this shows logging for all tests, not just the ones
67
+ # that failed. Instead, show all logs for failed tests at the end.
68
+ log_level = "DEBUG"
69
+ log_format = "%(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s)"
70
+ testpaths = ["tests"]
71
+ timeout = 60
72
+ timeout_func_only = true
73
+ filterwarnings = [
74
+ "error::temporalio.workflow.UnfinishedUpdateHandlersWarning",
75
+ "error::temporalio.workflow.UnfinishedSignalHandlersWarning",
76
+ "ignore::pytest.PytestDeprecationWarning",
77
+ "ignore::DeprecationWarning",
78
+ ]
79
+
80
+ [tool.mypy]
81
+ files = ["src", "tests"]
82
+ mypy_path = "src"
83
+ namespace_packages = true
84
+ explicit_package_bases = true
85
+ check_untyped_defs = true
86
+ ignore_missing_imports = true
87
+
88
+ [tool.pyright]
89
+ extends = "../_shared/pyrightconfig.json"
90
+ include = ["src", "tests"]
91
+
92
+ # Resolve the plugin's own modules from source rather than from site-packages.
93
+ [[tool.pyright.executionEnvironments]]
94
+ root = "."
95
+ extraPaths = ["src"]
96
+
97
+ [tool.pydocstyle]
98
+ convention = "google"
99
+ add_ignore = [
100
+ # We like to wrap at a certain number of chars, even long summary sentences.
101
+ # https://github.com/PyCQA/pydocstyle/issues/184
102
+ "D205",
103
+ "D415",
104
+ ]
105
+
106
+ [tool.ruff]
107
+ extend = "../_shared/ruff.toml"
108
+ src = [".", "src"]