python-codex 0.2.5__py3-none-any.whl → 0.2.7__py3-none-any.whl
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.
- pycodex/__init__.py +4 -0
- pycodex/agent.py +54 -14
- pycodex/cli.py +110 -36
- pycodex/image_utils.py +79 -0
- pycodex/model.py +7 -1
- pycodex/portable.py +14 -7
- pycodex/prompts/models.json +170 -0
- pycodex/runtime.py +2 -0
- pycodex/tools/__init__.py +3 -0
- pycodex/tools/clock_tool.py +168 -0
- pycodex/tools/view_image_tool.py +7 -9
- pycodex/utils/compactor.py +17 -2
- pycodex/utils/session_persist.py +50 -2
- pycodex/utils/visualize.py +11 -4
- {python_codex-0.2.5.dist-info → python_codex-0.2.7.dist-info}/METADATA +47 -17
- {python_codex-0.2.5.dist-info → python_codex-0.2.7.dist-info}/RECORD +27 -25
- responses_server/config.py +4 -1
- responses_server/messages_api.py +49 -0
- responses_server/payload_processors.py +1 -0
- responses_server/stream_router.py +108 -22
- responses_server/trajectory_dump.py +18 -2
- workspace_server/app.py +12 -12
- workspace_server/workspace.html +300 -103
- workspace_server/workspaces.py +34 -24
- {python_codex-0.2.5.dist-info → python_codex-0.2.7.dist-info}/WHEEL +0 -0
- {python_codex-0.2.5.dist-info → python_codex-0.2.7.dist-info}/entry_points.txt +0 -0
- {python_codex-0.2.5.dist-info → python_codex-0.2.7.dist-info}/licenses/LICENSE +0 -0
pycodex/utils/session_persist.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import json
|
|
2
|
+
import mmap
|
|
2
3
|
import os
|
|
3
4
|
import re
|
|
4
5
|
from datetime import datetime
|
|
@@ -23,6 +24,8 @@ UUID_PATTERN = re.compile(
|
|
|
23
24
|
re.IGNORECASE,
|
|
24
25
|
)
|
|
25
26
|
ROLLOUT_READ_CHUNK_SIZE = 1024 * 1024
|
|
27
|
+
COMPACTED_RECORD_MARKER = b',"type":"compacted","payload":'
|
|
28
|
+
ROLLOUT_RECORD_PREFIX = b'\n{"timestamp":"'
|
|
26
29
|
|
|
27
30
|
|
|
28
31
|
def resolve_codex_home(
|
|
@@ -215,7 +218,14 @@ def load_resumed_session_path(
|
|
|
215
218
|
saw_user_turn = False
|
|
216
219
|
tool_names_by_call_id: 'typing.Dict[str, str]' = {}
|
|
217
220
|
|
|
218
|
-
|
|
221
|
+
# A rollout is append-only, and a compacted entry replaces everything
|
|
222
|
+
# before it for the next request. Large tool outputs before the latest
|
|
223
|
+
# checkpoint are retained for audit, but do not need to be decoded while
|
|
224
|
+
# restoring the active conversation.
|
|
225
|
+
compacted_offset = _find_last_compacted_offset(rollout_path)
|
|
226
|
+
entry_start_offset = compacted_offset if compacted_offset is not None else 0
|
|
227
|
+
|
|
228
|
+
for entry in _iter_rollout_entries(rollout_path, entry_start_offset):
|
|
219
229
|
item_type = str(entry.get("type", "")).strip()
|
|
220
230
|
payload = entry.get("payload")
|
|
221
231
|
|
|
@@ -363,13 +373,51 @@ def _extract_first_user_message_preview(rollout_path: 'Path') -> 'typing.Union[s
|
|
|
363
373
|
return None
|
|
364
374
|
|
|
365
375
|
|
|
366
|
-
def
|
|
376
|
+
def _find_last_compacted_offset(
|
|
377
|
+
rollout_path: 'Path',
|
|
378
|
+
) -> 'typing.Union[int, None]':
|
|
379
|
+
"""Find the latest recorder-format compact checkpoint."""
|
|
380
|
+
|
|
381
|
+
file_size = rollout_path.stat().st_size
|
|
382
|
+
if not file_size:
|
|
383
|
+
return None
|
|
384
|
+
|
|
385
|
+
with rollout_path.open("rb") as handle:
|
|
386
|
+
mapped = mmap.mmap(handle.fileno(), 0, access=mmap.ACCESS_READ)
|
|
387
|
+
try:
|
|
388
|
+
marker_offset = mapped.rfind(COMPACTED_RECORD_MARKER)
|
|
389
|
+
if marker_offset < 0:
|
|
390
|
+
return None
|
|
391
|
+
record_start = mapped.rfind(
|
|
392
|
+
ROLLOUT_RECORD_PREFIX,
|
|
393
|
+
0,
|
|
394
|
+
marker_offset,
|
|
395
|
+
)
|
|
396
|
+
if record_start < 0:
|
|
397
|
+
return None
|
|
398
|
+
previous = record_start - 1
|
|
399
|
+
while previous >= 0 and mapped[previous] in b" \t\r\n":
|
|
400
|
+
previous -= 1
|
|
401
|
+
if previous >= 0 and mapped[previous] != ord("}"):
|
|
402
|
+
return None
|
|
403
|
+
return record_start + 1
|
|
404
|
+
finally:
|
|
405
|
+
mapped.close()
|
|
406
|
+
return None
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
def _iter_rollout_entries(
|
|
410
|
+
rollout_path: 'Path',
|
|
411
|
+
start_offset: 'int' = 0,
|
|
412
|
+
) -> 'typing.Iterable[typing.Dict[str, object]]':
|
|
367
413
|
decoder = json.JSONDecoder()
|
|
368
414
|
buffer = ""
|
|
369
415
|
start = 0
|
|
370
416
|
parsed_entries = 0
|
|
371
417
|
|
|
372
418
|
with rollout_path.open("r", encoding="utf-8", errors="replace") as handle:
|
|
419
|
+
if start_offset:
|
|
420
|
+
handle.seek(start_offset)
|
|
373
421
|
while True:
|
|
374
422
|
chunk = handle.read(ROLLOUT_READ_CHUNK_SIZE)
|
|
375
423
|
eof = not chunk
|
pycodex/utils/visualize.py
CHANGED
|
@@ -17,7 +17,15 @@ import typing
|
|
|
17
17
|
STATUS_FRAMES = ("⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏")
|
|
18
18
|
PROMPT_CONTEXT_BASELINE_TOKENS = 12_000
|
|
19
19
|
DEFAULT_MAIN_PROMPT = "pycodex> "
|
|
20
|
-
|
|
20
|
+
IDLE_SLEEPING_STATUS = "idle: sleeping"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def background_work_count(payload: "typing.Mapping[str, object]") -> "int":
|
|
24
|
+
value = payload.get("background_work_count", 0)
|
|
25
|
+
try:
|
|
26
|
+
return max(int(value), 0)
|
|
27
|
+
except (TypeError, ValueError):
|
|
28
|
+
return 0
|
|
21
29
|
|
|
22
30
|
|
|
23
31
|
def shorten_title(text: "str", limit: "int" = 48) -> "str":
|
|
@@ -467,9 +475,8 @@ class CliSessionView:
|
|
|
467
475
|
return f"pyco({self._context_remaining_percent}%)> "
|
|
468
476
|
|
|
469
477
|
def _set_idle_status(self, event: "AgentEvent") -> "None":
|
|
470
|
-
background_work_count
|
|
471
|
-
|
|
472
|
-
self.prompter.set_status(IDLE_LISTENING_STATUS)
|
|
478
|
+
if background_work_count(event.payload) > 0:
|
|
479
|
+
self.prompter.set_status(IDLE_SLEEPING_STATUS)
|
|
473
480
|
else:
|
|
474
481
|
self.prompter.set_status(active=False)
|
|
475
482
|
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-codex
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
4
4
|
Summary: A minimal Python extraction of Codex's main agent loop
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Python: >=3.6.2
|
|
7
|
-
Requires-Dist: cryptography<41,>=40.0.2; python_version < '3.7'
|
|
8
|
-
Requires-Dist: cryptography>=40.0.2; python_version >= '3.7'
|
|
9
7
|
Requires-Dist: dataclasses>=0.8; python_version < '3.7'
|
|
10
8
|
Requires-Dist: fastapi<0.84,>=0.83.0; python_version < '3.7'
|
|
11
9
|
Requires-Dist: fastapi>=0.83.0; python_version >= '3.7'
|
|
12
10
|
Requires-Dist: importlib-metadata>=4.8.3; python_version < '3.8'
|
|
13
11
|
Requires-Dist: loguru>=0.7.3
|
|
12
|
+
Requires-Dist: pillow>=8.4.0; python_version < '3.7'
|
|
13
|
+
Requires-Dist: pillow>=9.0.0; python_version >= '3.7'
|
|
14
14
|
Requires-Dist: prompt-toolkit>=3.0.36
|
|
15
|
+
Requires-Dist: pycryptodomex>=3.20
|
|
15
16
|
Requires-Dist: requests>=2.27.1
|
|
16
17
|
Requires-Dist: tomli<2,>=1.2.3; python_version < '3.11'
|
|
17
18
|
Requires-Dist: typing-extensions>=4.1.1; python_version < '3.8'
|
|
@@ -23,8 +24,17 @@ Description-Content-Type: text/markdown
|
|
|
23
24
|
|
|
24
25
|
English README. Chinese version: `README_ZH.md`
|
|
25
26
|
|
|
26
|
-
PyPI
|
|
27
|
-
|
|
27
|
+
PyPI distributions:
|
|
28
|
+
|
|
29
|
+
- Primary package: `python-codex`
|
|
30
|
+
- Workspace install alias: `pycodex-ws`
|
|
31
|
+
|
|
32
|
+
The import path remains `pycodex`; the CLI commands are `pycodex` and
|
|
33
|
+
`pycodex-ws`.
|
|
34
|
+
|
|
35
|
+
`pycodex-ws` is a thin metapackage that depends on the exact matching
|
|
36
|
+
`python-codex` version. The implementation and console script remain owned by
|
|
37
|
+
`python-codex`, so the two distributions never install duplicate modules.
|
|
28
38
|
|
|
29
39
|
This repository extracts the core Codex agent loop from upstream Codex
|
|
30
40
|
(`https://github.com/openai/codex`) into a deliberately small Python version,
|
|
@@ -44,6 +54,13 @@ Relevant Rust reference points:
|
|
|
44
54
|
|
|
45
55
|
## Quick Start
|
|
46
56
|
|
|
57
|
+
Install the full package or the workspace-oriented alias:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install python-codex
|
|
61
|
+
pip install pycodex-ws
|
|
62
|
+
```
|
|
63
|
+
|
|
47
64
|
Install dependencies first:
|
|
48
65
|
|
|
49
66
|
```bash
|
|
@@ -238,17 +255,29 @@ Current behavior:
|
|
|
238
255
|
later steer text is appended to the next model request's `input` in order;
|
|
239
256
|
for explicit queueing, use `/queue <message>`, which prints
|
|
240
257
|
`[steer] queued: ...` and later `[steer] inserted: ...`
|
|
241
|
-
- the default
|
|
242
|
-
|
|
243
|
-
`
|
|
244
|
-
`
|
|
245
|
-
`
|
|
258
|
+
- the default local tool set includes the upstream-aligned subset plus the
|
|
259
|
+
pycodex `clock` extension: `shell`, `shell_command`, `exec_command`,
|
|
260
|
+
`write_stdin`, `clock`, `exec`, `wait`, `web_search`, `update_plan`,
|
|
261
|
+
`request_user_input`, `request_permissions`, `spawn_agent`, `send_input`,
|
|
262
|
+
`resume_agent`, `wait_agent`, `close_agent`, `apply_patch`, `grep_files`,
|
|
263
|
+
`read_file`, `list_dir`, `view_image`
|
|
264
|
+
- `clock(period_m)` sets one periodic clock for the current Agent session;
|
|
265
|
+
`null` cancels it. The countdown restarts after each reply and wakes the
|
|
266
|
+
Agent with a `<clock_tick>` message containing the current timezone-aware
|
|
267
|
+
time when it expires.
|
|
268
|
+
- while a background command or clock is pending, the idle status is
|
|
269
|
+
`idle: sleeping`
|
|
270
|
+
- only the active workspace tab shows its close button
|
|
246
271
|
- `--vllm-endpoint http://host:port` automatically launches a local
|
|
247
272
|
`responses_server` compatibility layer; when the URL path is empty it is
|
|
248
273
|
normalized to `/v1`, and `/responses` requests are still forwarded to the
|
|
249
|
-
downstream `/v1/chat/completions` endpoint.
|
|
250
|
-
|
|
251
|
-
|
|
274
|
+
downstream `/v1/chat/completions` endpoint. This local compat path always
|
|
275
|
+
uses the canonical Responses request shape, even when the selected model's
|
|
276
|
+
metadata enables `responses_lite`. With `--vllm-endpoint`, startup also reads
|
|
277
|
+
`/v1/models` and uses the last returned model id for the downstream request.
|
|
278
|
+
For `model_provider = "vllm"`, reasoning is preserved across this path:
|
|
279
|
+
chat chunks with `reasoning` or `reasoning_content` are translated back into
|
|
280
|
+
Responses `reasoning` items, and
|
|
252
281
|
historical `reasoning` items are replayed into downstream assistant messages
|
|
253
282
|
via the `reasoning` field. Streaming token usage is also requested from vLLM
|
|
254
283
|
and forwarded to the final `response.completed.response.usage`. If a
|
|
@@ -392,6 +421,7 @@ Upstream low-frequency / special-mode tools not yet modeled separately:
|
|
|
392
421
|
|
|
393
422
|
Repository-specific compatibility / transition tools:
|
|
394
423
|
|
|
424
|
+
- [x] `clock` - pycodex periodic Agent wake-up extension.
|
|
395
425
|
- [x] `exec` - current local approximation of code mode.
|
|
396
426
|
- [x] `wait` - current local approximation of code-mode waiting behavior.
|
|
397
427
|
|
|
@@ -406,8 +436,8 @@ Repository-specific compatibility / transition tools:
|
|
|
406
436
|
matches upstream.
|
|
407
437
|
- [x] `AGENTS.md` + `<environment_context>` injection alignment - context
|
|
408
438
|
assembly order matches upstream.
|
|
409
|
-
- [x] non-interactive `exec` tool subset alignment - the
|
|
410
|
-
has converged
|
|
439
|
+
- [x] non-interactive `exec` upstream tool subset alignment - the aligned
|
|
440
|
+
subset has converged; pycodex additionally exposes `clock`.
|
|
411
441
|
- [x] `include = ["reasoning.encrypted_content"]` - reasoning include field is
|
|
412
442
|
aligned.
|
|
413
443
|
- [x] `prompt_cache_key` - request-level prompt cache key is implemented.
|
|
@@ -416,8 +446,8 @@ Repository-specific compatibility / transition tools:
|
|
|
416
446
|
- [x] `originator` - mode-aware originator header is implemented.
|
|
417
447
|
- [x] exact `user-agent` string alignment - aligned on the non-interactive
|
|
418
448
|
`exec` path.
|
|
419
|
-
- [x] field-by-field exec-mode tool schema alignment -
|
|
420
|
-
|
|
449
|
+
- [x] field-by-field upstream exec-mode tool schema alignment - aligned tools
|
|
450
|
+
use class-level specs; `clock` is documented separately as an extension.
|
|
421
451
|
- [ ] full interactive-mode and non-`exec` behavior alignment - the non-exec
|
|
422
452
|
first-turn context is now on the `codex-tui` path, but continuous REPL
|
|
423
453
|
multi-turn behavior is not fully verified yet.
|
|
@@ -1,24 +1,25 @@
|
|
|
1
|
-
pycodex/__init__.py,sha256=
|
|
2
|
-
pycodex/agent.py,sha256=
|
|
3
|
-
pycodex/cli.py,sha256=
|
|
1
|
+
pycodex/__init__.py,sha256=dzSorjXWi2XYzUU1l_zcCxO_WRb3B0a5OzMyCzQ9WC4,3266
|
|
2
|
+
pycodex/agent.py,sha256=sz0qAvCP52tpKlmPZc5LA1e9hY_vWWQ3tgHpvLNqojY,22968
|
|
3
|
+
pycodex/cli.py,sha256=XGiYLjK5s5ObvKRejuT3KQh-ua-TlmOw-BcT9K0VYF0,22809
|
|
4
4
|
pycodex/collaboration.py,sha256=yQ6pBD-R3ZWR4_FAYQFoS7KF0m4LLD42otXIbPqw2ys,641
|
|
5
5
|
pycodex/compat.py,sha256=l35JE0vGAOCn9NWbWbqwGURGk83HXddXQ5wJIfcG41o,3254
|
|
6
6
|
pycodex/context.py,sha256=NRCJpYpEm76cAK1akid0BvQ-Z0mAvw1ZNboVxjPtm6U,26219
|
|
7
7
|
pycodex/doctor.py,sha256=De3M4hRBJq8ZeqsUJgHz0vitqrH18YugrEnz7oHhTdQ,10572
|
|
8
8
|
pycodex/feishu_card.py,sha256=De6pM--3MfhgGo-WcWfhm-fop5UtzjwKrZ4gI2Lls3w,26198
|
|
9
9
|
pycodex/feishu_link.py,sha256=DDQCYXQXgo_2SpXPyWlK8KYcumNO1-sqFuQDsEMQpTg,16427
|
|
10
|
+
pycodex/image_utils.py,sha256=9TvowOPq0vTX3J4F-WLgz_Cfl7-jt3zLNM0oL3Vbhq8,2432
|
|
10
11
|
pycodex/interactive_session.py,sha256=WzOD06d43ckF7SPer12JKzUPIK42Djw4kEjFRdM6_Lw,16420
|
|
11
|
-
pycodex/model.py,sha256=
|
|
12
|
+
pycodex/model.py,sha256=cgCFtOhB2GBFDiGU1OvqtBh5ngFmBhN53PphonCY5ek,42066
|
|
12
13
|
pycodex/model_metadata.py,sha256=7fHntKSUXhG5eASJi4yw4jU_IyzGbYBp-WE6af19ATA,877
|
|
13
|
-
pycodex/portable.py,sha256=
|
|
14
|
+
pycodex/portable.py,sha256=HdgfVwEoooh_eaZwiu4lqLXYes8ONyTnCMDqKuM8-Tc,15901
|
|
14
15
|
pycodex/portable_server.py,sha256=6I3pQkWj3e_SFlDXY2mGdCPns1w_3PSxByBV9wv5epI,7331
|
|
15
16
|
pycodex/protocol.py,sha256=4qiEcBQc3d3RZqsIKBjwORsHsQa78cqwvNljtnIuNbM,10795
|
|
16
|
-
pycodex/runtime.py,sha256=
|
|
17
|
+
pycodex/runtime.py,sha256=_-d0nMSFTlLK7UxMcGnjtktSCfVTxy8dCNl355j5D9Y,8758
|
|
17
18
|
pycodex/runtime_services.py,sha256=6PQMI4MM8F9imijaVKBIz_0ADtoKDcdYs6vQ-c4frtQ,13931
|
|
18
19
|
pycodex/prompts/collaboration_default.md,sha256=MBTmPuMubeWfZgIeFVj49wwnwD4n_o3fVYAbgWKwu6Q,955
|
|
19
20
|
pycodex/prompts/collaboration_plan.md,sha256=IzjQAA5oHJz-3FmJdOjsJ4LHq6LW1tlEYMoy09n0HKk,8777
|
|
20
21
|
pycodex/prompts/default_base_instructions.md,sha256=D65mcj6bo4CDvVom-D9cbJRJVNquo0NghKt164_fRsg,20923
|
|
21
|
-
pycodex/prompts/models.json,sha256=
|
|
22
|
+
pycodex/prompts/models.json,sha256=3J0FN5UUUj2oPBXWRR2AFo2LXB3-2PJ_5T52-F-eVh8,525055
|
|
22
23
|
pycodex/prompts/permissions/approval_policy/never.md,sha256=QceTG6wjkaJARjYr0HYV1aPnPcpGcrkRUW-smWRr6MQ,120
|
|
23
24
|
pycodex/prompts/permissions/approval_policy/on_failure.md,sha256=dfJjpXkpO6_ANdCKxbVJ8o4vyLxevrJWfKsGHTqtbkc,289
|
|
24
25
|
pycodex/prompts/permissions/approval_policy/on_request.md,sha256=hVQalzh0FAdkKzw5u-N4H7-LtC9ijVDlYsh3OKsZKzo,3661
|
|
@@ -27,10 +28,11 @@ pycodex/prompts/permissions/approval_policy/unless_trusted.md,sha256=XHpi1Lfx1iI
|
|
|
27
28
|
pycodex/prompts/permissions/sandbox_mode/danger_full_access.md,sha256=nZ7YHacBd3cAHKRZc9XClOOOnXJPXPh0WFBueh5C2D0,197
|
|
28
29
|
pycodex/prompts/permissions/sandbox_mode/read_only.md,sha256=2rAPEXsBYCcuttI5j3euS-3uv_v97catIsnhxlSQSIM,173
|
|
29
30
|
pycodex/prompts/permissions/sandbox_mode/workspace_write.md,sha256=lVN-LwrBbHqlv5yVjcd_mU8tzZW8jfKpTatJKIZu9HI,277
|
|
30
|
-
pycodex/tools/__init__.py,sha256
|
|
31
|
+
pycodex/tools/__init__.py,sha256=-d08bLB5pI0GzYKdY7OKpvj1kPk8u7xwD0_mqFoZpgY,1857
|
|
31
32
|
pycodex/tools/agent_tool_schemas.py,sha256=8cL9ml4So0H6IIeK0ZzjAo9bk3VeprDOSYPpIamOqEU,2048
|
|
32
33
|
pycodex/tools/apply_patch_tool.py,sha256=wLHk5sl_UkR7iv0yqMJBMLHbGkZlMmCuX9a0yTevzps,13802
|
|
33
34
|
pycodex/tools/base_tool.py,sha256=0aQ69ygvSYKtDq1wHXr_j7EF3NtKIfmj0A9AbB-pD4Q,5431
|
|
35
|
+
pycodex/tools/clock_tool.py,sha256=lV4gtx51nGlbYyHRan-pvdOLzQWlJePA5o6xvWvApWg,5410
|
|
34
36
|
pycodex/tools/close_agent_tool.py,sha256=79qr4ljPTvjnpH-Oe83yFTUGZdgNNnGoLZbpdqp4eOY,2031
|
|
35
37
|
pycodex/tools/code_mode_manager.py,sha256=T8RHM8tyJvHWwS6AD2g9hsaOmvApMmlWuSCRfmDVMDE,19053
|
|
36
38
|
pycodex/tools/exec_command_tool.py,sha256=KIt-pni7b2I41EMXGa1NW9YWJ1AoiWlPW_Ljlu7kLpk,4082
|
|
@@ -49,43 +51,43 @@ pycodex/tools/shell_tool.py,sha256=1m-Tcbn3His4ggyK5ec8Mkg6ihopqTvBd9F6fJlm6m4,4
|
|
|
49
51
|
pycodex/tools/spawn_agent_tool.py,sha256=7Me4Frjz7_fTpjMMFCvNmDHPOK2G1qTfXUr2Ugs61bU,6194
|
|
50
52
|
pycodex/tools/unified_exec_manager.py,sha256=YM0mKGygkprV51iAgt-A2YXJCG8Fyg3wuGGHktXuuQM,13957
|
|
51
53
|
pycodex/tools/update_plan_tool.py,sha256=UsChtCBqI1RnVnPQbByPmD2LMZXMoCwfe6NpGqP8qu0,3174
|
|
52
|
-
pycodex/tools/view_image_tool.py,sha256=
|
|
54
|
+
pycodex/tools/view_image_tool.py,sha256=TbeO3vY0GlXCaonPGTut1TiJ91Co0I7V3qHuVku9aGc,3882
|
|
53
55
|
pycodex/tools/wait_agent_tool.py,sha256=0Uj9-IrXe2dSvOtOMq-RAc0XzaidwFH5q7Mri3BXWyM,3135
|
|
54
56
|
pycodex/tools/wait_tool.py,sha256=N6IrwzMp-fr1EohF-bxSwUlVs7KvZO2vV2csm3xCmS0,3205
|
|
55
57
|
pycodex/tools/web_search_tool.py,sha256=mhiK_G6VC6K6-01KctmIpsr-BURDF8L4ZJyY6IFGlNA,964
|
|
56
58
|
pycodex/tools/write_stdin_tool.py,sha256=K--XMp-AyEdp8yQ9EseYHsK3QrGrK4AgzOyvUk4swmw,3608
|
|
57
59
|
pycodex/utils/__init__.py,sha256=p3jaERPxkimNDhmMIsm4glvKiazf3UMv1X-qoH5Zl3U,963
|
|
58
60
|
pycodex/utils/async_bridge.py,sha256=d21Pjim-nsQbSG5pJddd0WaQ03CzA3w3TINWDmmjWbg,1815
|
|
59
|
-
pycodex/utils/compactor.py,sha256=
|
|
61
|
+
pycodex/utils/compactor.py,sha256=MqsJ_wcuDGPM8vndOGamLJ8j-51ivYDMlBek-Z7zZ8k,7111
|
|
60
62
|
pycodex/utils/debug.py,sha256=JeEB5JfzYfbdG0fXlrWFmXyR1ts86fKsI_97IqgF6R0,296
|
|
61
63
|
pycodex/utils/dotenv.py,sha256=rGKmurHjm7GdP4giyjHBPpSPv2Oi45qBqDB6HG3CnfA,1866
|
|
62
64
|
pycodex/utils/get_env.py,sha256=5fNhcNhujOakWV6AS66rGW3jEA68WGpuE4YVXJZFE6U,7427
|
|
63
65
|
pycodex/utils/random_ids.py,sha256=zBphjVGc7OXk9ZNExAbxRi_bk7ipyLG491qTv7hi8jM,380
|
|
64
|
-
pycodex/utils/session_persist.py,sha256=
|
|
66
|
+
pycodex/utils/session_persist.py,sha256=X4SkBDWz9hIIETfZMkKrNUP4Lx7fi2agtGQtnXIypqo,20171
|
|
65
67
|
pycodex/utils/toolcall_visualize.py,sha256=zIqmdsOfyYaLy_P4jpKnRxDsfTgYLRBx55R8m1P_lBE,24708
|
|
66
68
|
pycodex/utils/truncation.py,sha256=B_RvfXC2-M1oKz--eQIqDLqMD0g7_J-MSQd3WD6Rh08,6110
|
|
67
|
-
pycodex/utils/visualize.py,sha256=
|
|
69
|
+
pycodex/utils/visualize.py,sha256=NfmAQdleK-yd62zJRB_oDX9SXii0wr3F3U313QlgNlU,20751
|
|
68
70
|
responses_server/__init__.py,sha256=3yPv_zeGT7P11tTnmj5kXktISLNsNW-02MUnnbiZcb0,394
|
|
69
71
|
responses_server/__main__.py,sha256=9SRp-Yw7ShGxc6DhSIXcDLKgGEdAVm3oBZ59rBOPjT0,62
|
|
70
72
|
responses_server/app.py,sha256=ack2a0otiBwq_DpsFURqLMlQzcf9oJPwo8o6iJ1fuig,7885
|
|
71
|
-
responses_server/config.py,sha256=
|
|
72
|
-
responses_server/messages_api.py,sha256=
|
|
73
|
-
responses_server/payload_processors.py,sha256=
|
|
73
|
+
responses_server/config.py,sha256=9CR9gxIhNa5yAaH6u-jgJAS1EgweTejuP6EPx0vFC4I,2792
|
|
74
|
+
responses_server/messages_api.py,sha256=4jlLHJjoa50qy4ZnVN4NjMXu52qnIngldw2g55qx-A0,18835
|
|
75
|
+
responses_server/payload_processors.py,sha256=e0IqDGOtxpJElfQNvy6IiIfnjnMOOw9uaexhAdFpU1o,3546
|
|
74
76
|
responses_server/server.py,sha256=Ko-Cqz_kW-uve091itucMklsPhEei77v-YcTjtjEdqU,2286
|
|
75
77
|
responses_server/session_store.py,sha256=ZD3cH2aEOkWaQsu5qTzcal2mThTSFQPAhAhPUN9srgI,1115
|
|
76
|
-
responses_server/stream_router.py,sha256=
|
|
77
|
-
responses_server/trajectory_dump.py,sha256=
|
|
78
|
+
responses_server/stream_router.py,sha256=3aEPqagupgP-_fiI6MC9C5IAnf3m7RAmj11NA1MAx1E,40906
|
|
79
|
+
responses_server/trajectory_dump.py,sha256=t9WO5h40h5MSs2bB_b6xdct84YUWQlp-5CiY_071Lhg,4006
|
|
78
80
|
responses_server/tools/__init__.py,sha256=ivsBSEy0SBUhY-Uea5v1XMLXShkwHdCVl0id-1FwdZg,150
|
|
79
81
|
responses_server/tools/custom_adapter.py,sha256=LxO7ldydvR-GWachDz8GKC0Q8KGGFoFPbZxM0QvxuZ0,8350
|
|
80
82
|
responses_server/tools/web_search.py,sha256=pm4ZUiHUfxc0bGY1kEvt-BCzDrZIyP24xzPUcga2ul0,8908
|
|
81
83
|
workspace_server/__init__.py,sha256=PRM4ONODb6hxjBUHkBD4TprE_pkTCIlT9sfftK5ic6M,866
|
|
82
84
|
workspace_server/__main__.py,sha256=9SRp-Yw7ShGxc6DhSIXcDLKgGEdAVm3oBZ59rBOPjT0,62
|
|
83
|
-
workspace_server/app.py,sha256=
|
|
84
|
-
workspace_server/workspace.html,sha256=
|
|
85
|
+
workspace_server/app.py,sha256=vRhnBzWRVRKxVrcLY2LLHssCaAw9tsVunCMUqE7697U,56797
|
|
86
|
+
workspace_server/workspace.html,sha256=zfnPejztriVQ3vyYSwAX5rQnUEM17fExDhMQRDuYklA,48598
|
|
85
87
|
workspace_server/workspaces.html,sha256=GuYlFiOm1RJINrCKncClHt24D5i7XNABiP9C0xGxBA0,15679
|
|
86
|
-
workspace_server/workspaces.py,sha256=
|
|
87
|
-
python_codex-0.2.
|
|
88
|
-
python_codex-0.2.
|
|
89
|
-
python_codex-0.2.
|
|
90
|
-
python_codex-0.2.
|
|
91
|
-
python_codex-0.2.
|
|
88
|
+
workspace_server/workspaces.py,sha256=4sZ7YDvsOeqCqO2azJkJD5Lw5BtQFcpHol4f7ZCWfi8,20393
|
|
89
|
+
python_codex-0.2.7.dist-info/METADATA,sha256=7YqrDbroneodIA_xL4ilQFjA5X4erzKTdOHGCgM2rYk,20124
|
|
90
|
+
python_codex-0.2.7.dist-info/WHEEL,sha256=KGYbc1zXlYddvwxnNty23BeaKzh7YuoSIvIMO4jEhvw,87
|
|
91
|
+
python_codex-0.2.7.dist-info/entry_points.txt,sha256=vkV2UWCtEKvQNMJuPNjt8HyBKiwp83JyqBatrBNGDp8,80
|
|
92
|
+
python_codex-0.2.7.dist-info/licenses/LICENSE,sha256=0X8ifk312hYAORM4hlzg8wVSEXYKNmiPgWlB1YIy2Nw,10926
|
|
93
|
+
python_codex-0.2.7.dist-info/RECORD,,
|
responses_server/config.py
CHANGED
|
@@ -5,6 +5,9 @@ import urllib.parse
|
|
|
5
5
|
import typing
|
|
6
6
|
|
|
7
7
|
|
|
8
|
+
DEFAULT_OUTCOMMING_TIMEOUT_SECONDS = 300.0
|
|
9
|
+
|
|
10
|
+
|
|
8
11
|
@dataclass(frozen=True, )
|
|
9
12
|
class CompatServerConfig:
|
|
10
13
|
host: 'str' = "127.0.0.1"
|
|
@@ -13,7 +16,7 @@ class CompatServerConfig:
|
|
|
13
16
|
outcomming_api: 'str' = "chat_completions"
|
|
14
17
|
outcomming_api_key_env: 'typing.Union[str, None]' = None
|
|
15
18
|
model_provider: 'typing.Union[str, None]' = None
|
|
16
|
-
timeout_seconds: 'float' =
|
|
19
|
+
timeout_seconds: 'float' = DEFAULT_OUTCOMMING_TIMEOUT_SECONDS
|
|
17
20
|
|
|
18
21
|
def outcomming_api_key(self) -> 'typing.Union[str, None]':
|
|
19
22
|
if self.outcomming_api_key_env is None:
|
responses_server/messages_api.py
CHANGED
|
@@ -193,12 +193,61 @@ def saw_message_stop(state: 'typing.Dict[str, object]') -> 'bool':
|
|
|
193
193
|
|
|
194
194
|
|
|
195
195
|
def _build_text_blocks(raw_content: 'object') -> 'typing.List[typing.Dict[str, object]]':
|
|
196
|
+
if isinstance(raw_content, list):
|
|
197
|
+
blocks: 'typing.List[typing.Dict[str, object]]' = []
|
|
198
|
+
for raw_part in raw_content:
|
|
199
|
+
if not isinstance(raw_part, dict):
|
|
200
|
+
raise MessagesAPIAdapterError("message content parts must be objects")
|
|
201
|
+
part_type = str(raw_part.get("type", "")).strip()
|
|
202
|
+
if part_type == "text":
|
|
203
|
+
text = str(raw_part.get("text", "") or "")
|
|
204
|
+
if text:
|
|
205
|
+
blocks.append({"type": "text", "text": text})
|
|
206
|
+
continue
|
|
207
|
+
if part_type == "image_url":
|
|
208
|
+
blocks.append(_build_image_block(raw_part))
|
|
209
|
+
continue
|
|
210
|
+
raise MessagesAPIAdapterError(
|
|
211
|
+
f"unsupported outcomming content part type for messages API: {part_type!r}"
|
|
212
|
+
)
|
|
213
|
+
return blocks
|
|
214
|
+
|
|
196
215
|
text = str(raw_content or "")
|
|
197
216
|
if not text:
|
|
198
217
|
return []
|
|
199
218
|
return [{"type": "text", "text": text}]
|
|
200
219
|
|
|
201
220
|
|
|
221
|
+
def _build_image_block(
|
|
222
|
+
raw_part: 'typing.Dict[str, object]',
|
|
223
|
+
) -> 'typing.Dict[str, object]':
|
|
224
|
+
image_url = raw_part.get("image_url") or {}
|
|
225
|
+
if not isinstance(image_url, dict):
|
|
226
|
+
raise MessagesAPIAdapterError("`image_url` content parts must be objects")
|
|
227
|
+
url = str(image_url.get("url", "") or "").strip()
|
|
228
|
+
if not url:
|
|
229
|
+
raise MessagesAPIAdapterError(
|
|
230
|
+
"`image_url` content parts must carry a non-empty `url`"
|
|
231
|
+
)
|
|
232
|
+
if not url.startswith("data:"):
|
|
233
|
+
return {"type": "image", "source": {"type": "url", "url": url}}
|
|
234
|
+
|
|
235
|
+
header, _, data = url.partition(",")
|
|
236
|
+
media_type = header[len("data:"):].split(";")[0].strip()
|
|
237
|
+
if not media_type or not header.endswith(";base64"):
|
|
238
|
+
raise MessagesAPIAdapterError(
|
|
239
|
+
"`image_url` data URLs must be base64 encoded with a media type"
|
|
240
|
+
)
|
|
241
|
+
return {
|
|
242
|
+
"type": "image",
|
|
243
|
+
"source": {
|
|
244
|
+
"type": "base64",
|
|
245
|
+
"media_type": media_type,
|
|
246
|
+
"data": data,
|
|
247
|
+
},
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
|
|
202
251
|
def _build_assistant_blocks(
|
|
203
252
|
raw_message: 'typing.Dict[str, object]',
|
|
204
253
|
) -> 'typing.List[typing.Dict[str, object]]':
|
|
@@ -28,6 +28,7 @@ class OutgoingRequest(TypedDict):
|
|
|
28
28
|
model: 'str'
|
|
29
29
|
messages: 'typing.List[ChatMessage]'
|
|
30
30
|
stream: 'bool'
|
|
31
|
+
chat_template_kwargs: 'Optional[typing.Dict[str, object]]'
|
|
31
32
|
max_tokens: 'Optional[int]'
|
|
32
33
|
tools: 'Optional[typing.List[typing.Dict[str, object]]]'
|
|
33
34
|
tool_choice: 'Optional[object]'
|