python-codex 0.2.6__tar.gz → 0.2.7__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.
- {python_codex-0.2.6 → python_codex-0.2.7}/.github/workflows/publish.yml +6 -2
- {python_codex-0.2.6 → python_codex-0.2.7}/.github/workflows/test.yml +6 -2
- {python_codex-0.2.6 → python_codex-0.2.7}/AGENTS.md +5 -3
- {python_codex-0.2.6 → python_codex-0.2.7}/PKG-INFO +29 -15
- {python_codex-0.2.6 → python_codex-0.2.7}/README.md +25 -12
- {python_codex-0.2.6 → python_codex-0.2.7}/README_ZH.md +8 -4
- {python_codex-0.2.6 → python_codex-0.2.7}/docs/ALIGNMENT.md +11 -6
- {python_codex-0.2.6 → python_codex-0.2.7}/docs/CONTEXT.md +5 -1
- {python_codex-0.2.6 → python_codex-0.2.7}/docs/responses_server/README.md +20 -4
- {python_codex-0.2.6 → python_codex-0.2.7}/packages/pycodex-ws/pyproject.toml +2 -2
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/__init__.py +4 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/agent.py +54 -14
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/cli.py +110 -36
- python_codex-0.2.7/pycodex/image_utils.py +79 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/model.py +7 -1
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/portable.py +14 -7
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/prompts/models.json +170 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/runtime.py +2 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/__init__.py +3 -0
- python_codex-0.2.7/pycodex/tools/clock_tool.py +168 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/view_image_tool.py +7 -9
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/utils/compactor.py +17 -2
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/utils/session_persist.py +50 -2
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/utils/visualize.py +11 -4
- {python_codex-0.2.6 → python_codex-0.2.7}/pyproject.toml +4 -3
- {python_codex-0.2.6 → python_codex-0.2.7}/responses_server/config.py +4 -1
- {python_codex-0.2.6 → python_codex-0.2.7}/responses_server/messages_api.py +49 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/responses_server/payload_processors.py +1 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/responses_server/stream_router.py +108 -22
- {python_codex-0.2.6 → python_codex-0.2.7}/responses_server/trajectory_dump.py +18 -2
- {python_codex-0.2.6 → python_codex-0.2.7}/tests/TESTS.md +18 -1
- {python_codex-0.2.6 → python_codex-0.2.7}/tests/responses_server/test_server.py +291 -9
- {python_codex-0.2.6 → python_codex-0.2.7}/tests/test_agent.py +112 -3
- {python_codex-0.2.6 → python_codex-0.2.7}/tests/test_builtin_tools.py +87 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/tests/test_cli.py +106 -6
- {python_codex-0.2.6 → python_codex-0.2.7}/tests/test_model.py +25 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/tests/test_portable.py +10 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/tests/test_workspace_server.py +68 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/tools/feishu_oauth.py +15 -1
- {python_codex-0.2.6 → python_codex-0.2.7}/workspace_server/app.py +12 -12
- {python_codex-0.2.6 → python_codex-0.2.7}/workspace_server/workspace.html +7 -4
- {python_codex-0.2.6 → python_codex-0.2.7}/workspace_server/workspaces.py +34 -24
- {python_codex-0.2.6 → python_codex-0.2.7}/.gitignore +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/LICENSE +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/packages/pycodex-ws/README.md +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/packages/pycodex-ws/pycodex_ws_meta/__init__.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/collaboration.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/compat.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/context.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/doctor.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/feishu_card.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/feishu_link.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/interactive_session.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/model_metadata.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/portable_server.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/prompts/collaboration_default.md +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/prompts/collaboration_plan.md +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/prompts/default_base_instructions.md +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/prompts/permissions/approval_policy/never.md +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/prompts/permissions/approval_policy/on_failure.md +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/prompts/permissions/approval_policy/on_request.md +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/prompts/permissions/approval_policy/on_request_rule_request_permission.md +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/prompts/permissions/approval_policy/unless_trusted.md +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/prompts/permissions/sandbox_mode/danger_full_access.md +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/prompts/permissions/sandbox_mode/read_only.md +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/prompts/permissions/sandbox_mode/workspace_write.md +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/protocol.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/runtime_services.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/agent_tool_schemas.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/apply_patch_tool.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/base_tool.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/close_agent_tool.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/code_mode_manager.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/exec_command_tool.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/exec_runtime.js +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/exec_tool.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/grep_files_tool.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/ipython_tool.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/list_dir_tool.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/read_file_tool.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/request_permissions_tool.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/request_user_input_tool.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/resume_agent_tool.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/send_input_tool.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/shell_command_tool.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/shell_tool.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/spawn_agent_tool.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/unified_exec_manager.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/update_plan_tool.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/wait_agent_tool.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/wait_tool.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/web_search_tool.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/tools/write_stdin_tool.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/utils/__init__.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/utils/async_bridge.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/utils/debug.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/utils/dotenv.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/utils/get_env.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/utils/random_ids.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/utils/toolcall_visualize.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/pycodex/utils/truncation.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/responses_server/__init__.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/responses_server/__main__.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/responses_server/app.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/responses_server/server.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/responses_server/session_store.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/responses_server/tools/__init__.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/responses_server/tools/custom_adapter.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/responses_server/tools/web_search.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/tests/__init__.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/tests/compare_request_user_input_roundtrip.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/tests/compare_steer_request_bodies.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/tests/compare_tool_schemas.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/tests/fake_responses_server.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/tests/fakes.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/tests/responses_server/fake_chat_completions_server.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/tests/test_async_bridge.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/tests/test_compactor.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/tests/test_context.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/tests/test_doctor.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/tests/test_fake_responses_server.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/tests/test_feishu_card.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/tests/test_feishu_link.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/tests/test_ipython_tool.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/tests/test_packaging.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/tests/test_py36_syntax.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/tests/test_visualize.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/workspace_server/__init__.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/workspace_server/__main__.py +0 -0
- {python_codex-0.2.6 → python_codex-0.2.7}/workspace_server/workspaces.html +0 -0
|
@@ -54,7 +54,10 @@ jobs:
|
|
|
54
54
|
steps:
|
|
55
55
|
- name: Install system dependencies
|
|
56
56
|
run: |
|
|
57
|
-
|
|
57
|
+
printf '%s\n' \
|
|
58
|
+
'deb [check-valid-until=no] https://archive.debian.org/debian bullseye main' \
|
|
59
|
+
> /etc/apt/sources.list
|
|
60
|
+
apt-get -o APT::Update::Error-Mode=any update
|
|
58
61
|
apt-get install -y --no-install-recommends git nodejs npm
|
|
59
62
|
|
|
60
63
|
- name: Check out repository
|
|
@@ -80,7 +83,8 @@ jobs:
|
|
|
80
83
|
"requests>=2.27.1" \
|
|
81
84
|
"prompt-toolkit>=3.0.36,<3.1" \
|
|
82
85
|
"loguru>=0.7.3,<1" \
|
|
83
|
-
"
|
|
86
|
+
"pycryptodomex>=3.20" \
|
|
87
|
+
"pillow>=8.4.0,<9" \
|
|
84
88
|
"fastapi>=0.83,<0.84" \
|
|
85
89
|
"uvicorn>=0.16,<0.17" \
|
|
86
90
|
"pytest>=6.2.5,<7" \
|
|
@@ -44,7 +44,10 @@ jobs:
|
|
|
44
44
|
steps:
|
|
45
45
|
- name: Install system dependencies
|
|
46
46
|
run: |
|
|
47
|
-
|
|
47
|
+
printf '%s\n' \
|
|
48
|
+
'deb [check-valid-until=no] https://archive.debian.org/debian bullseye main' \
|
|
49
|
+
> /etc/apt/sources.list
|
|
50
|
+
apt-get -o APT::Update::Error-Mode=any update
|
|
48
51
|
apt-get install -y --no-install-recommends git nodejs npm
|
|
49
52
|
|
|
50
53
|
- name: Check out repository
|
|
@@ -70,7 +73,8 @@ jobs:
|
|
|
70
73
|
"requests>=2.27.1" \
|
|
71
74
|
"prompt-toolkit>=3.0.36,<3.1" \
|
|
72
75
|
"loguru>=0.7.3,<1" \
|
|
73
|
-
"
|
|
76
|
+
"pycryptodomex>=3.20" \
|
|
77
|
+
"pillow>=8.4.0,<9" \
|
|
74
78
|
"fastapi>=0.83,<0.84" \
|
|
75
79
|
"uvicorn>=0.16,<0.17" \
|
|
76
80
|
"pytest>=6.2.5,<7" \
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
- `responses_server` 如果要兼容下游 `/v1/messages`,也优先保持这条边界:内部继续用 canonical chat request / chat-like chunk 流,只有真正发请求和读取 SSE 时才做 messages 适配,这样 tool hydration、mock `web_search` follow-up、provider payload post-process 都能复用。
|
|
20
20
|
- 真实 vLLM `0.19.0` 的 `/v1/messages` 会对缺失 `max_tokens` 直接返回 `400`;messages 适配层必须总是补这个字段。当前约定是优先透传请求里的 `max_output_tokens`/`max_tokens`,否则回退到默认 `32000`。
|
|
21
21
|
- 对 vLLM chat-completions 打开 `return_token_ids=true` 时,streaming `prompt_token_ids` 只出现在首个 chunk,后续每个 chunk 的 `choices[*].token_ids` 都是 decode delta;要在 `responses_server` 侧导出 trajectory 时,按“首个 `prompt_token_ids` + 按序拼接所有 chunk 的 `token_ids`”重建即可。
|
|
22
|
-
- `pycodex` 默认是最小交互 CLI;无 prompt 时进入 REPL,并通过 `AgentRuntime` 跑外层提交循环。当前会显示最小事件流、assistant 流式输出、简单 title/history(`/title`, `/history
|
|
22
|
+
- `pycodex` 默认是最小交互 CLI;无 prompt 时进入 REPL,并通过 `AgentRuntime` 跑外层提交循环。当前会显示最小事件流、assistant 流式输出、简单 title/history(`/title`, `/history`),并默认注册上游对齐工具子集和明确标记的本地扩展。
|
|
23
23
|
- Web workspace lives in the standalone `workspace_server/` package and is launched with `pycodex-ws --listen <host:port> --board <html>`, not through `pycodex` CLI dispatch. CLI and web share `pycodex.interactive_session.run_interactive_session`; slash-command semantics such as `/resume`, `/compact`, `/model`, and `/link` belong to that shared interactive shell loop, while workspace only supplies a web view/input adapter and tab/session lifecycle.
|
|
24
24
|
- Relative images in board HTML resolve to HTTP siblings such as `/w/<workspace-id>/<image>` (or `/<image>` in single-workspace mode), not to `/board/...`. Keep the image fallback routes after concrete workspace/API routes, resolve files from the board's directory, and reject non-`image/*` files or paths that escape that directory.
|
|
25
25
|
- 交互 CLI 的事件流展示优先表达用户可感知的阶段(例如工具开始/完成、模型回看工具结果),不要直接把内部 `iteration` 计数暴露成主要状态文案;`iterations` 应继续保留在 `TurnResult` 等程序化结果里。
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
- `README.md` 和 `docs/` 属于对齐工作的一部分:只要实现状态、对齐结论或使用方式发生实质变化,就应及时更新,不要让文档滞后于当前代码。
|
|
34
34
|
- 新工具必须继承 `BaseTool`,然后通过 `ToolRegistry.register(tool_instance)` 接入;不要再给 registry 传散装 name/description/handler 参数。
|
|
35
35
|
- request/tool schema 对齐应落在实际建模层(`ToolSpec` / `BaseTool` / request builder)本身,不要再引入 prompt 级别的 `serialized_tools` 旁路覆盖。
|
|
36
|
-
-
|
|
36
|
+
- 当前已接入的默认工具集:`shell`、`shell_command`、`exec_command`、`write_stdin`、`clock`、`exec`、`wait`、`web_search`、`update_plan`、`request_user_input`、`request_permissions`、`spawn_agent`、`send_input`、`resume_agent`、`wait_agent`、`close_agent`、`apply_patch`、`grep_files`、`read_file`、`list_dir`、`view_image`。其中 `clock` 是明确的 pycodex 扩展;其余新增工具仍优先逐个对齐原版 Codex。
|
|
37
37
|
- `exec_command` / `write_stdin` 的 unified-exec 对齐要注意两层默认截断语义:省略 `max_output_tokens` 时也要按 upstream 默认 `10_000` token 预算裁剪 tool response;同时长时间未轮询的未读输出缓冲不能无限累积,需保留 upstream 同款 `1 MiB` head/tail。
|
|
38
38
|
- 当前协议层已经支持两类原版工具载荷:普通 function tools,以及 `apply_patch` 这类 freeform/custom tools;工具结果也支持结构化 `input_image` content items,用于 `view_image` 这类会把图片喂回模型的工具。
|
|
39
39
|
- 当前本地 runtime 已支持一个最小的 in-process sub-agent 管理层:`spawn_agent` / `send_input` / `resume_agent` / `wait_agent` / `close_agent` 通过共享的 `SubAgentManager` 驱动新的 `AgentRuntime` 实例,不依赖 CLI harness 自带的多 agent 基础设施。
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
- `request_user_input` 对齐上游时要注意两层:Default mode 默认返回固定错误 `request_user_input is unavailable in Default mode`;Plan mode happy path 则要求每个问题都有非空 `options`、handler 会补 `isOther=true`,并把结构化答案序列化成 JSON 字符串加 `success=true` 塞回 `function_call_output`。
|
|
42
42
|
- 在 `/data/pycodex` 本机已安装的 `codex-cli 0.115.0` 上,用 `tests/compare_request_user_input_roundtrip.py` 做 Plan-mode deterministic proxy live capture 时,upstream 的 second request `function_call_output` 当前不带 `success`;而 GitHub `openai/codex` `main` 源码里的 `FunctionCallOutputPayload` / `request_user_input` handler 支持传递 `success`。写对齐结论时要明确区分“installed CLI live capture”与“upstream main 源码建模”。
|
|
43
43
|
- 当前 `exec` / `wait` 已有一个最小 code-mode 实现:底层通过 Node 子进程运行 JavaScript,自带 `text` / `image` / `store` / `load` / `exit` / `notify` / `yield_control` helper,并允许通过 `tools.<name>(...)` 调回本地已注册工具;`web_search` 当前作为 Responses API provider-native tool declaration 暴露给模型,不经过本地 ToolRegistry 执行。
|
|
44
|
-
-
|
|
44
|
+
- 除明确记录的 pycodex 扩展(当前为 `clock`)外,我们支持的 tools 必须和原版 Codex 内置 tools 一一对应:名称、定位、参数形状、交互模型、输出语义都应尽量对齐;不要混合多个原版工具的语义做一个“折中工具”。
|
|
45
45
|
- 代码风格上不要使用 `*` 定义 keyword-only 参数;接口默认允许位置参数。
|
|
46
46
|
- runtime 包和会在 import 阶段执行的测试辅助代码需要保持 Python 3.6.2 语法兼容;不要引入 walrus `:=`、`match` 等仅 3.8+/3.10+ 可解析的新语法,哪怕分支在运行时不会走到。
|
|
47
47
|
- 本仓库使用 `uv`;本地默认没有预装测试依赖,开始工作前先跑 `uv sync --dev`,验证用 `uv run pytest`。
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
- 对接真实 `~/.codex/sessions/.../rollout-*.jsonl` 时,不要假设它一定是严格的一行一个 JSON object:本机样本可能包含 pretty-printed 多行对象,且文件尾部偶尔带未完成记录。恢复历史时用 concatenated-JSON 方式读取,并容忍尾部残缺。
|
|
63
63
|
- `pycodex` 本地 session 保存现在也按上游思路走:新 session 一开始就分配稳定的 uuidv7 thread/session id,并把历史增量追加到 `CODEX_HOME/sessions/.../rollout-*.jsonl`;`/resume` 列表应只展示至少有真实 user message 的 rollout,避免空白新 session 污染恢复列表。
|
|
64
64
|
- auto-compact 对齐上游配置名 `model_auto_compact_token_limit`;为空时关闭,触发依据是最近一次模型上报的 `usage.total_tokens`,pre-turn 压缩上一轮历史,mid-turn 压缩工具 follow-up 前的当前历史,并继续复用现有 compacted rollout 记录。
|
|
65
|
+
- Local compact templates intentionally diverge from upstream: the prompt explicitly preserves the latest real user request/language, and the summary prefix tells the next model to continue instead of merely acknowledging the handoff. Do not mechanically replace these additions with `codex-rs/core/templates/compact/{prompt.md,summary_prefix.md}`. Real `compacted` rollout entries remain append-only checkpoints whose `replacement_history` can be summary-only; the old transcript stays on disk for audit but is not part of the next active prompt.
|
|
65
66
|
- Responses streaming 里的 `response.incomplete` 不是连接断开:不要让 `ResponsesModelClient` 把它当 retryable incomplete stream 反复重连。普通 turn 应明确报 `response.incomplete`;compact 请求如果已经收到 assistant partial summary,可以用这个 partial summary 完成 replacement history,避免 midturn auto-compact 卡在 5 次 retry。
|
|
66
67
|
- 上游 Codex Responses 请求当前不传模型级 `max_output_tokens`,也没有读取 `model_max_output_tokens` 这个 config key;这个名字在上游主要用于工具输出截断,不要为了上游对齐把它加进模型请求。
|
|
67
68
|
- `service_tier = "fast"` 在 config 中是 Fast mode 的持久写法,但 Responses wire 值是 `priority`;只对模型 metadata 的 `service_tiers` 明确包含该 id 的模型发送,`default` 和不支持的 tier 都省略。
|
|
@@ -69,4 +70,5 @@
|
|
|
69
70
|
- Feishu card tests read `~/.codex/.feishu_refresh_token` through production code; when running `tests/test_feishu_card.py` locally, isolate HOME (for example `HOME=/tmp/pycodex-empty-home env -u VIRTUAL_ENV uv run pytest tests/test_feishu_card.py tests/test_feishu_link.py`) unless the test itself controls `HOME`.
|
|
70
71
|
- `lark_oapi.ws.client` creates a module-level asyncio `loop` at import time and `Client.start()` always uses that global. For `/link` long-connection listeners, bind that SDK global to a listener-thread-owned loop before constructing the client, and stop it through private `_disconnect()` plus `loop.stop()` on `/unlink`; otherwise unlink/link can reuse a still-running SDK loop and fail with `RuntimeError: This event loop is already running`.
|
|
71
72
|
- `exec_command` background completion auto-resume is intentionally Agent-idle-only: when a session exits, it may call `Agent.maybe_invoke(...)` and start a synthetic `<exec_command_completed>` turn only if that Agent is not already running a turn. Do not enqueue/cache these events in `CliSubmissionQueue`; direct Agent/IPython use should share the same Agent-level hook.
|
|
73
|
+
- `clock` is a per-Agent/session pycodex extension with one global period value. Reuse `Agent.maybe_invoke(...)` for `<clock_tick>` wake-ups and include the timezone-aware ISO 8601 `current_time` captured when the timer expires: cancel the pending countdown when any turn starts, restart it only after a successful reply, and cancel it on `clock(null)` or runtime shutdown. Terminal events expose only aggregate `background_work_count`, which includes running exec sessions and an enabled clock after a successful reply; CLI/workspace idle status should use that single field. Do not add a second submission queue or shell-based sleep loop.
|
|
72
74
|
- The tool description JSON fallbacks (`pycodex/prompts/exec_tools.json` and `pycodex/prompts/subagent_tools.json`) were deleted after moving schemas into class-level `BaseTool` specs. `ToolSpec.serialize()` intentionally skips function-tool `output_schema`, matching upstream `ResponsesApiTool.output_schema #[serde(skip)]`; keep output schemas as local metadata only unless upstream wire format changes.
|
|
@@ -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.
|
|
@@ -233,17 +233,29 @@ Current behavior:
|
|
|
233
233
|
later steer text is appended to the next model request's `input` in order;
|
|
234
234
|
for explicit queueing, use `/queue <message>`, which prints
|
|
235
235
|
`[steer] queued: ...` and later `[steer] inserted: ...`
|
|
236
|
-
- the default
|
|
237
|
-
|
|
238
|
-
`
|
|
239
|
-
`
|
|
240
|
-
`
|
|
236
|
+
- the default local tool set includes the upstream-aligned subset plus the
|
|
237
|
+
pycodex `clock` extension: `shell`, `shell_command`, `exec_command`,
|
|
238
|
+
`write_stdin`, `clock`, `exec`, `wait`, `web_search`, `update_plan`,
|
|
239
|
+
`request_user_input`, `request_permissions`, `spawn_agent`, `send_input`,
|
|
240
|
+
`resume_agent`, `wait_agent`, `close_agent`, `apply_patch`, `grep_files`,
|
|
241
|
+
`read_file`, `list_dir`, `view_image`
|
|
242
|
+
- `clock(period_m)` sets one periodic clock for the current Agent session;
|
|
243
|
+
`null` cancels it. The countdown restarts after each reply and wakes the
|
|
244
|
+
Agent with a `<clock_tick>` message containing the current timezone-aware
|
|
245
|
+
time when it expires.
|
|
246
|
+
- while a background command or clock is pending, the idle status is
|
|
247
|
+
`idle: sleeping`
|
|
248
|
+
- only the active workspace tab shows its close button
|
|
241
249
|
- `--vllm-endpoint http://host:port` automatically launches a local
|
|
242
250
|
`responses_server` compatibility layer; when the URL path is empty it is
|
|
243
251
|
normalized to `/v1`, and `/responses` requests are still forwarded to the
|
|
244
|
-
downstream `/v1/chat/completions` endpoint.
|
|
245
|
-
|
|
246
|
-
|
|
252
|
+
downstream `/v1/chat/completions` endpoint. This local compat path always
|
|
253
|
+
uses the canonical Responses request shape, even when the selected model's
|
|
254
|
+
metadata enables `responses_lite`. With `--vllm-endpoint`, startup also reads
|
|
255
|
+
`/v1/models` and uses the last returned model id for the downstream request.
|
|
256
|
+
For `model_provider = "vllm"`, reasoning is preserved across this path:
|
|
257
|
+
chat chunks with `reasoning` or `reasoning_content` are translated back into
|
|
258
|
+
Responses `reasoning` items, and
|
|
247
259
|
historical `reasoning` items are replayed into downstream assistant messages
|
|
248
260
|
via the `reasoning` field. Streaming token usage is also requested from vLLM
|
|
249
261
|
and forwarded to the final `response.completed.response.usage`. If a
|
|
@@ -387,6 +399,7 @@ Upstream low-frequency / special-mode tools not yet modeled separately:
|
|
|
387
399
|
|
|
388
400
|
Repository-specific compatibility / transition tools:
|
|
389
401
|
|
|
402
|
+
- [x] `clock` - pycodex periodic Agent wake-up extension.
|
|
390
403
|
- [x] `exec` - current local approximation of code mode.
|
|
391
404
|
- [x] `wait` - current local approximation of code-mode waiting behavior.
|
|
392
405
|
|
|
@@ -401,8 +414,8 @@ Repository-specific compatibility / transition tools:
|
|
|
401
414
|
matches upstream.
|
|
402
415
|
- [x] `AGENTS.md` + `<environment_context>` injection alignment - context
|
|
403
416
|
assembly order matches upstream.
|
|
404
|
-
- [x] non-interactive `exec` tool subset alignment - the
|
|
405
|
-
has converged
|
|
417
|
+
- [x] non-interactive `exec` upstream tool subset alignment - the aligned
|
|
418
|
+
subset has converged; pycodex additionally exposes `clock`.
|
|
406
419
|
- [x] `include = ["reasoning.encrypted_content"]` - reasoning include field is
|
|
407
420
|
aligned.
|
|
408
421
|
- [x] `prompt_cache_key` - request-level prompt cache key is implemented.
|
|
@@ -411,8 +424,8 @@ Repository-specific compatibility / transition tools:
|
|
|
411
424
|
- [x] `originator` - mode-aware originator header is implemented.
|
|
412
425
|
- [x] exact `user-agent` string alignment - aligned on the non-interactive
|
|
413
426
|
`exec` path.
|
|
414
|
-
- [x] field-by-field exec-mode tool schema alignment -
|
|
415
|
-
|
|
427
|
+
- [x] field-by-field upstream exec-mode tool schema alignment - aligned tools
|
|
428
|
+
use class-level specs; `clock` is documented separately as an extension.
|
|
416
429
|
- [ ] full interactive-mode and non-`exec` behavior alignment - the non-exec
|
|
417
430
|
first-turn context is now on the `codex-tui` path, but continuous REPL
|
|
418
431
|
multi-turn behavior is not fully verified yet.
|
|
@@ -157,8 +157,11 @@ pycodex doctor
|
|
|
157
157
|
刚结束的 history fork 成一个不落盘的临时 follow-up 会话,并把文件内容作为下一条
|
|
158
158
|
user 指令提交;适合做 Feishu 通知这类副作用收尾动作
|
|
159
159
|
- 交互模式默认支持 steer:普通输入会走 runtime 的 steer 路径,当前请求会在下一个安全边界尽快停下,后续 steer 文本会按顺序并入下一次模型请求的 `input`;如需明确排队可用 `/queue <message>`,会打印 `[steer] queued: ...`,随后等该 turn 真正开始时再打印 `[steer] inserted: ...`
|
|
160
|
-
-
|
|
161
|
-
-
|
|
160
|
+
- 当前默认工具集由上游对齐子集和 pycodex 的 `clock` 扩展组成:`shell`、`shell_command`、`exec_command`、`write_stdin`、`clock`、`exec`、`wait`、`web_search`、`update_plan`、`request_user_input`、`request_permissions`、`spawn_agent`、`send_input`、`resume_agent`、`wait_agent`、`close_agent`、`apply_patch`、`grep_files`、`read_file`、`list_dir`、`view_image`
|
|
161
|
+
- `clock(period_m)` 为当前 Agent session 设置一个周期计时器,传 `null` 取消;每次回复后重新计时,到期后用包含当前时区时间的 `<clock_tick>` 消息唤醒 Agent
|
|
162
|
+
- 后台命令或 clock 正在等待时,空闲状态统一显示为 `idle: sleeping`
|
|
163
|
+
- workspace 只在当前活动 tab 上显示关闭按钮
|
|
164
|
+
- `--vllm-endpoint http://host:port` 会自动拉起一个本地 `responses_server` compat 层;当 path 为空时会内部补 `/v1`,继续把 `/responses` 请求转到下游 `/v1/chat/completions`。这条本地 compat 路径始终使用标准 Responses request shape,即使所选模型的 metadata 打开了 `responses_lite` 也不会切换成 lite wire format;启动时还会读取下游 `/v1/models`,自动使用返回列表中的最后一个 model id。当前对 `model_provider = "vllm"` 已补上 reasoning 兼容:会把 chat chunk 里的 `reasoning` / `reasoning_content` 翻回 Responses `reasoning` item,并把历史里的 `reasoning` item 回放成下游 assistant message 的 `reasoning` 字段;同时会向 vLLM 请求 streaming usage,并在最终 `response.completed.response.usage` 中回传
|
|
162
165
|
- `pycodex doctor` 会检查配置、`.env`、API key、DNS、TCP/TLS,以及可选的 live Responses API 请求
|
|
163
166
|
|
|
164
167
|
它目前主要用于:
|
|
@@ -280,6 +283,7 @@ asyncio.run(main())
|
|
|
280
283
|
|
|
281
284
|
本仓库额外兼容层 / 过渡工具:
|
|
282
285
|
|
|
286
|
+
- [x] `clock` — pycodex 的周期性 Agent 唤醒扩展。
|
|
283
287
|
- [x] `exec` — 当前对 code-mode 的本地近似实现。
|
|
284
288
|
- [x] `wait` — 当前对 code-mode 等待行为的本地近似实现。
|
|
285
289
|
|
|
@@ -290,13 +294,13 @@ asyncio.run(main())
|
|
|
290
294
|
- [x] 非交互 `exec` 路径的 `input` 对齐 — prompt input 已对齐上游。
|
|
291
295
|
- [x] developer/contextual-user message 的 shape 对齐 — message/content 结构已对齐。
|
|
292
296
|
- [x] `AGENTS.md` + `<environment_context>` 注入逻辑对齐 — 上下文拼接顺序已对齐。
|
|
293
|
-
- [x] 非交互 `exec`
|
|
297
|
+
- [x] 非交互 `exec` 路径的上游工具子集对齐 — 对齐子集已收敛;pycodex 额外暴露 `clock`。
|
|
294
298
|
- [x] `include = ["reasoning.encrypted_content"]` — reasoning include 字段已对齐。
|
|
295
299
|
- [x] `prompt_cache_key` — 请求级 prompt cache key 已补齐。
|
|
296
300
|
- [x] `x-client-request-id` — 请求 id header 已补齐。
|
|
297
301
|
- [x] `x-codex-turn-metadata` — turn id / sandbox header 已补齐。
|
|
298
302
|
- [x] `originator` — mode-aware originator header 已补齐。
|
|
299
303
|
- [x] `user-agent` 精确字符串对齐 — 非交互 `exec` 路径已对齐上游字符串。
|
|
300
|
-
- [x] exec-mode tool schema 的逐字段对齐 —
|
|
304
|
+
- [x] 上游 exec-mode tool schema 的逐字段对齐 — 对齐工具使用类内 spec;`clock` 作为扩展单独记录。
|
|
301
305
|
- [ ] 交互模式与非 `exec` 路径的完整行为对齐 — non-exec 首轮 context 已切到 `codex-tui` 路径,但 REPL 连续多轮行为还未完全验证。
|
|
302
306
|
- [ ] sandbox / approvals / compact / memory 等外围行为对齐 — 外围系统仍在后续范围。
|
|
@@ -126,9 +126,10 @@ At the time of writing:
|
|
|
126
126
|
- class-level tool descriptions, input schemas, output schemas, and notable
|
|
127
127
|
runtime result shapes have been refreshed across the default local tool set
|
|
128
128
|
|
|
129
|
-
The current
|
|
129
|
+
The current upstream-aligned subset already matches:
|
|
130
130
|
|
|
131
|
-
- exec-mode tool subset
|
|
131
|
+
- exec-mode upstream tool subset membership for the compared path; pycodex
|
|
132
|
+
additionally inserts its `clock` extension after `write_stdin`
|
|
132
133
|
- `include = ["reasoning.encrypted_content"]`
|
|
133
134
|
- model-visible prompt fields (`instructions` and `input`)
|
|
134
135
|
- request-scoped `prompt_cache_key`
|
|
@@ -194,6 +195,8 @@ schema 一致的工具有:
|
|
|
194
195
|
schema 对齐。
|
|
195
196
|
- `request_user_input`:`pycodex` 按 upstream source main 建模,带
|
|
196
197
|
`autoResolutionMs`;installed `codex-cli 0.138.0` 的 live capture 仍未带该字段。
|
|
198
|
+
- `clock`:pycodex 特有的 per-Agent/session 周期唤醒扩展,不属于 upstream
|
|
199
|
+
schema 对齐集合。
|
|
197
200
|
- `spawn_agent` / `send_input` / `resume_agent` / `wait_agent` /
|
|
198
201
|
`close_agent`:upstream 当前首轮 request 不再平铺暴露这些工具,而是暴露
|
|
199
202
|
`tool_search`,并由 deferred discovery 加载 Multi-agent tools。`pycodex` 仍在首轮
|
|
@@ -375,6 +378,7 @@ schema 一致的工具有:
|
|
|
375
378
|
| `shell_command` | `not exposed` | `class aligned` | 默认首轮路径不带;类内 desc/schema 已刷新为 shell-string command 语义 |
|
|
376
379
|
| `exec_command` | `intentional approval-field/description delta; round-trip same` | `class aligned except skipped auth + local idle resume` | 删除 fallback 后不再暴露 `sandbox_permissions` / `justification` / `prefix_rule`,这是 pycodex 刻意跳过鉴权逻辑的差异;description 额外提示长任务可以先回复用户,任务完成时 agent 会被 invoke 来继续处理;其余参数按 schema 执行,`function_call` / `function_call_output` 外层 shape 一致;默认 `10_000` token 截断和未读输出 `1 MiB` head/tail cap 已补齐,仅剩动态值差异 |
|
|
377
380
|
| `write_stdin` | `first-request same; round-trip same` | `class aligned` | 删除 fallback 后首轮 schema 相等;`function_call` / `function_call_output` 外层 shape 一致;默认 `10_000` token 截断和未读输出 `1 MiB` head/tail cap 已补齐,仅剩动态值差异 |
|
|
381
|
+
| `clock` | `pycodex-only` | `local extension` | 每个 Agent/session 一个周期计时器;每次成功回复后重新计时,到期复用 Agent idle auto-resume 注入带 `current_time` 的 `<clock_tick>`,传 `null` 或关闭 session 时取消 |
|
|
378
382
|
| `exec` | `not exposed` | `class aligned` | 默认首轮路径不带;code-mode custom/freeform desc 和 grammar 已刷新,仍需 code-mode request-visible 抓包复测 |
|
|
379
383
|
| `wait` | `not exposed` | `class aligned` | 默认首轮路径不带;code-mode wait schema/runtime 已刷新,仍需 code-mode request-visible 抓包复测 |
|
|
380
384
|
| `web_search` | `first-request same; round-trip same` | `class aligned` | 删除 fallback 后 provider-native payload 相等,包含 `search_content_types=["text","image"]`;`web_search_call` shape 一致;provider-native tool 无单独客户端 `tool_result` |
|
|
@@ -402,7 +406,7 @@ same:
|
|
|
402
406
|
- instructions
|
|
403
407
|
- input
|
|
404
408
|
- include
|
|
405
|
-
- exec-mode tool subset membership
|
|
409
|
+
- upstream exec-mode tool subset membership
|
|
406
410
|
- request context field presence
|
|
407
411
|
- exec-mode tool schemas
|
|
408
412
|
- current default-path schemas for `write_stdin`, `web_search`, `update_plan`,
|
|
@@ -411,6 +415,7 @@ same:
|
|
|
411
415
|
|
|
412
416
|
different:
|
|
413
417
|
- dynamic request metadata values
|
|
418
|
+
- pycodex inserts the local `clock` extension after `write_stdin`
|
|
414
419
|
- intentional `exec_command` approval/sandbox field omission and idle-resume
|
|
415
420
|
description in pycodex
|
|
416
421
|
- transport-layer header casing / normalization
|
|
@@ -543,9 +548,9 @@ including the IANA timezone name (`Asia/Hong_Kong` rather than `HKT`).
|
|
|
543
548
|
|
|
544
549
|
### 7. Exec-mode tool exposure
|
|
545
550
|
|
|
546
|
-
For non-interactive `pycodex`, `get_tools(exec_mode=True)`
|
|
547
|
-
|
|
548
|
-
tool
|
|
551
|
+
For non-interactive `pycodex`, `get_tools(exec_mode=True)` keeps the upstream
|
|
552
|
+
`codex exec` subset and inserts the documented local `clock` extension after
|
|
553
|
+
`write_stdin`. Alignment comparisons exclude that extra tool.
|
|
549
554
|
|
|
550
555
|
## Files involved
|
|
551
556
|
|
|
@@ -398,7 +398,8 @@ CodexNonExecConfirmedFacts = {
|
|
|
398
398
|
当前代码已知差异:
|
|
399
399
|
|
|
400
400
|
- `collaboration_mode` 内容来自 `./pycodex/prompts/collaboration_default.md` / `./pycodex/prompts/collaboration_plan.md`。
|
|
401
|
-
- 默认 CLI 的 non-exec
|
|
401
|
+
- 默认 CLI 的 non-exec 路径在 12 个上游对齐 exec-mode tools 之外,还会在
|
|
402
|
+
`write_stdin` 后暴露 pycodex 扩展 `clock`。
|
|
402
403
|
- REPL 连续多轮路径还没有单独 fake-server capture,所以现在不能声称它已经完全和 Codex 对齐。
|
|
403
404
|
|
|
404
405
|
## 4. Tool Schema
|
|
@@ -422,6 +423,9 @@ ExecModeToolOrder = [
|
|
|
422
423
|
]
|
|
423
424
|
```
|
|
424
425
|
|
|
426
|
+
`pycodex` 的实际默认顺序会在 `write_stdin` 后插入扩展工具 `clock`;上面的列表仍是
|
|
427
|
+
Codex 基准。
|
|
428
|
+
|
|
425
429
|
### 4.2 schema 形状
|
|
426
430
|
|
|
427
431
|
```python
|
|
@@ -8,6 +8,14 @@
|
|
|
8
8
|
当前 HTTP backend 使用 FastAPI。
|
|
9
9
|
当前独立测试里的 fake outcomming chat backend 也使用 FastAPI。
|
|
10
10
|
|
|
11
|
+
compat runtime 的下游 chat-completions / messages 流读取 timeout 默认是
|
|
12
|
+
`300` 秒,与 pycodex Responses client 的 `300_000 ms` SSE idle timeout
|
|
13
|
+
对齐。
|
|
14
|
+
|
|
15
|
+
Responses 请求里的 `reasoning.effort` 会转换为下游 chat 请求的
|
|
16
|
+
`chat_template_kwargs.reasoning_effort`。这与 vLLM Responses renderer 的
|
|
17
|
+
prompt 渲染路径一致,也保留 provider 自定义 effort 值(例如 `max`)。
|
|
18
|
+
|
|
11
19
|
## 目录边界
|
|
12
20
|
|
|
13
21
|
- incomming server 实现在 `responses_server/`
|
|
@@ -26,19 +34,19 @@
|
|
|
26
34
|
- vLLM chat-completions `reasoning` / `reasoning_content` -> Responses `reasoning` item 适配
|
|
27
35
|
- vLLM 历史 `reasoning` item -> assistant message `reasoning` 字段回放
|
|
28
36
|
- vLLM streaming `usage` -> final `response.completed.response.usage`
|
|
29
|
-
- 当环境变量 `PYCODEX_DUMP` 存在时,为每条 outcomming 请求附加 `return_token_ids = true
|
|
37
|
+
- 当环境变量 `PYCODEX_DUMP` 存在时,为每条 outcomming 请求附加 `return_token_ids = true`,并把实际下发的 request body、下游 usage/cache、`prompt_token_ids` / `token_ids` 以 JSONL 追加到 `{PYCODEX_DUMP}/dump.jsonl`
|
|
30
38
|
- 下游 chat stream 如果半路断开,会转成上游可解析的 `response.failed` 事件,而不是直接截断 HTTP body
|
|
31
39
|
- 普通 function tools
|
|
32
40
|
- custom tools 的 function-wrapper 兼容适配
|
|
33
41
|
- mock `web_search` 接口对齐(返回空结果)
|
|
34
42
|
- function / custom tool follow-up history 重建
|
|
43
|
+
- 结构化 `input_image` content items:user message 里的图片直接转成 chat `image_url` part;tool output 里的图片会在该 `tool` message 之后补一条只带图片的 `user` message(chat backend 普遍会丢弃 `tool` role 里的图片);`/v1/messages` 侧再转成 Anthropic `image` block
|
|
35
44
|
|
|
36
45
|
## 当前明确不支持
|
|
37
46
|
|
|
38
47
|
这些能力当前会被显式拒绝,而不是静默降级:
|
|
39
48
|
|
|
40
49
|
- 真正的 Responses `web_search` 执行;当前只做空结果 mock
|
|
41
|
-
- 结构化 `input_image` tool output
|
|
42
50
|
- 非流式 incomming 请求
|
|
43
51
|
|
|
44
52
|
## Incomming / Outcomming 分层
|
|
@@ -87,15 +95,23 @@ server 会为每条实际转发到下游的请求附上 `return_token_ids = true
|
|
|
87
95
|
trajectory 追加到 `${PYCODEX_DUMP}/dump.jsonl`,当前记录格式是:
|
|
88
96
|
|
|
89
97
|
```json
|
|
90
|
-
{"tokens":{"prefill":[1,2,3],"decode":[4,5,6]},"send_timestamp":2222.0}
|
|
98
|
+
{"request":{"model":"water18","messages":[],"stream":true,"return_token_ids":true},"usage":{"prompt_tokens":100,"completion_tokens":6,"total_tokens":106,"prompt_tokens_details":{"cached_tokens":64}},"tokens":{"prefill":[1,2,3],"decode":[4,5,6]},"send_timestamp":2222.0}
|
|
91
99
|
```
|
|
92
100
|
|
|
101
|
+
`request` 是 provider post-process 之后真正发出的 JSON body;HTTP headers 和
|
|
102
|
+
API key 不会写入 dump。对多轮 tool-call,可以逐条比较
|
|
103
|
+
`request.messages` 是否是上一轮的严格前缀扩展,并读取
|
|
104
|
+
`usage.prompt_tokens_details.cached_tokens` 核对下游 prefix-cache 命中。
|
|
105
|
+
重试和 mock tool follow-up 都会各写一条独立记录。
|
|
106
|
+
|
|
93
107
|
如果下游 provider 需要对 chat payload 做定制化改写,可以在
|
|
94
108
|
`responses_server/payload_processors.py` 里注册对应 `model_provider -> proc_fn`
|
|
95
109
|
映射;server 会在真正发出每一条 outcomming `/v1/chat/completions` 请求前,
|
|
96
110
|
对 canonical `outcomming_request` 调一次这个 hook,默认按 `vllm` 处理。
|
|
97
111
|
当前内置规则里,`vllm` 仍走 chat-completions compat 路径,但会额外保留
|
|
98
|
-
reasoning;`stepfun`
|
|
112
|
+
reasoning;`stepfun` 会把 `developer` role 转成 `system`。Step4 chat
|
|
113
|
+
template 不消费 `developer` role;使用该模板的 provider 应显式配置为
|
|
114
|
+
`stepfun`,否则 base instructions / AGENTS context 会被静默忽略。
|
|
99
115
|
|
|
100
116
|
如果下游 chat stream 一轮结束时只给了 `reasoning` / `reasoning_content`,
|
|
101
117
|
没有 assistant `content` 且没有 tool call,server 会丢弃这次 partial reasoning 并用
|
|
@@ -4,12 +4,12 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pycodex-ws"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.7"
|
|
8
8
|
description = "Browser workspace install alias for python-codex"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.6.2"
|
|
11
11
|
dependencies = [
|
|
12
|
-
"python-codex==0.2.
|
|
12
|
+
"python-codex==0.2.7",
|
|
13
13
|
]
|
|
14
14
|
|
|
15
15
|
[tool.hatch.build.targets.wheel]
|
|
@@ -39,6 +39,8 @@ from .runtime_services import (
|
|
|
39
39
|
from .tools import (
|
|
40
40
|
ApplyPatchTool,
|
|
41
41
|
BaseTool,
|
|
42
|
+
ClockManager,
|
|
43
|
+
ClockTool,
|
|
42
44
|
CloseAgentTool,
|
|
43
45
|
CodeModeManager,
|
|
44
46
|
ExecTool,
|
|
@@ -96,6 +98,8 @@ __all__ = [
|
|
|
96
98
|
"ApplyPatchTool",
|
|
97
99
|
"AssistantMessage",
|
|
98
100
|
"BaseTool",
|
|
101
|
+
"ClockManager",
|
|
102
|
+
"ClockTool",
|
|
99
103
|
"CloseAgentTool",
|
|
100
104
|
"create_agent_runtime_environment",
|
|
101
105
|
"CodeModeManager",
|
|
@@ -17,7 +17,14 @@ from .protocol import (
|
|
|
17
17
|
TurnResult,
|
|
18
18
|
UserMessage,
|
|
19
19
|
)
|
|
20
|
-
from .tools import
|
|
20
|
+
from .tools import (
|
|
21
|
+
ClockManager,
|
|
22
|
+
ClockTool,
|
|
23
|
+
ExecCommandTool,
|
|
24
|
+
ToolContext,
|
|
25
|
+
ToolRegistry,
|
|
26
|
+
UnifiedExecManager,
|
|
27
|
+
)
|
|
21
28
|
from .utils.truncation import truncate_tool_results_for_history
|
|
22
29
|
from .utils import uuid7_string
|
|
23
30
|
import typing
|
|
@@ -96,6 +103,14 @@ class Agent:
|
|
|
96
103
|
)
|
|
97
104
|
if self._exec_manager is not None:
|
|
98
105
|
self._exec_manager.set_notify_hook(self.maybe_invoke)
|
|
106
|
+
clock_tool = self._tool_registry.get_tool("clock")
|
|
107
|
+
self._clock_manager: 'typing.Union[ClockManager, None]' = (
|
|
108
|
+
clock_tool._manager
|
|
109
|
+
if isinstance(clock_tool, ClockTool)
|
|
110
|
+
else None
|
|
111
|
+
)
|
|
112
|
+
if self._clock_manager is not None:
|
|
113
|
+
self._clock_manager.set_notify_hook(self.maybe_invoke)
|
|
99
114
|
|
|
100
115
|
@property
|
|
101
116
|
def history(self) -> 'typing.Tuple[ConversationItem, ...]':
|
|
@@ -141,6 +156,8 @@ class Agent:
|
|
|
141
156
|
self, texts: 'typing.List[str]', turn_id: 'typing.Union[str, None]' = None
|
|
142
157
|
) -> 'TurnResult':
|
|
143
158
|
self._turn_running = True
|
|
159
|
+
if self._clock_manager is not None:
|
|
160
|
+
self._clock_manager.turn_started()
|
|
144
161
|
turn_id = turn_id or uuid7_string()
|
|
145
162
|
self.interrupt_asap = False
|
|
146
163
|
new_user_messages = [UserMessage(text=text) for text in texts]
|
|
@@ -198,6 +215,8 @@ class Agent:
|
|
|
198
215
|
output_text=last_assistant_message,
|
|
199
216
|
)
|
|
200
217
|
self._turn_running = False
|
|
218
|
+
if self._clock_manager is not None:
|
|
219
|
+
self._clock_manager.arm_after_reply()
|
|
201
220
|
return TurnResult(
|
|
202
221
|
turn_id=turn_id,
|
|
203
222
|
output_text=last_assistant_message,
|
|
@@ -240,17 +259,28 @@ class Agent:
|
|
|
240
259
|
raise
|
|
241
260
|
|
|
242
261
|
async def maybe_invoke(self, event: 'typing.Dict[str, object]') -> 'bool':
|
|
243
|
-
if self._turn_running
|
|
262
|
+
if self._turn_running:
|
|
263
|
+
return False
|
|
264
|
+
event_type = event.get("type")
|
|
265
|
+
if event_type == "exec_command_completed":
|
|
266
|
+
payload = {
|
|
267
|
+
"session_id": event.get("session_id"),
|
|
268
|
+
"exit_code": event.get("exit_code"),
|
|
269
|
+
"command": event.get("command"),
|
|
270
|
+
}
|
|
271
|
+
tag = "exec_command_completed"
|
|
272
|
+
elif event_type == "clock_tick":
|
|
273
|
+
payload = {
|
|
274
|
+
"period_m": event.get("period_m"),
|
|
275
|
+
"current_time": event.get("current_time"),
|
|
276
|
+
}
|
|
277
|
+
tag = "clock_tick"
|
|
278
|
+
else:
|
|
244
279
|
return False
|
|
245
|
-
payload = {
|
|
246
|
-
"session_id": event.get("session_id"),
|
|
247
|
-
"exit_code": event.get("exit_code"),
|
|
248
|
-
"command": event.get("command"),
|
|
249
|
-
}
|
|
250
280
|
text = (
|
|
251
|
-
"<
|
|
281
|
+
f"<{tag}>\n"
|
|
252
282
|
f"{json.dumps(payload, ensure_ascii=False, separators=(',', ':'))}\n"
|
|
253
|
-
"</
|
|
283
|
+
f"</{tag}>"
|
|
254
284
|
)
|
|
255
285
|
self._turn_running = True
|
|
256
286
|
task = asyncio.create_task(self.run_turn([text]))
|
|
@@ -259,6 +289,10 @@ class Agent:
|
|
|
259
289
|
)
|
|
260
290
|
return True
|
|
261
291
|
|
|
292
|
+
def shutdown(self) -> 'None':
|
|
293
|
+
if self._clock_manager is not None:
|
|
294
|
+
self._clock_manager.cancel()
|
|
295
|
+
|
|
262
296
|
async def _execute_tool_batch(
|
|
263
297
|
self,
|
|
264
298
|
turn_id: 'str',
|
|
@@ -328,16 +362,22 @@ class Agent:
|
|
|
328
362
|
|
|
329
363
|
def _emit(self, kind: 'str', turn_id: 'str', **payload: 'object') -> 'None':
|
|
330
364
|
if kind in TERMINAL_TURN_EVENTS:
|
|
331
|
-
payload["
|
|
365
|
+
payload["background_work_count"] = self._background_work_count(kind)
|
|
332
366
|
self._event_handler(
|
|
333
367
|
AgentEvent(kind=kind, turn_id=turn_id, payload=dict(payload))
|
|
334
368
|
)
|
|
335
369
|
|
|
336
|
-
def
|
|
370
|
+
def _background_work_count(self, terminal_event: 'str') -> 'int':
|
|
337
371
|
manager: 'typing.Union[UnifiedExecManager, None]' = self._exec_manager
|
|
338
|
-
if manager is None
|
|
339
|
-
|
|
340
|
-
|
|
372
|
+
count = 0 if manager is None else manager.running_session_count()
|
|
373
|
+
clock_manager = self._clock_manager
|
|
374
|
+
if (
|
|
375
|
+
terminal_event == "turn_completed"
|
|
376
|
+
and clock_manager is not None
|
|
377
|
+
and clock_manager.enabled
|
|
378
|
+
):
|
|
379
|
+
count += 1
|
|
380
|
+
return count
|
|
341
381
|
|
|
342
382
|
def _persist_history_items(
|
|
343
383
|
self,
|