codexapi 0.1.2__tar.gz → 0.1.3__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.1.2/src/codexapi.egg-info → codexapi-0.1.3}/PKG-INFO +44 -9
- codexapi-0.1.3/README.md +108 -0
- {codexapi-0.1.2 → codexapi-0.1.3}/pyproject.toml +1 -1
- codexapi-0.1.3/src/codexapi/__init__.py +7 -0
- codexapi-0.1.2/src/codexapi/client.py → codexapi-0.1.3/src/codexapi/agent.py +37 -65
- codexapi-0.1.3/src/codexapi/task.py +145 -0
- {codexapi-0.1.2 → codexapi-0.1.3/src/codexapi.egg-info}/PKG-INFO +44 -9
- {codexapi-0.1.2 → codexapi-0.1.3}/src/codexapi.egg-info/SOURCES.txt +2 -1
- codexapi-0.1.2/README.md +0 -73
- codexapi-0.1.2/src/codexapi/__init__.py +0 -6
- {codexapi-0.1.2 → codexapi-0.1.3}/LICENSE +0 -0
- {codexapi-0.1.2 → codexapi-0.1.3}/setup.cfg +0 -0
- {codexapi-0.1.2 → codexapi-0.1.3}/src/codexapi.egg-info/dependency_links.txt +0 -0
- {codexapi-0.1.2 → codexapi-0.1.3}/src/codexapi.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: codexapi
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Minimal Python API for running the Codex CLI.
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: codex,agent,cli,openai
|
|
@@ -12,7 +12,9 @@ License-File: LICENSE
|
|
|
12
12
|
|
|
13
13
|
# CodexAPI
|
|
14
14
|
|
|
15
|
-
Use codex from python as easily as calling a function with your codex credits instead of the API.
|
|
15
|
+
Use OpenAI's codex from python as easily as calling a function with your codex credits instead of the API.
|
|
16
|
+
|
|
17
|
+
*Note: this project is not affiliated with OpenAI in any way. Thanks for the awesome tools and models though!*
|
|
16
18
|
|
|
17
19
|
## Requirements
|
|
18
20
|
|
|
@@ -28,7 +30,7 @@ pip install codexapi
|
|
|
28
30
|
## Quickstart
|
|
29
31
|
|
|
30
32
|
```python
|
|
31
|
-
from codexapi import agent, Agent
|
|
33
|
+
from codexapi import agent, Agent, Task
|
|
32
34
|
|
|
33
35
|
# Run one-shot tasks as a function call
|
|
34
36
|
print(agent("Say hello"))
|
|
@@ -40,13 +42,23 @@ print(session("Now list any risks."))
|
|
|
40
42
|
|
|
41
43
|
# Save and resume a session later
|
|
42
44
|
thread_id = session.thread_id
|
|
43
|
-
session2 = Agent(cwd="/path/to/project",
|
|
45
|
+
session2 = Agent(cwd="/path/to/project", thread_id=thread_id)
|
|
44
46
|
print(session2("Continue from where we left off."))
|
|
47
|
+
|
|
48
|
+
# Define a task with a checker
|
|
49
|
+
class RepoTask(Task):
|
|
50
|
+
def check(self):
|
|
51
|
+
# Return an error string if something is wrong, or None/"" if OK
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
task = RepoTask("Summarize this repo.", cwd="/path/to/project")
|
|
55
|
+
result = task()
|
|
56
|
+
print(result.success, result.summary)
|
|
45
57
|
```
|
|
46
58
|
|
|
47
59
|
## API
|
|
48
60
|
|
|
49
|
-
### `agent(prompt, cwd=None,
|
|
61
|
+
### `agent(prompt, cwd=None, yolo=False, flags=None, full_auto=False) -> str`
|
|
50
62
|
|
|
51
63
|
Runs a single Codex turn and returns only the agent's message. Any reasoning
|
|
52
64
|
items are filtered out.
|
|
@@ -54,20 +66,43 @@ items are filtered out.
|
|
|
54
66
|
- `prompt` (str): prompt to send to Codex.
|
|
55
67
|
- `cwd` (str | PathLike | None): working directory for the Codex session.
|
|
56
68
|
- `yolo` (bool): pass `--yolo` to Codex when true.
|
|
57
|
-
- `agent` (str): agent backend to use (only `"codex"` is supported).
|
|
58
69
|
- `flags` (str | None): extra CLI flags to pass to Codex.
|
|
70
|
+
- `full_auto` (bool): enable `--full-auto` for workspace-write and on-request approvals.
|
|
59
71
|
|
|
60
|
-
### `Agent(cwd=None,
|
|
72
|
+
### `Agent(cwd=None, yolo=False, thread_id=None, flags=None, full_auto=False)`
|
|
61
73
|
|
|
62
74
|
Creates a stateful session wrapper. Calling the instance sends the prompt into
|
|
63
75
|
the same conversation and returns only the agent's message.
|
|
64
76
|
|
|
65
77
|
- `__call__(prompt) -> str`: send a prompt to Codex and return the message.
|
|
66
78
|
- `thread_id -> str | None`: expose the underlying session id once created.
|
|
67
|
-
- `trace_id` (str | None): Codex thread id to resume from the first call.
|
|
68
79
|
- `yolo` (bool): pass `--yolo` to Codex when true.
|
|
69
|
-
- `agent` (str): agent backend to use (only `"codex"` is supported).
|
|
70
80
|
- `flags` (str | None): extra CLI flags to pass to Codex.
|
|
81
|
+
- `full_auto` (bool): enable `--full-auto` for workspace-write and on-request approvals.
|
|
82
|
+
|
|
83
|
+
### `Task(prompt, max_attempts=10, cwd=None, yolo=False, thread_id=None, flags=None, full_auto=True)`
|
|
84
|
+
|
|
85
|
+
Runs a Codex task with checker-driven retries. Subclass it and implement
|
|
86
|
+
`check()` to return an error string when the task is incomplete, or return
|
|
87
|
+
`None`/`""` when the task passes.
|
|
88
|
+
|
|
89
|
+
- `__call__() -> TaskResult`: run the task.
|
|
90
|
+
- `set_up()`: optional setup hook.
|
|
91
|
+
- `tear_down()`: optional cleanup hook.
|
|
92
|
+
- `check() -> str | None`: return an error description or `None`/`""`.
|
|
93
|
+
- `on_success(result)`: optional success hook.
|
|
94
|
+
- `on_failure(result)`: optional failure hook.
|
|
95
|
+
- `full_auto` (bool): enable `--full-auto` for workspace-write and on-request approvals.
|
|
96
|
+
|
|
97
|
+
### `TaskResult(success, summary, attempts, errors, thread_id)`
|
|
98
|
+
|
|
99
|
+
Simple result object returned by `Task.__call__`.
|
|
100
|
+
|
|
101
|
+
- `success` (bool): whether the task completed successfully.
|
|
102
|
+
- `summary` (str): agent summary of what happened.
|
|
103
|
+
- `attempts` (int): how many attempts were used.
|
|
104
|
+
- `errors` (str | None): last checker error, if any.
|
|
105
|
+
- `thread_id` (str | None): Codex thread id for the session.
|
|
71
106
|
|
|
72
107
|
## Behavior notes
|
|
73
108
|
|
codexapi-0.1.3/README.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# CodexAPI
|
|
2
|
+
|
|
3
|
+
Use OpenAI's codex from python as easily as calling a function with your codex credits instead of the API.
|
|
4
|
+
|
|
5
|
+
*Note: this project is not affiliated with OpenAI in any way. Thanks for the awesome tools and models though!*
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
- Codex CLI installed and authenticated (`codex` must be on your PATH).
|
|
10
|
+
- Python 3.8+.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install codexapi
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Quickstart
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
from codexapi import agent, Agent, Task
|
|
22
|
+
|
|
23
|
+
# Run one-shot tasks as a function call
|
|
24
|
+
print(agent("Say hello"))
|
|
25
|
+
|
|
26
|
+
# Run a multi-turn conversation as a session
|
|
27
|
+
session = Agent(cwd="/path/to/project")
|
|
28
|
+
print(session("Summarize this repo."))
|
|
29
|
+
print(session("Now list any risks."))
|
|
30
|
+
|
|
31
|
+
# Save and resume a session later
|
|
32
|
+
thread_id = session.thread_id
|
|
33
|
+
session2 = Agent(cwd="/path/to/project", thread_id=thread_id)
|
|
34
|
+
print(session2("Continue from where we left off."))
|
|
35
|
+
|
|
36
|
+
# Define a task with a checker
|
|
37
|
+
class RepoTask(Task):
|
|
38
|
+
def check(self):
|
|
39
|
+
# Return an error string if something is wrong, or None/"" if OK
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
task = RepoTask("Summarize this repo.", cwd="/path/to/project")
|
|
43
|
+
result = task()
|
|
44
|
+
print(result.success, result.summary)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## API
|
|
48
|
+
|
|
49
|
+
### `agent(prompt, cwd=None, yolo=False, flags=None, full_auto=False) -> str`
|
|
50
|
+
|
|
51
|
+
Runs a single Codex turn and returns only the agent's message. Any reasoning
|
|
52
|
+
items are filtered out.
|
|
53
|
+
|
|
54
|
+
- `prompt` (str): prompt to send to Codex.
|
|
55
|
+
- `cwd` (str | PathLike | None): working directory for the Codex session.
|
|
56
|
+
- `yolo` (bool): pass `--yolo` to Codex when true.
|
|
57
|
+
- `flags` (str | None): extra CLI flags to pass to Codex.
|
|
58
|
+
- `full_auto` (bool): enable `--full-auto` for workspace-write and on-request approvals.
|
|
59
|
+
|
|
60
|
+
### `Agent(cwd=None, yolo=False, thread_id=None, flags=None, full_auto=False)`
|
|
61
|
+
|
|
62
|
+
Creates a stateful session wrapper. Calling the instance sends the prompt into
|
|
63
|
+
the same conversation and returns only the agent's message.
|
|
64
|
+
|
|
65
|
+
- `__call__(prompt) -> str`: send a prompt to Codex and return the message.
|
|
66
|
+
- `thread_id -> str | None`: expose the underlying session id once created.
|
|
67
|
+
- `yolo` (bool): pass `--yolo` to Codex when true.
|
|
68
|
+
- `flags` (str | None): extra CLI flags to pass to Codex.
|
|
69
|
+
- `full_auto` (bool): enable `--full-auto` for workspace-write and on-request approvals.
|
|
70
|
+
|
|
71
|
+
### `Task(prompt, max_attempts=10, cwd=None, yolo=False, thread_id=None, flags=None, full_auto=True)`
|
|
72
|
+
|
|
73
|
+
Runs a Codex task with checker-driven retries. Subclass it and implement
|
|
74
|
+
`check()` to return an error string when the task is incomplete, or return
|
|
75
|
+
`None`/`""` when the task passes.
|
|
76
|
+
|
|
77
|
+
- `__call__() -> TaskResult`: run the task.
|
|
78
|
+
- `set_up()`: optional setup hook.
|
|
79
|
+
- `tear_down()`: optional cleanup hook.
|
|
80
|
+
- `check() -> str | None`: return an error description or `None`/`""`.
|
|
81
|
+
- `on_success(result)`: optional success hook.
|
|
82
|
+
- `on_failure(result)`: optional failure hook.
|
|
83
|
+
- `full_auto` (bool): enable `--full-auto` for workspace-write and on-request approvals.
|
|
84
|
+
|
|
85
|
+
### `TaskResult(success, summary, attempts, errors, thread_id)`
|
|
86
|
+
|
|
87
|
+
Simple result object returned by `Task.__call__`.
|
|
88
|
+
|
|
89
|
+
- `success` (bool): whether the task completed successfully.
|
|
90
|
+
- `summary` (str): agent summary of what happened.
|
|
91
|
+
- `attempts` (int): how many attempts were used.
|
|
92
|
+
- `errors` (str | None): last checker error, if any.
|
|
93
|
+
- `thread_id` (str | None): Codex thread id for the session.
|
|
94
|
+
|
|
95
|
+
## Behavior notes
|
|
96
|
+
|
|
97
|
+
- Uses `codex exec --json` and parses JSONL events for `agent_message` items.
|
|
98
|
+
- Automatically passes `--skip-git-repo-check` so it can run outside a git repo.
|
|
99
|
+
- Passes `--yolo` when enabled (use with care).
|
|
100
|
+
- Raises `RuntimeError` if Codex exits non-zero or returns no agent message.
|
|
101
|
+
|
|
102
|
+
## Configuration
|
|
103
|
+
|
|
104
|
+
Set `CODEX_BIN` to point at a non-default Codex binary:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
export CODEX_BIN=/path/to/codex
|
|
108
|
+
```
|
|
@@ -1,45 +1,32 @@
|
|
|
1
1
|
"""Codex CLI wrapper used by the codexapi public interface."""
|
|
2
2
|
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
3
|
import json
|
|
6
4
|
import os
|
|
7
5
|
import shlex
|
|
8
6
|
import subprocess
|
|
9
|
-
from typing import Optional, Tuple, Union
|
|
10
|
-
|
|
11
|
-
Pathish = Union[str, os.PathLike]
|
|
12
7
|
|
|
13
8
|
_CODEX_BIN = os.environ.get("CODEX_BIN", "codex")
|
|
14
9
|
|
|
15
10
|
|
|
16
|
-
def agent(
|
|
17
|
-
prompt: str,
|
|
18
|
-
cwd: Optional[Pathish] = None,
|
|
19
|
-
*,
|
|
20
|
-
yolo: bool = False,
|
|
21
|
-
agent: str = "codex",
|
|
22
|
-
flags: Optional[str] = None,
|
|
23
|
-
) -> str:
|
|
11
|
+
def agent(prompt, cwd=None, yolo=False, flags=None, full_auto=False):
|
|
24
12
|
"""Run a single Codex turn and return only the agent's message.
|
|
25
13
|
|
|
26
14
|
Args:
|
|
27
15
|
prompt: The user prompt to send to Codex.
|
|
28
16
|
cwd: Optional working directory for the Codex session.
|
|
29
17
|
yolo: Whether to pass --yolo to Codex.
|
|
30
|
-
agent: Agent backend to use (only "codex" is supported).
|
|
31
18
|
flags: Additional raw CLI flags to pass to Codex.
|
|
32
19
|
|
|
33
20
|
Returns:
|
|
34
21
|
The agent's visible response text with reasoning traces removed.
|
|
35
22
|
"""
|
|
36
|
-
_require_codex_agent(agent)
|
|
37
23
|
message, _thread_id = _run_codex(
|
|
38
|
-
prompt
|
|
39
|
-
cwd
|
|
24
|
+
prompt,
|
|
25
|
+
cwd,
|
|
40
26
|
thread_id=None,
|
|
41
27
|
yolo=yolo,
|
|
42
28
|
flags=flags,
|
|
29
|
+
full_auto=full_auto,
|
|
43
30
|
)
|
|
44
31
|
return message
|
|
45
32
|
|
|
@@ -55,13 +42,12 @@ class Agent:
|
|
|
55
42
|
|
|
56
43
|
def __init__(
|
|
57
44
|
self,
|
|
58
|
-
cwd
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
) -> None:
|
|
45
|
+
cwd=None,
|
|
46
|
+
yolo=False,
|
|
47
|
+
thread_id=None,
|
|
48
|
+
flags=None,
|
|
49
|
+
full_auto=False,
|
|
50
|
+
):
|
|
65
51
|
"""Create a new session wrapper.
|
|
66
52
|
|
|
67
53
|
Args:
|
|
@@ -71,41 +57,30 @@ class Agent:
|
|
|
71
57
|
trace_id: Optional Codex thread id to resume from the first call.
|
|
72
58
|
flags: Additional raw CLI flags to pass to Codex.
|
|
73
59
|
"""
|
|
74
|
-
|
|
75
|
-
self._cwd = cwd
|
|
60
|
+
self.cwd = cwd
|
|
76
61
|
self._yolo = yolo
|
|
77
62
|
self._flags = flags
|
|
78
|
-
self.
|
|
63
|
+
self._full_auto = full_auto
|
|
64
|
+
self.thread_id = thread_id
|
|
79
65
|
|
|
80
|
-
def __call__(self, prompt
|
|
66
|
+
def __call__(self, prompt):
|
|
81
67
|
"""Send a prompt to Codex and return only the agent's message."""
|
|
82
68
|
message, thread_id = _run_codex(
|
|
83
|
-
prompt
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
69
|
+
prompt,
|
|
70
|
+
self.cwd,
|
|
71
|
+
self.thread_id,
|
|
72
|
+
self._yolo,
|
|
73
|
+
self._flags,
|
|
74
|
+
self._full_auto,
|
|
88
75
|
)
|
|
89
76
|
if thread_id:
|
|
90
|
-
self.
|
|
77
|
+
self.thread_id = thread_id
|
|
91
78
|
return message
|
|
92
79
|
|
|
93
|
-
@property
|
|
94
|
-
def thread_id(self) -> Optional[str]:
|
|
95
|
-
"""Return the current Codex session id, if any."""
|
|
96
|
-
return self._thread_id
|
|
97
80
|
|
|
98
|
-
|
|
99
|
-
def _run_codex(
|
|
100
|
-
*,
|
|
101
|
-
prompt: str,
|
|
102
|
-
cwd: Optional[Pathish],
|
|
103
|
-
thread_id: Optional[str],
|
|
104
|
-
yolo: bool,
|
|
105
|
-
flags: Optional[str],
|
|
106
|
-
) -> Tuple[str, Optional[str]]:
|
|
81
|
+
def _run_codex(prompt, cwd, thread_id, yolo, flags, full_auto):
|
|
107
82
|
"""Invoke the Codex CLI and return the message plus thread id (if any)."""
|
|
108
|
-
|
|
83
|
+
command = [
|
|
109
84
|
_CODEX_BIN,
|
|
110
85
|
"exec",
|
|
111
86
|
"--json",
|
|
@@ -114,22 +89,24 @@ def _run_codex(
|
|
|
114
89
|
"--skip-git-repo-check",
|
|
115
90
|
]
|
|
116
91
|
if yolo:
|
|
117
|
-
|
|
92
|
+
command.append("--yolo")
|
|
93
|
+
if full_auto:
|
|
94
|
+
command.append("--full-auto")
|
|
118
95
|
if flags:
|
|
119
|
-
|
|
120
|
-
if cwd
|
|
121
|
-
|
|
96
|
+
command.extend(shlex.split(flags))
|
|
97
|
+
if cwd:
|
|
98
|
+
command.extend(["--cd", os.fspath(cwd)])
|
|
122
99
|
if thread_id:
|
|
123
|
-
|
|
100
|
+
command.extend(["resume", thread_id, "-"])
|
|
124
101
|
else:
|
|
125
|
-
|
|
102
|
+
command.append("-")
|
|
126
103
|
|
|
127
104
|
result = subprocess.run(
|
|
128
|
-
|
|
105
|
+
command,
|
|
129
106
|
input=prompt,
|
|
130
107
|
text=True,
|
|
131
108
|
capture_output=True,
|
|
132
|
-
cwd=os.fspath(cwd) if cwd
|
|
109
|
+
cwd=os.fspath(cwd) if cwd else None,
|
|
133
110
|
)
|
|
134
111
|
if result.returncode != 0:
|
|
135
112
|
stderr = result.stderr.strip()
|
|
@@ -141,16 +118,11 @@ def _run_codex(
|
|
|
141
118
|
return _parse_jsonl(result.stdout)
|
|
142
119
|
|
|
143
120
|
|
|
144
|
-
def
|
|
145
|
-
if agent_name != "codex":
|
|
146
|
-
raise ValueError('Only agent="codex" is supported right now.')
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
def _parse_jsonl(output: str) -> Tuple[str, Optional[str]]:
|
|
121
|
+
def _parse_jsonl(output):
|
|
150
122
|
"""Extract agent messages and the latest thread id from Codex JSONL output."""
|
|
151
|
-
thread_id
|
|
152
|
-
messages
|
|
153
|
-
raw_lines
|
|
123
|
+
thread_id = None
|
|
124
|
+
messages = []
|
|
125
|
+
raw_lines = []
|
|
154
126
|
|
|
155
127
|
for line in output.splitlines():
|
|
156
128
|
line = line.strip()
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""Task wrapper for running Codex Agent flows with checkers."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
from .agent import Agent
|
|
6
|
+
|
|
7
|
+
_logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TaskResult:
|
|
11
|
+
"""Outcome summary for a task run."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, success, summary, attempts, errors, thread_id):
|
|
14
|
+
self.success = success
|
|
15
|
+
self.summary = summary
|
|
16
|
+
self.attempts = attempts
|
|
17
|
+
self.errors = errors
|
|
18
|
+
self.thread_id = thread_id
|
|
19
|
+
|
|
20
|
+
def __repr__(self):
|
|
21
|
+
return (
|
|
22
|
+
"TaskResult("
|
|
23
|
+
f"success={self.success}, "
|
|
24
|
+
f"attempts={self.attempts}, "
|
|
25
|
+
f"errors={self.errors!r}, "
|
|
26
|
+
f"thread_id={self.thread_id!r}, "
|
|
27
|
+
f"summary={self.summary!r}"
|
|
28
|
+
")"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Task:
|
|
33
|
+
""" Run a Codex Agent in a directory until it is verifiably done.
|
|
34
|
+
Subclass and override these functions:
|
|
35
|
+
set_up : prepare working directory, install things etc.
|
|
36
|
+
tear_down : undo the above and leave machine in a clean state
|
|
37
|
+
check : check if the task is done, return an error string if not
|
|
38
|
+
on_success : run if the task succeeds, e.g. commit and push
|
|
39
|
+
on_failure : run if the tsak fails, e.g. record why
|
|
40
|
+
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(
|
|
44
|
+
self,
|
|
45
|
+
prompt,
|
|
46
|
+
max_attempts=10,
|
|
47
|
+
cwd=None,
|
|
48
|
+
yolo=False,
|
|
49
|
+
thread_id=None,
|
|
50
|
+
flags=None,
|
|
51
|
+
full_auto=True,
|
|
52
|
+
):
|
|
53
|
+
if max_attempts < 1:
|
|
54
|
+
raise ValueError("max_attempts must be >= 1")
|
|
55
|
+
self.prompt = prompt
|
|
56
|
+
self.max_attempts = max_attempts
|
|
57
|
+
self.cwd = cwd
|
|
58
|
+
self.agent = Agent(
|
|
59
|
+
cwd=cwd,
|
|
60
|
+
yolo=yolo,
|
|
61
|
+
thread_id=thread_id,
|
|
62
|
+
flags=flags,
|
|
63
|
+
full_auto=full_auto,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
def set_up(self):
|
|
67
|
+
"""Clone a repo, set up a directory etc."""
|
|
68
|
+
|
|
69
|
+
def tear_down(self):
|
|
70
|
+
"""Delete the directory etc."""
|
|
71
|
+
|
|
72
|
+
def check(self):
|
|
73
|
+
""" Check if the task is done, return a string describing the problems if not.
|
|
74
|
+
This can be any combination of running tests, python code or running an agent
|
|
75
|
+
with a specific prompt in self.cwd.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
def on_success(self, result):
|
|
79
|
+
"""Hook called after a successful task, e.g. commit the changes."""
|
|
80
|
+
|
|
81
|
+
def on_failure(self, result):
|
|
82
|
+
"""Hook called after a failed run, e.g. log the failure reason."""
|
|
83
|
+
|
|
84
|
+
def fix_prompt(self, error):
|
|
85
|
+
"""Build a prompt that asks the agent to fix checker failures."""
|
|
86
|
+
return (
|
|
87
|
+
"The following checks failed:\n"
|
|
88
|
+
f"{error}\n\n"
|
|
89
|
+
"Can you please dive in and see if you agree with this assessment, then fix these issues while staying as close as you can to the spirit of the original task?"
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
def success_prompt(self):
|
|
93
|
+
"""Ask the agent to summarize what it did."""
|
|
94
|
+
return "Awesome - great job! Can you please produce a short summary of what you've done?"
|
|
95
|
+
|
|
96
|
+
def failure_prompt(self, error):
|
|
97
|
+
"""Ask the agent to summarize remaining issues after retries."""
|
|
98
|
+
return (
|
|
99
|
+
"We ran out of attempts. Can you please look back at everything you tried and summarize what it was that made this task too hard to complete, including anything you wish you'd known at the start that would have helped improve things?\n\n"
|
|
100
|
+
f"Outstanding issues:\n{error}"
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
def __call__(self, debug=False):
|
|
104
|
+
"""Run the task with checker-driven retries.
|
|
105
|
+
If debug is True, log debug messages.
|
|
106
|
+
"""
|
|
107
|
+
try:
|
|
108
|
+
# If this fails in the middle we will still try to tear down
|
|
109
|
+
self.set_up()
|
|
110
|
+
|
|
111
|
+
# Start with the initial prompt
|
|
112
|
+
output = self.agent(self.prompt)
|
|
113
|
+
if debug:
|
|
114
|
+
_logger.debug("Initial output: %s", output)
|
|
115
|
+
|
|
116
|
+
# Try correcting it up to max_attempts times
|
|
117
|
+
for attempt in range(self.max_attempts):
|
|
118
|
+
error = self.check()
|
|
119
|
+
if debug:
|
|
120
|
+
_logger.debug("Check error: %s", error)
|
|
121
|
+
|
|
122
|
+
if error:
|
|
123
|
+
# if there were errors, tell the agent to fix them
|
|
124
|
+
output = self.agent(self.fix_prompt(error))
|
|
125
|
+
if debug:
|
|
126
|
+
_logger.debug("Fix output: %s", output)
|
|
127
|
+
else:
|
|
128
|
+
# otherwise get a summary of what was done and run on_success
|
|
129
|
+
summary = self.agent(self.success_prompt())
|
|
130
|
+
if debug:
|
|
131
|
+
_logger.debug("Success summary: %s", summary)
|
|
132
|
+
result = TaskResult(True, summary, attempt + 1, error, self.agent.thread_id)
|
|
133
|
+
self.on_success(result)
|
|
134
|
+
return result
|
|
135
|
+
|
|
136
|
+
# Ran out of attempts - get a reason why and run on_failure
|
|
137
|
+
summary = self.agent(self.failure_prompt(error))
|
|
138
|
+
if debug:
|
|
139
|
+
_logger.debug("Failure summary: %s", summary)
|
|
140
|
+
result = TaskResult(False, summary, attempt + 1, error, self.agent.thread_id)
|
|
141
|
+
self.on_failure(result)
|
|
142
|
+
return result
|
|
143
|
+
finally:
|
|
144
|
+
# No matter what, once we have set_up we will always tear_down
|
|
145
|
+
self.tear_down()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: codexapi
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Minimal Python API for running the Codex CLI.
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: codex,agent,cli,openai
|
|
@@ -12,7 +12,9 @@ License-File: LICENSE
|
|
|
12
12
|
|
|
13
13
|
# CodexAPI
|
|
14
14
|
|
|
15
|
-
Use codex from python as easily as calling a function with your codex credits instead of the API.
|
|
15
|
+
Use OpenAI's codex from python as easily as calling a function with your codex credits instead of the API.
|
|
16
|
+
|
|
17
|
+
*Note: this project is not affiliated with OpenAI in any way. Thanks for the awesome tools and models though!*
|
|
16
18
|
|
|
17
19
|
## Requirements
|
|
18
20
|
|
|
@@ -28,7 +30,7 @@ pip install codexapi
|
|
|
28
30
|
## Quickstart
|
|
29
31
|
|
|
30
32
|
```python
|
|
31
|
-
from codexapi import agent, Agent
|
|
33
|
+
from codexapi import agent, Agent, Task
|
|
32
34
|
|
|
33
35
|
# Run one-shot tasks as a function call
|
|
34
36
|
print(agent("Say hello"))
|
|
@@ -40,13 +42,23 @@ print(session("Now list any risks."))
|
|
|
40
42
|
|
|
41
43
|
# Save and resume a session later
|
|
42
44
|
thread_id = session.thread_id
|
|
43
|
-
session2 = Agent(cwd="/path/to/project",
|
|
45
|
+
session2 = Agent(cwd="/path/to/project", thread_id=thread_id)
|
|
44
46
|
print(session2("Continue from where we left off."))
|
|
47
|
+
|
|
48
|
+
# Define a task with a checker
|
|
49
|
+
class RepoTask(Task):
|
|
50
|
+
def check(self):
|
|
51
|
+
# Return an error string if something is wrong, or None/"" if OK
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
task = RepoTask("Summarize this repo.", cwd="/path/to/project")
|
|
55
|
+
result = task()
|
|
56
|
+
print(result.success, result.summary)
|
|
45
57
|
```
|
|
46
58
|
|
|
47
59
|
## API
|
|
48
60
|
|
|
49
|
-
### `agent(prompt, cwd=None,
|
|
61
|
+
### `agent(prompt, cwd=None, yolo=False, flags=None, full_auto=False) -> str`
|
|
50
62
|
|
|
51
63
|
Runs a single Codex turn and returns only the agent's message. Any reasoning
|
|
52
64
|
items are filtered out.
|
|
@@ -54,20 +66,43 @@ items are filtered out.
|
|
|
54
66
|
- `prompt` (str): prompt to send to Codex.
|
|
55
67
|
- `cwd` (str | PathLike | None): working directory for the Codex session.
|
|
56
68
|
- `yolo` (bool): pass `--yolo` to Codex when true.
|
|
57
|
-
- `agent` (str): agent backend to use (only `"codex"` is supported).
|
|
58
69
|
- `flags` (str | None): extra CLI flags to pass to Codex.
|
|
70
|
+
- `full_auto` (bool): enable `--full-auto` for workspace-write and on-request approvals.
|
|
59
71
|
|
|
60
|
-
### `Agent(cwd=None,
|
|
72
|
+
### `Agent(cwd=None, yolo=False, thread_id=None, flags=None, full_auto=False)`
|
|
61
73
|
|
|
62
74
|
Creates a stateful session wrapper. Calling the instance sends the prompt into
|
|
63
75
|
the same conversation and returns only the agent's message.
|
|
64
76
|
|
|
65
77
|
- `__call__(prompt) -> str`: send a prompt to Codex and return the message.
|
|
66
78
|
- `thread_id -> str | None`: expose the underlying session id once created.
|
|
67
|
-
- `trace_id` (str | None): Codex thread id to resume from the first call.
|
|
68
79
|
- `yolo` (bool): pass `--yolo` to Codex when true.
|
|
69
|
-
- `agent` (str): agent backend to use (only `"codex"` is supported).
|
|
70
80
|
- `flags` (str | None): extra CLI flags to pass to Codex.
|
|
81
|
+
- `full_auto` (bool): enable `--full-auto` for workspace-write and on-request approvals.
|
|
82
|
+
|
|
83
|
+
### `Task(prompt, max_attempts=10, cwd=None, yolo=False, thread_id=None, flags=None, full_auto=True)`
|
|
84
|
+
|
|
85
|
+
Runs a Codex task with checker-driven retries. Subclass it and implement
|
|
86
|
+
`check()` to return an error string when the task is incomplete, or return
|
|
87
|
+
`None`/`""` when the task passes.
|
|
88
|
+
|
|
89
|
+
- `__call__() -> TaskResult`: run the task.
|
|
90
|
+
- `set_up()`: optional setup hook.
|
|
91
|
+
- `tear_down()`: optional cleanup hook.
|
|
92
|
+
- `check() -> str | None`: return an error description or `None`/`""`.
|
|
93
|
+
- `on_success(result)`: optional success hook.
|
|
94
|
+
- `on_failure(result)`: optional failure hook.
|
|
95
|
+
- `full_auto` (bool): enable `--full-auto` for workspace-write and on-request approvals.
|
|
96
|
+
|
|
97
|
+
### `TaskResult(success, summary, attempts, errors, thread_id)`
|
|
98
|
+
|
|
99
|
+
Simple result object returned by `Task.__call__`.
|
|
100
|
+
|
|
101
|
+
- `success` (bool): whether the task completed successfully.
|
|
102
|
+
- `summary` (str): agent summary of what happened.
|
|
103
|
+
- `attempts` (int): how many attempts were used.
|
|
104
|
+
- `errors` (str | None): last checker error, if any.
|
|
105
|
+
- `thread_id` (str | None): Codex thread id for the session.
|
|
71
106
|
|
|
72
107
|
## Behavior notes
|
|
73
108
|
|
codexapi-0.1.2/README.md
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
# CodexAPI
|
|
2
|
-
|
|
3
|
-
Use codex from python as easily as calling a function with your codex credits instead of the API.
|
|
4
|
-
|
|
5
|
-
## Requirements
|
|
6
|
-
|
|
7
|
-
- Codex CLI installed and authenticated (`codex` must be on your PATH).
|
|
8
|
-
- Python 3.8+.
|
|
9
|
-
|
|
10
|
-
## Install
|
|
11
|
-
|
|
12
|
-
```bash
|
|
13
|
-
pip install codexapi
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## Quickstart
|
|
17
|
-
|
|
18
|
-
```python
|
|
19
|
-
from codexapi import agent, Agent
|
|
20
|
-
|
|
21
|
-
# Run one-shot tasks as a function call
|
|
22
|
-
print(agent("Say hello"))
|
|
23
|
-
|
|
24
|
-
# Run a multi-turn conversation as a session
|
|
25
|
-
session = Agent(cwd="/path/to/project")
|
|
26
|
-
print(session("Summarize this repo."))
|
|
27
|
-
print(session("Now list any risks."))
|
|
28
|
-
|
|
29
|
-
# Save and resume a session later
|
|
30
|
-
thread_id = session.thread_id
|
|
31
|
-
session2 = Agent(cwd="/path/to/project", trace_id=thread_id)
|
|
32
|
-
print(session2("Continue from where we left off."))
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## API
|
|
36
|
-
|
|
37
|
-
### `agent(prompt, cwd=None, *, yolo=False, agent="codex", flags=None) -> str`
|
|
38
|
-
|
|
39
|
-
Runs a single Codex turn and returns only the agent's message. Any reasoning
|
|
40
|
-
items are filtered out.
|
|
41
|
-
|
|
42
|
-
- `prompt` (str): prompt to send to Codex.
|
|
43
|
-
- `cwd` (str | PathLike | None): working directory for the Codex session.
|
|
44
|
-
- `yolo` (bool): pass `--yolo` to Codex when true.
|
|
45
|
-
- `agent` (str): agent backend to use (only `"codex"` is supported).
|
|
46
|
-
- `flags` (str | None): extra CLI flags to pass to Codex.
|
|
47
|
-
|
|
48
|
-
### `Agent(cwd=None, *, yolo=False, agent="codex", trace_id=None, flags=None)`
|
|
49
|
-
|
|
50
|
-
Creates a stateful session wrapper. Calling the instance sends the prompt into
|
|
51
|
-
the same conversation and returns only the agent's message.
|
|
52
|
-
|
|
53
|
-
- `__call__(prompt) -> str`: send a prompt to Codex and return the message.
|
|
54
|
-
- `thread_id -> str | None`: expose the underlying session id once created.
|
|
55
|
-
- `trace_id` (str | None): Codex thread id to resume from the first call.
|
|
56
|
-
- `yolo` (bool): pass `--yolo` to Codex when true.
|
|
57
|
-
- `agent` (str): agent backend to use (only `"codex"` is supported).
|
|
58
|
-
- `flags` (str | None): extra CLI flags to pass to Codex.
|
|
59
|
-
|
|
60
|
-
## Behavior notes
|
|
61
|
-
|
|
62
|
-
- Uses `codex exec --json` and parses JSONL events for `agent_message` items.
|
|
63
|
-
- Automatically passes `--skip-git-repo-check` so it can run outside a git repo.
|
|
64
|
-
- Passes `--yolo` when enabled (use with care).
|
|
65
|
-
- Raises `RuntimeError` if Codex exits non-zero or returns no agent message.
|
|
66
|
-
|
|
67
|
-
## Configuration
|
|
68
|
-
|
|
69
|
-
Set `CODEX_BIN` to point at a non-default Codex binary:
|
|
70
|
-
|
|
71
|
-
```bash
|
|
72
|
-
export CODEX_BIN=/path/to/codex
|
|
73
|
-
```
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|