python-codex 0.2.6__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.6.dist-info → python_codex-0.2.7.dist-info}/METADATA +29 -15
- {python_codex-0.2.6.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 +7 -4
- workspace_server/workspaces.py +34 -24
- {python_codex-0.2.6.dist-info → python_codex-0.2.7.dist-info}/WHEEL +0 -0
- {python_codex-0.2.6.dist-info → python_codex-0.2.7.dist-info}/entry_points.txt +0 -0
- {python_codex-0.2.6.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'
|
|
@@ -254,17 +255,29 @@ Current behavior:
|
|
|
254
255
|
later steer text is appended to the next model request's `input` in order;
|
|
255
256
|
for explicit queueing, use `/queue <message>`, which prints
|
|
256
257
|
`[steer] queued: ...` and later `[steer] inserted: ...`
|
|
257
|
-
- the default
|
|
258
|
-
|
|
259
|
-
`
|
|
260
|
-
`
|
|
261
|
-
`
|
|
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
|
|
262
271
|
- `--vllm-endpoint http://host:port` automatically launches a local
|
|
263
272
|
`responses_server` compatibility layer; when the URL path is empty it is
|
|
264
273
|
normalized to `/v1`, and `/responses` requests are still forwarded to the
|
|
265
|
-
downstream `/v1/chat/completions` endpoint.
|
|
266
|
-
|
|
267
|
-
|
|
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
|
|
268
281
|
historical `reasoning` items are replayed into downstream assistant messages
|
|
269
282
|
via the `reasoning` field. Streaming token usage is also requested from vLLM
|
|
270
283
|
and forwarded to the final `response.completed.response.usage`. If a
|
|
@@ -408,6 +421,7 @@ Upstream low-frequency / special-mode tools not yet modeled separately:
|
|
|
408
421
|
|
|
409
422
|
Repository-specific compatibility / transition tools:
|
|
410
423
|
|
|
424
|
+
- [x] `clock` - pycodex periodic Agent wake-up extension.
|
|
411
425
|
- [x] `exec` - current local approximation of code mode.
|
|
412
426
|
- [x] `wait` - current local approximation of code-mode waiting behavior.
|
|
413
427
|
|
|
@@ -422,8 +436,8 @@ Repository-specific compatibility / transition tools:
|
|
|
422
436
|
matches upstream.
|
|
423
437
|
- [x] `AGENTS.md` + `<environment_context>` injection alignment - context
|
|
424
438
|
assembly order matches upstream.
|
|
425
|
-
- [x] non-interactive `exec` tool subset alignment - the
|
|
426
|
-
has converged
|
|
439
|
+
- [x] non-interactive `exec` upstream tool subset alignment - the aligned
|
|
440
|
+
subset has converged; pycodex additionally exposes `clock`.
|
|
427
441
|
- [x] `include = ["reasoning.encrypted_content"]` - reasoning include field is
|
|
428
442
|
aligned.
|
|
429
443
|
- [x] `prompt_cache_key` - request-level prompt cache key is implemented.
|
|
@@ -432,8 +446,8 @@ Repository-specific compatibility / transition tools:
|
|
|
432
446
|
- [x] `originator` - mode-aware originator header is implemented.
|
|
433
447
|
- [x] exact `user-agent` string alignment - aligned on the non-interactive
|
|
434
448
|
`exec` path.
|
|
435
|
-
- [x] field-by-field exec-mode tool schema alignment -
|
|
436
|
-
|
|
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.
|
|
437
451
|
- [ ] full interactive-mode and non-`exec` behavior alignment - the non-exec
|
|
438
452
|
first-turn context is now on the `codex-tui` path, but continuous REPL
|
|
439
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]'
|