codexapi 0.12.9__tar.gz → 0.12.10__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.
- {codexapi-0.12.9/src/codexapi.egg-info → codexapi-0.12.10}/PKG-INFO +9 -3
- {codexapi-0.12.9 → codexapi-0.12.10}/README.md +8 -2
- {codexapi-0.12.9 → codexapi-0.12.10}/pyproject.toml +1 -1
- {codexapi-0.12.9 → codexapi-0.12.10}/src/codexapi/__init__.py +3 -2
- {codexapi-0.12.9 → codexapi-0.12.10}/src/codexapi/agent.py +143 -10
- {codexapi-0.12.9 → codexapi-0.12.10}/src/codexapi/async_agent.py +13 -10
- {codexapi-0.12.9 → codexapi-0.12.10/src/codexapi.egg-info}/PKG-INFO +9 -3
- {codexapi-0.12.9 → codexapi-0.12.10}/tests/test_agent_backend.py +32 -2
- {codexapi-0.12.9 → codexapi-0.12.10}/LICENSE +0 -0
- {codexapi-0.12.9 → codexapi-0.12.10}/setup.cfg +0 -0
- {codexapi-0.12.9 → codexapi-0.12.10}/src/codexapi/__main__.py +0 -0
- {codexapi-0.12.9 → codexapi-0.12.10}/src/codexapi/agents.py +0 -0
- {codexapi-0.12.9 → codexapi-0.12.10}/src/codexapi/cli.py +0 -0
- {codexapi-0.12.9 → codexapi-0.12.10}/src/codexapi/foreach.py +0 -0
- {codexapi-0.12.9 → codexapi-0.12.10}/src/codexapi/gh_integration.py +0 -0
- {codexapi-0.12.9 → codexapi-0.12.10}/src/codexapi/lead.py +0 -0
- {codexapi-0.12.9 → codexapi-0.12.10}/src/codexapi/pushover.py +0 -0
- {codexapi-0.12.9 → codexapi-0.12.10}/src/codexapi/ralph.py +0 -0
- {codexapi-0.12.9 → codexapi-0.12.10}/src/codexapi/rate_limits.py +0 -0
- {codexapi-0.12.9 → codexapi-0.12.10}/src/codexapi/science.py +0 -0
- {codexapi-0.12.9 → codexapi-0.12.10}/src/codexapi/task.py +0 -0
- {codexapi-0.12.9 → codexapi-0.12.10}/src/codexapi/taskfile.py +0 -0
- {codexapi-0.12.9 → codexapi-0.12.10}/src/codexapi/welfare.py +0 -0
- {codexapi-0.12.9 → codexapi-0.12.10}/src/codexapi.egg-info/SOURCES.txt +0 -0
- {codexapi-0.12.9 → codexapi-0.12.10}/src/codexapi.egg-info/dependency_links.txt +0 -0
- {codexapi-0.12.9 → codexapi-0.12.10}/src/codexapi.egg-info/entry_points.txt +0 -0
- {codexapi-0.12.9 → codexapi-0.12.10}/src/codexapi.egg-info/requires.txt +0 -0
- {codexapi-0.12.9 → codexapi-0.12.10}/src/codexapi.egg-info/top_level.txt +0 -0
- {codexapi-0.12.9 → codexapi-0.12.10}/tests/test_agents.py +0 -0
- {codexapi-0.12.9 → codexapi-0.12.10}/tests/test_async_agent.py +0 -0
- {codexapi-0.12.9 → codexapi-0.12.10}/tests/test_rate_limits.py +0 -0
- {codexapi-0.12.9 → codexapi-0.12.10}/tests/test_science.py +0 -0
- {codexapi-0.12.9 → codexapi-0.12.10}/tests/test_task_progress.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: codexapi
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.10
|
|
4
4
|
Summary: Minimal Python API for running the Codex CLI.
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: codex,agent,cli,openai
|
|
@@ -80,6 +80,8 @@ Use `backend="cursor"` (or set `CODEXAPI_BACKEND=cursor`) to switch to the
|
|
|
80
80
|
Cursor agent backend.
|
|
81
81
|
Use `fast=True` in Codex API calls, or `--fast` in the CLI, to opt into Codex
|
|
82
82
|
fast mode. Normal mode is the default.
|
|
83
|
+
Use `model="..."` and `thinking="..."` in Codex API calls to override the
|
|
84
|
+
backend model and reasoning effort for a run.
|
|
83
85
|
|
|
84
86
|
## CLI
|
|
85
87
|
|
|
@@ -336,7 +338,7 @@ codexapi foreach list.txt task.yaml --retry-all
|
|
|
336
338
|
|
|
337
339
|
## API
|
|
338
340
|
|
|
339
|
-
### `agent(prompt, cwd=None, yolo=True, flags=None, include_thinking=False, backend=None, fast=False) -> str`
|
|
341
|
+
### `agent(prompt, cwd=None, yolo=True, flags=None, include_thinking=False, backend=None, fast=False, model=None, thinking=None) -> str`
|
|
340
342
|
|
|
341
343
|
Runs a single agent turn and returns only the agent's message. Any reasoning
|
|
342
344
|
items are filtered out.
|
|
@@ -348,8 +350,10 @@ items are filtered out.
|
|
|
348
350
|
- `include_thinking` (bool): when true, return all agent messages joined.
|
|
349
351
|
- `backend` (str | None): `codex` or `cursor` (defaults to `CODEXAPI_BACKEND` or `codex`).
|
|
350
352
|
- `fast` (bool): enable Codex fast mode (defaults to normal mode).
|
|
353
|
+
- `model` (str | None): backend model override. Codex maps this to `-c model=...`; Cursor maps it to `--model ...`.
|
|
354
|
+
- `thinking` (str | None): Codex reasoning effort override, mapped to `-c model_reasoning_effort=...`.
|
|
351
355
|
|
|
352
|
-
### `Agent(cwd=None, yolo=True, thread_id=None, flags=None, welfare=False, include_thinking=False, backend=None, fast=False)`
|
|
356
|
+
### `Agent(cwd=None, yolo=True, thread_id=None, flags=None, welfare=False, include_thinking=False, backend=None, fast=False, model=None, thinking=None)`
|
|
353
357
|
|
|
354
358
|
Creates a stateful session wrapper. Calling the instance sends the prompt into
|
|
355
359
|
the same conversation and returns only the agent's message.
|
|
@@ -363,6 +367,8 @@ the same conversation and returns only the agent's message.
|
|
|
363
367
|
- `include_thinking` (bool): when true, return all agent messages joined.
|
|
364
368
|
- `backend` (str | None): `codex` or `cursor` (defaults to `CODEXAPI_BACKEND` or `codex`).
|
|
365
369
|
- `fast` (bool): enable Codex fast mode (defaults to normal mode).
|
|
370
|
+
- `model` (str | None): backend model override.
|
|
371
|
+
- `thinking` (str | None): Codex reasoning effort override.
|
|
366
372
|
For Cursor, `thread_id` corresponds to the `session_id` returned by the agent.
|
|
367
373
|
|
|
368
374
|
### `lead(minutes, prompt, cwd=None, yolo=True, flags=None, leadbook=None, backend=None, fast=False) -> dict`
|
|
@@ -65,6 +65,8 @@ Use `backend="cursor"` (or set `CODEXAPI_BACKEND=cursor`) to switch to the
|
|
|
65
65
|
Cursor agent backend.
|
|
66
66
|
Use `fast=True` in Codex API calls, or `--fast` in the CLI, to opt into Codex
|
|
67
67
|
fast mode. Normal mode is the default.
|
|
68
|
+
Use `model="..."` and `thinking="..."` in Codex API calls to override the
|
|
69
|
+
backend model and reasoning effort for a run.
|
|
68
70
|
|
|
69
71
|
## CLI
|
|
70
72
|
|
|
@@ -321,7 +323,7 @@ codexapi foreach list.txt task.yaml --retry-all
|
|
|
321
323
|
|
|
322
324
|
## API
|
|
323
325
|
|
|
324
|
-
### `agent(prompt, cwd=None, yolo=True, flags=None, include_thinking=False, backend=None, fast=False) -> str`
|
|
326
|
+
### `agent(prompt, cwd=None, yolo=True, flags=None, include_thinking=False, backend=None, fast=False, model=None, thinking=None) -> str`
|
|
325
327
|
|
|
326
328
|
Runs a single agent turn and returns only the agent's message. Any reasoning
|
|
327
329
|
items are filtered out.
|
|
@@ -333,8 +335,10 @@ items are filtered out.
|
|
|
333
335
|
- `include_thinking` (bool): when true, return all agent messages joined.
|
|
334
336
|
- `backend` (str | None): `codex` or `cursor` (defaults to `CODEXAPI_BACKEND` or `codex`).
|
|
335
337
|
- `fast` (bool): enable Codex fast mode (defaults to normal mode).
|
|
338
|
+
- `model` (str | None): backend model override. Codex maps this to `-c model=...`; Cursor maps it to `--model ...`.
|
|
339
|
+
- `thinking` (str | None): Codex reasoning effort override, mapped to `-c model_reasoning_effort=...`.
|
|
336
340
|
|
|
337
|
-
### `Agent(cwd=None, yolo=True, thread_id=None, flags=None, welfare=False, include_thinking=False, backend=None, fast=False)`
|
|
341
|
+
### `Agent(cwd=None, yolo=True, thread_id=None, flags=None, welfare=False, include_thinking=False, backend=None, fast=False, model=None, thinking=None)`
|
|
338
342
|
|
|
339
343
|
Creates a stateful session wrapper. Calling the instance sends the prompt into
|
|
340
344
|
the same conversation and returns only the agent's message.
|
|
@@ -348,6 +352,8 @@ the same conversation and returns only the agent's message.
|
|
|
348
352
|
- `include_thinking` (bool): when true, return all agent messages joined.
|
|
349
353
|
- `backend` (str | None): `codex` or `cursor` (defaults to `CODEXAPI_BACKEND` or `codex`).
|
|
350
354
|
- `fast` (bool): enable Codex fast mode (defaults to normal mode).
|
|
355
|
+
- `model` (str | None): backend model override.
|
|
356
|
+
- `thinking` (str | None): Codex reasoning effort override.
|
|
351
357
|
For Cursor, `thread_id` corresponds to the `session_id` returned by the agent.
|
|
352
358
|
|
|
353
359
|
### `lead(minutes, prompt, cwd=None, yolo=True, flags=None, leadbook=None, backend=None, fast=False) -> dict`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Minimal Python API for running agent CLIs."""
|
|
2
2
|
|
|
3
|
-
from .agent import Agent, WelfareStop, agent
|
|
3
|
+
from .agent import Agent, WelfareStop, agent, build_agent_flags
|
|
4
4
|
from .async_agent import AsyncAgent
|
|
5
5
|
from .foreach import ForeachResult, foreach
|
|
6
6
|
from .pushover import Pushover
|
|
@@ -15,6 +15,7 @@ __all__ = [
|
|
|
15
15
|
"AsyncAgent",
|
|
16
16
|
"ForeachResult",
|
|
17
17
|
"Pushover",
|
|
18
|
+
"build_agent_flags",
|
|
18
19
|
"quota_line",
|
|
19
20
|
"rate_limits",
|
|
20
21
|
"Ralph",
|
|
@@ -29,4 +30,4 @@ __all__ = [
|
|
|
29
30
|
"task_result",
|
|
30
31
|
"lead",
|
|
31
32
|
]
|
|
32
|
-
__version__ = "0.12.
|
|
33
|
+
__version__ = "0.12.10"
|
|
@@ -11,6 +11,7 @@ from . import welfare
|
|
|
11
11
|
_CODEX_BIN = os.environ.get("CODEX_BIN", "codex")
|
|
12
12
|
_CURSOR_BIN = os.environ.get("CURSOR_BIN", "cursor")
|
|
13
13
|
_SUPPORTED_BACKENDS = {"codex", "cursor"}
|
|
14
|
+
_CURSOR_AGENT_BIN = os.path.expanduser("~/.local/bin/cursor-agent")
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
def _resolve_backend(backend):
|
|
@@ -33,12 +34,15 @@ def _ensure_backend_available(backend, env=None):
|
|
|
33
34
|
env_var = "CODEX_BIN"
|
|
34
35
|
label = "Codex CLI"
|
|
35
36
|
else:
|
|
36
|
-
command =
|
|
37
|
+
command = _cursor_bin(env)
|
|
37
38
|
env_var = "CURSOR_BIN"
|
|
38
39
|
label = "Cursor agent CLI"
|
|
39
40
|
merged = _merged_env(env)
|
|
40
41
|
path_value = None if merged is None else merged.get("PATH")
|
|
41
|
-
|
|
42
|
+
if os.path.isabs(command):
|
|
43
|
+
resolved = command if os.path.exists(command) else None
|
|
44
|
+
else:
|
|
45
|
+
resolved = shutil.which(command, path=path_value)
|
|
42
46
|
if resolved:
|
|
43
47
|
return resolved
|
|
44
48
|
raise RuntimeError(
|
|
@@ -55,6 +59,8 @@ def agent(
|
|
|
55
59
|
backend=None,
|
|
56
60
|
env=None,
|
|
57
61
|
fast=False,
|
|
62
|
+
model=None,
|
|
63
|
+
thinking=None,
|
|
58
64
|
):
|
|
59
65
|
"""Run a single agent turn and return only the agent's message.
|
|
60
66
|
|
|
@@ -67,12 +73,24 @@ def agent(
|
|
|
67
73
|
backend: Agent backend to use ("codex" or "cursor").
|
|
68
74
|
env: Optional environment variables for the backend subprocess.
|
|
69
75
|
fast: Enable Codex fast mode. Defaults to normal mode.
|
|
76
|
+
model: Optional backend model override.
|
|
77
|
+
thinking: Optional backend reasoning/thinking effort override.
|
|
70
78
|
|
|
71
79
|
Returns:
|
|
72
80
|
The agent's visible response text with reasoning traces removed.
|
|
73
81
|
"""
|
|
74
82
|
message, _thread_id, _usage = _run_agent(
|
|
75
|
-
prompt,
|
|
83
|
+
prompt,
|
|
84
|
+
cwd,
|
|
85
|
+
None,
|
|
86
|
+
yolo,
|
|
87
|
+
flags,
|
|
88
|
+
include_thinking,
|
|
89
|
+
backend,
|
|
90
|
+
env,
|
|
91
|
+
fast,
|
|
92
|
+
model,
|
|
93
|
+
thinking,
|
|
76
94
|
)
|
|
77
95
|
return message
|
|
78
96
|
|
|
@@ -106,6 +124,8 @@ class Agent:
|
|
|
106
124
|
backend=None,
|
|
107
125
|
env=None,
|
|
108
126
|
fast=False,
|
|
127
|
+
model=None,
|
|
128
|
+
thinking=None,
|
|
109
129
|
):
|
|
110
130
|
"""Create a new session wrapper.
|
|
111
131
|
|
|
@@ -120,6 +140,8 @@ class Agent:
|
|
|
120
140
|
backend: Agent backend to use ("codex" or "cursor").
|
|
121
141
|
env: Optional environment variables for the backend subprocess.
|
|
122
142
|
fast: Enable Codex fast mode. Defaults to normal mode.
|
|
143
|
+
model: Optional backend model override.
|
|
144
|
+
thinking: Optional backend reasoning/thinking effort override.
|
|
123
145
|
"""
|
|
124
146
|
self.cwd = cwd
|
|
125
147
|
self._yolo = yolo
|
|
@@ -130,6 +152,8 @@ class Agent:
|
|
|
130
152
|
self._backend = backend
|
|
131
153
|
self._env = env
|
|
132
154
|
self._fast = fast
|
|
155
|
+
self._model = model
|
|
156
|
+
self._thinking = thinking
|
|
133
157
|
self.last_usage = {}
|
|
134
158
|
|
|
135
159
|
def __call__(self, prompt):
|
|
@@ -146,6 +170,8 @@ class Agent:
|
|
|
146
170
|
self._backend,
|
|
147
171
|
self._env,
|
|
148
172
|
self._fast,
|
|
173
|
+
self._model,
|
|
174
|
+
self._thinking,
|
|
149
175
|
)
|
|
150
176
|
if thread_id:
|
|
151
177
|
self.thread_id = thread_id
|
|
@@ -165,15 +191,49 @@ def _run_agent(
|
|
|
165
191
|
backend,
|
|
166
192
|
env,
|
|
167
193
|
fast=False,
|
|
194
|
+
model=None,
|
|
195
|
+
thinking=None,
|
|
168
196
|
):
|
|
169
197
|
backend = _resolve_backend(backend)
|
|
170
198
|
_ensure_backend_available(backend, env)
|
|
171
199
|
if backend == "codex":
|
|
172
|
-
return _run_codex(
|
|
173
|
-
|
|
200
|
+
return _run_codex(
|
|
201
|
+
prompt,
|
|
202
|
+
cwd,
|
|
203
|
+
thread_id,
|
|
204
|
+
yolo,
|
|
205
|
+
flags,
|
|
206
|
+
include_thinking,
|
|
207
|
+
env,
|
|
208
|
+
fast,
|
|
209
|
+
model,
|
|
210
|
+
thinking,
|
|
211
|
+
)
|
|
212
|
+
return _run_cursor(
|
|
213
|
+
prompt,
|
|
214
|
+
cwd,
|
|
215
|
+
thread_id,
|
|
216
|
+
yolo,
|
|
217
|
+
flags,
|
|
218
|
+
include_thinking,
|
|
219
|
+
env,
|
|
220
|
+
model,
|
|
221
|
+
thinking,
|
|
222
|
+
)
|
|
174
223
|
|
|
175
224
|
|
|
176
|
-
def _run_codex(
|
|
225
|
+
def _run_codex(
|
|
226
|
+
prompt,
|
|
227
|
+
cwd,
|
|
228
|
+
thread_id,
|
|
229
|
+
yolo,
|
|
230
|
+
flags,
|
|
231
|
+
include_thinking,
|
|
232
|
+
env,
|
|
233
|
+
fast=False,
|
|
234
|
+
model=None,
|
|
235
|
+
thinking=None,
|
|
236
|
+
):
|
|
177
237
|
"""Invoke the Codex CLI and return the message plus thread id (if any)."""
|
|
178
238
|
command = [
|
|
179
239
|
_CODEX_BIN,
|
|
@@ -188,6 +248,7 @@ def _run_codex(prompt, cwd, thread_id, yolo, flags, include_thinking, env, fast=
|
|
|
188
248
|
else:
|
|
189
249
|
command.append("--full-auto")
|
|
190
250
|
command.extend(_codex_fast_config(fast))
|
|
251
|
+
command.extend(_agent_config_flag_parts("codex", model, thinking))
|
|
191
252
|
if flags:
|
|
192
253
|
command.extend(shlex.split(flags))
|
|
193
254
|
if cwd:
|
|
@@ -227,11 +288,82 @@ def _codex_fast_config(fast):
|
|
|
227
288
|
return ["-c", "features.fast_mode=false"]
|
|
228
289
|
|
|
229
290
|
|
|
230
|
-
def
|
|
291
|
+
def _cursor_bin(env=None):
|
|
292
|
+
merged = _merged_env(env)
|
|
293
|
+
env_source = merged or os.environ
|
|
294
|
+
override = env_source.get("CURSOR_BIN", "").strip()
|
|
295
|
+
if override:
|
|
296
|
+
return os.path.expanduser(override)
|
|
297
|
+
|
|
298
|
+
path_value = None if merged is None else merged.get("PATH")
|
|
299
|
+
direct = shutil.which("cursor-agent", path=path_value)
|
|
300
|
+
if direct:
|
|
301
|
+
return direct
|
|
302
|
+
if os.path.exists(_CURSOR_AGENT_BIN):
|
|
303
|
+
return _CURSOR_AGENT_BIN
|
|
304
|
+
return _CURSOR_BIN
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def _cursor_command_prefix(env=None):
|
|
308
|
+
command = _cursor_bin(env)
|
|
309
|
+
if os.path.basename(command) == "cursor-agent":
|
|
310
|
+
return [command]
|
|
311
|
+
return [command, "agent"]
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def build_agent_flags(*, backend=None, model=None, thinking=None, flags=None):
|
|
315
|
+
"""Return raw backend flags for a model/thinking configuration.
|
|
316
|
+
|
|
317
|
+
The returned string is suitable for APIs that accept the existing ``flags``
|
|
318
|
+
parameter.
|
|
319
|
+
"""
|
|
320
|
+
backend = _resolve_backend(backend)
|
|
321
|
+
parts = _agent_config_flag_parts(backend, model, thinking)
|
|
322
|
+
if flags:
|
|
323
|
+
parts.extend(shlex.split(flags))
|
|
324
|
+
return shlex.join(parts)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def _agent_config_flag_parts(backend, model=None, thinking=None):
|
|
328
|
+
backend = _resolve_backend(backend)
|
|
329
|
+
parts = []
|
|
330
|
+
model = _clean_optional_text(model)
|
|
331
|
+
thinking = _clean_optional_text(thinking)
|
|
332
|
+
|
|
333
|
+
if backend == "codex":
|
|
334
|
+
if model:
|
|
335
|
+
parts.extend(["-c", f"model={model}"])
|
|
336
|
+
if thinking:
|
|
337
|
+
parts.extend(["-c", f"model_reasoning_effort={thinking}"])
|
|
338
|
+
return parts
|
|
339
|
+
|
|
340
|
+
if model:
|
|
341
|
+
parts.extend(["--model", model])
|
|
342
|
+
if thinking:
|
|
343
|
+
raise ValueError("thinking is only supported by the codex backend")
|
|
344
|
+
return parts
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def _clean_optional_text(value):
|
|
348
|
+
if value is None:
|
|
349
|
+
return None
|
|
350
|
+
text = str(value).strip()
|
|
351
|
+
return text or None
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def _run_cursor(
|
|
355
|
+
prompt,
|
|
356
|
+
cwd,
|
|
357
|
+
thread_id,
|
|
358
|
+
yolo,
|
|
359
|
+
flags,
|
|
360
|
+
include_thinking,
|
|
361
|
+
env,
|
|
362
|
+
model=None,
|
|
363
|
+
thinking=None,
|
|
364
|
+
):
|
|
231
365
|
"""Invoke the Cursor agent CLI and return the message plus session id (if any)."""
|
|
232
|
-
command = [
|
|
233
|
-
_CURSOR_BIN,
|
|
234
|
-
"agent",
|
|
366
|
+
command = _cursor_command_prefix(env) + [
|
|
235
367
|
"--trust",
|
|
236
368
|
]
|
|
237
369
|
if cwd:
|
|
@@ -240,6 +372,7 @@ def _run_cursor(prompt, cwd, thread_id, yolo, flags, include_thinking, env):
|
|
|
240
372
|
command.extend(["--resume", thread_id])
|
|
241
373
|
if yolo:
|
|
242
374
|
command.append("--yolo")
|
|
375
|
+
command.extend(_agent_config_flag_parts("cursor", model, thinking))
|
|
243
376
|
if flags:
|
|
244
377
|
command.extend(shlex.split(flags))
|
|
245
378
|
command.extend(["--print", "--output-format", "json"])
|
|
@@ -12,8 +12,9 @@ import uuid
|
|
|
12
12
|
|
|
13
13
|
from .agent import (
|
|
14
14
|
_CODEX_BIN,
|
|
15
|
-
|
|
15
|
+
_agent_config_flag_parts,
|
|
16
16
|
_codex_fast_config,
|
|
17
|
+
_cursor_command_prefix,
|
|
17
18
|
_ensure_backend_available,
|
|
18
19
|
_event_usage,
|
|
19
20
|
_merged_env,
|
|
@@ -88,6 +89,8 @@ class AsyncAgent:
|
|
|
88
89
|
env=None,
|
|
89
90
|
name=None,
|
|
90
91
|
fast=False,
|
|
92
|
+
model=None,
|
|
93
|
+
thinking=None,
|
|
91
94
|
):
|
|
92
95
|
"""Start a backend subprocess and return an async handle immediately."""
|
|
93
96
|
if not isinstance(prompt, str) or not prompt.strip():
|
|
@@ -95,7 +98,7 @@ class AsyncAgent:
|
|
|
95
98
|
|
|
96
99
|
backend = _resolve_backend(backend)
|
|
97
100
|
_ensure_backend_available(backend, env)
|
|
98
|
-
command = _build_command(backend, cwd, yolo, flags, fast)
|
|
101
|
+
command = _build_command(backend, cwd, yolo, flags, fast, model, thinking)
|
|
99
102
|
process = subprocess.Popen(
|
|
100
103
|
command,
|
|
101
104
|
stdin=subprocess.PIPE,
|
|
@@ -376,13 +379,13 @@ class AsyncAgent:
|
|
|
376
379
|
return self._rollout_final_output
|
|
377
380
|
|
|
378
381
|
|
|
379
|
-
def _build_command(backend, cwd, yolo, flags, fast=False):
|
|
382
|
+
def _build_command(backend, cwd, yolo, flags, fast=False, model=None, thinking=None):
|
|
380
383
|
if backend == "codex":
|
|
381
|
-
return _build_codex_command(cwd, yolo, flags, fast)
|
|
382
|
-
return _build_cursor_command(cwd, yolo, flags)
|
|
384
|
+
return _build_codex_command(cwd, yolo, flags, fast, model, thinking)
|
|
385
|
+
return _build_cursor_command(cwd, yolo, flags, model, thinking)
|
|
383
386
|
|
|
384
387
|
|
|
385
|
-
def _build_codex_command(cwd, yolo, flags, fast=False):
|
|
388
|
+
def _build_codex_command(cwd, yolo, flags, fast=False, model=None, thinking=None):
|
|
386
389
|
command = [
|
|
387
390
|
_CODEX_BIN,
|
|
388
391
|
"exec",
|
|
@@ -396,6 +399,7 @@ def _build_codex_command(cwd, yolo, flags, fast=False):
|
|
|
396
399
|
else:
|
|
397
400
|
command.append("--full-auto")
|
|
398
401
|
command.extend(_codex_fast_config(fast))
|
|
402
|
+
command.extend(_agent_config_flag_parts("codex", model, thinking))
|
|
399
403
|
if flags:
|
|
400
404
|
command.extend(shlex.split(flags))
|
|
401
405
|
if cwd:
|
|
@@ -404,16 +408,15 @@ def _build_codex_command(cwd, yolo, flags, fast=False):
|
|
|
404
408
|
return command
|
|
405
409
|
|
|
406
410
|
|
|
407
|
-
def _build_cursor_command(cwd, yolo, flags):
|
|
408
|
-
command = [
|
|
409
|
-
_CURSOR_BIN,
|
|
410
|
-
"agent",
|
|
411
|
+
def _build_cursor_command(cwd, yolo, flags, model=None, thinking=None):
|
|
412
|
+
command = _cursor_command_prefix() + [
|
|
411
413
|
"--trust",
|
|
412
414
|
]
|
|
413
415
|
if cwd:
|
|
414
416
|
command.extend(["--workspace", os.fspath(cwd)])
|
|
415
417
|
if yolo:
|
|
416
418
|
command.append("--yolo")
|
|
419
|
+
command.extend(_agent_config_flag_parts("cursor", model, thinking))
|
|
417
420
|
if flags:
|
|
418
421
|
command.extend(shlex.split(flags))
|
|
419
422
|
command.extend(["--print", "--output-format", "json"])
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: codexapi
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.10
|
|
4
4
|
Summary: Minimal Python API for running the Codex CLI.
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: codex,agent,cli,openai
|
|
@@ -80,6 +80,8 @@ Use `backend="cursor"` (or set `CODEXAPI_BACKEND=cursor`) to switch to the
|
|
|
80
80
|
Cursor agent backend.
|
|
81
81
|
Use `fast=True` in Codex API calls, or `--fast` in the CLI, to opt into Codex
|
|
82
82
|
fast mode. Normal mode is the default.
|
|
83
|
+
Use `model="..."` and `thinking="..."` in Codex API calls to override the
|
|
84
|
+
backend model and reasoning effort for a run.
|
|
83
85
|
|
|
84
86
|
## CLI
|
|
85
87
|
|
|
@@ -336,7 +338,7 @@ codexapi foreach list.txt task.yaml --retry-all
|
|
|
336
338
|
|
|
337
339
|
## API
|
|
338
340
|
|
|
339
|
-
### `agent(prompt, cwd=None, yolo=True, flags=None, include_thinking=False, backend=None, fast=False) -> str`
|
|
341
|
+
### `agent(prompt, cwd=None, yolo=True, flags=None, include_thinking=False, backend=None, fast=False, model=None, thinking=None) -> str`
|
|
340
342
|
|
|
341
343
|
Runs a single agent turn and returns only the agent's message. Any reasoning
|
|
342
344
|
items are filtered out.
|
|
@@ -348,8 +350,10 @@ items are filtered out.
|
|
|
348
350
|
- `include_thinking` (bool): when true, return all agent messages joined.
|
|
349
351
|
- `backend` (str | None): `codex` or `cursor` (defaults to `CODEXAPI_BACKEND` or `codex`).
|
|
350
352
|
- `fast` (bool): enable Codex fast mode (defaults to normal mode).
|
|
353
|
+
- `model` (str | None): backend model override. Codex maps this to `-c model=...`; Cursor maps it to `--model ...`.
|
|
354
|
+
- `thinking` (str | None): Codex reasoning effort override, mapped to `-c model_reasoning_effort=...`.
|
|
351
355
|
|
|
352
|
-
### `Agent(cwd=None, yolo=True, thread_id=None, flags=None, welfare=False, include_thinking=False, backend=None, fast=False)`
|
|
356
|
+
### `Agent(cwd=None, yolo=True, thread_id=None, flags=None, welfare=False, include_thinking=False, backend=None, fast=False, model=None, thinking=None)`
|
|
353
357
|
|
|
354
358
|
Creates a stateful session wrapper. Calling the instance sends the prompt into
|
|
355
359
|
the same conversation and returns only the agent's message.
|
|
@@ -363,6 +367,8 @@ the same conversation and returns only the agent's message.
|
|
|
363
367
|
- `include_thinking` (bool): when true, return all agent messages joined.
|
|
364
368
|
- `backend` (str | None): `codex` or `cursor` (defaults to `CODEXAPI_BACKEND` or `codex`).
|
|
365
369
|
- `fast` (bool): enable Codex fast mode (defaults to normal mode).
|
|
370
|
+
- `model` (str | None): backend model override.
|
|
371
|
+
- `thinking` (str | None): Codex reasoning effort override.
|
|
366
372
|
For Cursor, `thread_id` corresponds to the `session_id` returned by the agent.
|
|
367
373
|
|
|
368
374
|
### `lead(minutes, prompt, cwd=None, yolo=True, flags=None, leadbook=None, backend=None, fast=False) -> dict`
|
|
@@ -2,11 +2,12 @@ import json
|
|
|
2
2
|
import sys
|
|
3
3
|
import unittest
|
|
4
4
|
from pathlib import Path
|
|
5
|
+
from unittest.mock import patch
|
|
5
6
|
|
|
6
7
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
|
7
8
|
|
|
8
|
-
from codexapi.agent import _codex_fast_config, _parse_jsonl
|
|
9
|
-
from codexapi.async_agent import _build_codex_command
|
|
9
|
+
from codexapi.agent import _codex_fast_config, _parse_jsonl, build_agent_flags
|
|
10
|
+
from codexapi.async_agent import _build_codex_command, _build_cursor_command
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class AgentBackendTests(unittest.TestCase):
|
|
@@ -34,6 +35,35 @@ class AgentBackendTests(unittest.TestCase):
|
|
|
34
35
|
self.assertIn("service_tier=fast", command)
|
|
35
36
|
self.assertIn("features.fast_mode=true", command)
|
|
36
37
|
|
|
38
|
+
def test_build_agent_flags_maps_codex_model_and_thinking_to_config(self):
|
|
39
|
+
self.assertEqual(
|
|
40
|
+
build_agent_flags(backend="codex", model="gpt-5.5", thinking="xhigh"),
|
|
41
|
+
"-c model=gpt-5.5 -c model_reasoning_effort=xhigh",
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def test_build_agent_flags_maps_cursor_model_to_model_flag(self):
|
|
45
|
+
self.assertEqual(
|
|
46
|
+
build_agent_flags(backend="cursor", model="claude-4"),
|
|
47
|
+
"--model claude-4",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def test_build_agent_flags_rejects_cursor_thinking(self):
|
|
51
|
+
with self.assertRaises(ValueError):
|
|
52
|
+
build_agent_flags(backend="cursor", thinking="high")
|
|
53
|
+
|
|
54
|
+
def test_async_codex_command_can_set_model_and_thinking(self):
|
|
55
|
+
command = _build_codex_command(None, True, None, model="gpt-5.5", thinking="high")
|
|
56
|
+
self.assertIn("model=gpt-5.5", command)
|
|
57
|
+
self.assertIn("model_reasoning_effort=high", command)
|
|
58
|
+
|
|
59
|
+
def test_async_cursor_command_can_use_direct_cursor_agent(self):
|
|
60
|
+
with patch("codexapi.async_agent._cursor_command_prefix", return_value=["/tmp/cursor-agent"]):
|
|
61
|
+
command = _build_cursor_command("/tmp/work", True, None, model="composer-2")
|
|
62
|
+
self.assertEqual(command[0], "/tmp/cursor-agent")
|
|
63
|
+
self.assertNotEqual(command[1], "agent")
|
|
64
|
+
self.assertIn("--model", command)
|
|
65
|
+
self.assertIn("composer-2", command)
|
|
66
|
+
|
|
37
67
|
def test_parse_jsonl_extracts_last_token_usage(self):
|
|
38
68
|
output = "\n".join(
|
|
39
69
|
[
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|