python-codex 0.0.1__tar.gz → 0.1.0__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.
Files changed (97) hide show
  1. {python_codex-0.0.1 → python_codex-0.1.0}/.github/workflows/publish.yml +2 -2
  2. python_codex-0.1.0/.gitignore +6 -0
  3. python_codex-0.1.0/AGENTS.md +38 -0
  4. python_codex-0.1.0/LICENSE +201 -0
  5. python_codex-0.1.0/PKG-INFO +267 -0
  6. python_codex-0.1.0/README.md +252 -0
  7. python_codex-0.1.0/docs/ALIGNMENT.md +551 -0
  8. python_codex-0.1.0/docs/CONTEXT.md +466 -0
  9. python_codex-0.1.0/docs/responses_server/README.md +85 -0
  10. python_codex-0.1.0/pycodex/__init__.py +140 -0
  11. python_codex-0.1.0/pycodex/agent.py +290 -0
  12. python_codex-0.1.0/pycodex/cli.py +641 -0
  13. python_codex-0.1.0/pycodex/collaboration.py +21 -0
  14. python_codex-0.1.0/pycodex/context.py +580 -0
  15. python_codex-0.1.0/pycodex/doctor.py +360 -0
  16. python_codex-0.1.0/pycodex/model.py +533 -0
  17. python_codex-0.1.0/pycodex/prompts/collaboration_default.md +11 -0
  18. python_codex-0.1.0/pycodex/prompts/collaboration_plan.md +128 -0
  19. python_codex-0.1.0/pycodex/prompts/default_base_instructions.md +275 -0
  20. python_codex-0.1.0/pycodex/prompts/exec_tools.json +411 -0
  21. python_codex-0.1.0/pycodex/prompts/models.json +847 -0
  22. python_codex-0.1.0/pycodex/prompts/permissions/approval_policy/never.md +1 -0
  23. python_codex-0.1.0/pycodex/prompts/permissions/approval_policy/on_failure.md +1 -0
  24. python_codex-0.1.0/pycodex/prompts/permissions/approval_policy/on_request.md +57 -0
  25. python_codex-0.1.0/pycodex/prompts/permissions/approval_policy/on_request_rule_request_permission.md +33 -0
  26. python_codex-0.1.0/pycodex/prompts/permissions/approval_policy/unless_trusted.md +1 -0
  27. python_codex-0.1.0/pycodex/prompts/permissions/sandbox_mode/danger_full_access.md +1 -0
  28. python_codex-0.1.0/pycodex/prompts/permissions/sandbox_mode/read_only.md +1 -0
  29. python_codex-0.1.0/pycodex/prompts/permissions/sandbox_mode/workspace_write.md +1 -0
  30. python_codex-0.1.0/pycodex/prompts/subagent_tools.json +163 -0
  31. python_codex-0.1.0/pycodex/protocol.py +347 -0
  32. python_codex-0.1.0/pycodex/runtime.py +200 -0
  33. python_codex-0.1.0/pycodex/runtime_services.py +408 -0
  34. python_codex-0.1.0/pycodex/tools/__init__.py +58 -0
  35. python_codex-0.1.0/pycodex/tools/agent_tool_schemas.py +70 -0
  36. python_codex-0.1.0/pycodex/tools/apply_patch_tool.py +363 -0
  37. python_codex-0.1.0/pycodex/tools/base_tool.py +168 -0
  38. python_codex-0.1.0/pycodex/tools/close_agent_tool.py +55 -0
  39. python_codex-0.1.0/pycodex/tools/code_mode_manager.py +519 -0
  40. python_codex-0.1.0/pycodex/tools/exec_command_tool.py +96 -0
  41. python_codex-0.1.0/pycodex/tools/exec_runtime.js +161 -0
  42. python_codex-0.1.0/pycodex/tools/exec_tool.py +48 -0
  43. python_codex-0.1.0/pycodex/tools/grep_files_tool.py +150 -0
  44. python_codex-0.1.0/pycodex/tools/list_dir_tool.py +135 -0
  45. python_codex-0.1.0/pycodex/tools/read_file_tool.py +217 -0
  46. python_codex-0.1.0/pycodex/tools/request_permissions_tool.py +95 -0
  47. python_codex-0.1.0/pycodex/tools/request_user_input_tool.py +167 -0
  48. python_codex-0.1.0/pycodex/tools/resume_agent_tool.py +56 -0
  49. python_codex-0.1.0/pycodex/tools/send_input_tool.py +106 -0
  50. python_codex-0.1.0/pycodex/tools/shell_command_tool.py +107 -0
  51. python_codex-0.1.0/pycodex/tools/shell_tool.py +112 -0
  52. python_codex-0.1.0/pycodex/tools/spawn_agent_tool.py +97 -0
  53. python_codex-0.1.0/pycodex/tools/unified_exec_manager.py +380 -0
  54. python_codex-0.1.0/pycodex/tools/update_plan_tool.py +79 -0
  55. python_codex-0.1.0/pycodex/tools/view_image_tool.py +111 -0
  56. python_codex-0.1.0/pycodex/tools/wait_agent_tool.py +75 -0
  57. python_codex-0.1.0/pycodex/tools/wait_tool.py +68 -0
  58. python_codex-0.1.0/pycodex/tools/web_search_tool.py +30 -0
  59. python_codex-0.1.0/pycodex/tools/write_stdin_tool.py +75 -0
  60. python_codex-0.1.0/pycodex/utils/__init__.py +40 -0
  61. python_codex-0.1.0/pycodex/utils/dotenv.py +64 -0
  62. python_codex-0.1.0/pycodex/utils/get_env.py +218 -0
  63. python_codex-0.1.0/pycodex/utils/random_ids.py +19 -0
  64. python_codex-0.1.0/pycodex/utils/visualize.py +978 -0
  65. python_codex-0.1.0/pyproject.toml +39 -0
  66. python_codex-0.1.0/responses_server/__init__.py +17 -0
  67. python_codex-0.1.0/responses_server/__main__.py +5 -0
  68. python_codex-0.1.0/responses_server/app.py +217 -0
  69. python_codex-0.1.0/responses_server/config.py +63 -0
  70. python_codex-0.1.0/responses_server/payload_processors.py +86 -0
  71. python_codex-0.1.0/responses_server/server.py +63 -0
  72. python_codex-0.1.0/responses_server/session_store.py +37 -0
  73. python_codex-0.1.0/responses_server/stream_router.py +601 -0
  74. python_codex-0.1.0/responses_server/tools/__init__.py +4 -0
  75. python_codex-0.1.0/responses_server/tools/custom_adapter.py +235 -0
  76. python_codex-0.1.0/responses_server/tools/web_search.py +261 -0
  77. python_codex-0.1.0/tests/TESTS.md +191 -0
  78. python_codex-0.1.0/tests/__init__.py +2 -0
  79. python_codex-0.1.0/tests/compare_request_user_input_roundtrip.py +679 -0
  80. python_codex-0.1.0/tests/compare_steer_request_bodies.py +633 -0
  81. python_codex-0.1.0/tests/compare_tool_schemas.py +484 -0
  82. python_codex-0.1.0/tests/fake_responses_server.py +389 -0
  83. python_codex-0.1.0/tests/fakes.py +65 -0
  84. python_codex-0.1.0/tests/responses_server/fake_chat_completions_server.py +276 -0
  85. python_codex-0.1.0/tests/responses_server/test_server.py +836 -0
  86. python_codex-0.1.0/tests/test_agent.py +302 -0
  87. python_codex-0.1.0/tests/test_builtin_tools.py +1028 -0
  88. python_codex-0.1.0/tests/test_cli.py +2412 -0
  89. python_codex-0.1.0/tests/test_context.py +290 -0
  90. python_codex-0.1.0/tests/test_doctor.py +154 -0
  91. python_codex-0.1.0/tests/test_fake_responses_server.py +104 -0
  92. python_codex-0.1.0/tests/test_model.py +737 -0
  93. python_codex-0.0.1/.gitignore +0 -7
  94. python_codex-0.0.1/PKG-INFO +0 -30
  95. python_codex-0.0.1/README.md +0 -21
  96. python_codex-0.0.1/pycodex/__init__.py +0 -3
  97. python_codex-0.0.1/pyproject.toml +0 -18
@@ -48,7 +48,7 @@ jobs:
48
48
  id-token: write
49
49
  environment:
50
50
  name: testpypi
51
- url: https://test.pypi.org/p/python-codex
51
+ url: https://test.pypi.org/project/pycodex/
52
52
  steps:
53
53
  - name: Download distributions
54
54
  uses: actions/download-artifact@v4
@@ -69,7 +69,7 @@ jobs:
69
69
  id-token: write
70
70
  environment:
71
71
  name: pypi
72
- url: https://pypi.org/p/python-codex
72
+ url: https://pypi.org/project/pycodex/
73
73
  steps:
74
74
  - name: Download distributions
75
75
  uses: actions/download-artifact@v4
@@ -0,0 +1,6 @@
1
+ .venv/
2
+ .pytest_cache/
3
+ __pycache__/
4
+ *.pyc
5
+ .tmp/
6
+ uv.lock
@@ -0,0 +1,38 @@
1
+ # pycodex AGENTS
2
+
3
+ ## Repo Priors
4
+
5
+ - 这个仓库的目标是尽可能准确地复现上游 Codex(`https://github.com/openai/codex`),不是发明一个“类似 Codex”的简化替代品;凡是行为差异都应优先朝原版 Codex 收敛。
6
+ - 发布到 PyPI 时,distribution name 用 `python-codex`;代码包目录、`import` 路径和 CLI 命令仍保持 `pycodex`。
7
+ - 包结构直接放在 repo 根目录下的 `pycodex/`,不使用 `src/` layout。
8
+ - Rust 到 Python 的主映射:`submission_loop` -> `pycodex/runtime.py`,`run_turn` / `run_sampling_request` -> `pycodex/agent.py`,`ToolRouter` -> `pycodex/tools/base_tool.py` + `pycodex/tools/`.
9
+ - 会话协议先收敛到 4 类 item:`UserMessage`、`AssistantMessage`、`ToolCall`、`ToolResult`;只有这层稳定后再扩 richer event model。
10
+ - 优先保持主循环可测试、可替换:模型侧通过 `ModelClient` 协议接入;测试专用的 `ScriptedModelClient` 放在 `tests/fakes.py`,不要放进运行时包。
11
+ - `ResponsesModelClient` 直接复用 `~/.codex/config.toml` 的 provider 配置;当前已验证这里的 responses provider 需要 `stream = true`,否则会返回 `400` 和 `Stream must be set to true`。
12
+ - `responses_server` compat 层应透传请求里的 `model`;不要再做 “取 downstream /models 第一个 id 并强制覆盖请求模型” 这种兜底兼容。
13
+ - `responses_server` 的 provider-specific chat payload 定制统一放在 `responses_server/payload_processors.py`:使用 `CompatServerConfig.model_provider` 选择 `provider_name -> proc_fn(outcomming_request)` 映射,并且只在真正发出 downstream `/v1/chat/completions` 前 post-process;`StreamRouter` 内部继续保留 canonical payload,避免 tool hydration / mock web_search follow-up 被 provider 改写污染。
14
+ - `pycodex` 默认是最小交互 CLI;无 prompt 时进入 REPL,并通过 `AgentRuntime` 跑外层提交循环。当前会显示最小事件流、assistant 流式输出、简单 title/history(`/title`, `/history`),并默认注册一组与原版一一对应的本地工具子集。
15
+ - 交互 CLI 的事件流展示优先表达用户可感知的阶段(例如工具开始/完成、模型回看工具结果),不要直接把内部 `iteration` 计数暴露成主要状态文案;`iterations` 应继续保留在 `TurnResult` 等程序化结果里。
16
+ - prompt/context 相关逻辑统一放在 `pycodex/context.py`:`AgentLoop` 只维护真实会话历史;每轮请求前由 `ContextManager` 注入 base instructions、developer message、`AGENTS.md` 指令和 `<environment_context>`,且这些注入项不写回 history。
17
+ - `AgentLoop` 的 turn-loop 语义要跟上游 `codex-rs/core/src/codex.rs` 一致:按 follow-up / tool handoff 自然收敛,不要加固定 12 轮之类的 hard cap,也不要保留本地专用的 iteration-limit 参数。
18
+ - `README.md` 和 `docs/` 属于对齐工作的一部分:只要实现状态、对齐结论或使用方式发生实质变化,就应及时更新,不要让文档滞后于当前代码。
19
+ - 新工具必须继承 `BaseTool`,然后通过 `ToolRegistry.register(tool_instance)` 接入;不要再给 registry 传散装 name/description/handler 参数。
20
+ - request/tool schema 对齐应落在实际建模层(`ToolSpec` / `BaseTool` / request builder)本身,不要再引入 prompt 级别的 `serialized_tools` 旁路覆盖。
21
+ - 当前已接入的内置工具子集:`shell`、`shell_command`、`exec_command`、`write_stdin`、`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`。后续继续新增时,仍然按原版 Codex 内置工具逐个补齐。
22
+ - `exec_command` / `write_stdin` 的 unified-exec 对齐要注意两层默认截断语义:省略 `max_output_tokens` 时也要按 upstream 默认 `10_000` token 预算裁剪 tool response;同时长时间未轮询的未读输出缓冲不能无限累积,需保留 upstream 同款 `1 MiB` head/tail。
23
+ - 当前协议层已经支持两类原版工具载荷:普通 function tools,以及 `apply_patch` 这类 freeform/custom tools;工具结果也支持结构化 `input_image` content items,用于 `view_image` 这类会把图片喂回模型的工具。
24
+ - 当前本地 runtime 已支持一个最小的 in-process sub-agent 管理层:`spawn_agent` / `send_input` / `resume_agent` / `wait_agent` / `close_agent` 通过共享的 `SubAgentManager` 驱动新的 `AgentRuntime` 实例,不依赖 CLI harness 自带的多 agent 基础设施。
25
+ - 当前交互工具 `request_user_input` / `request_permissions` 已接到 `pycodex` 交互 CLI:它们会在一次 turn 内直接复用同一个 stdin/input_fn 向用户提问,因此只有交互模式下有完整体验;非交互调用默认会返回取消/不可用类错误。
26
+ - `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`。
27
+ - 在 `/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 源码建模”。
28
+ - 当前 `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 执行。
29
+ - 我们支持的 tools 必须和原版 Codex 内置 tools 一一对应:名称、定位、参数形状、交互模型、输出语义都应尽量对齐;不要混合多个原版工具的语义做一个“折中工具”。
30
+ - 代码风格上不要使用 `*` 定义 keyword-only 参数;接口默认允许位置参数。
31
+ - 本仓库使用 `uv`;本地默认没有预装测试依赖,开始工作前先跑 `uv sync --dev`,验证用 `uv run pytest`。
32
+ - 上游 Codex 的 `steer` 目前是 TUI 交互层能力:开启后 `Enter` 会立即提交,`Tab` 才是排队。对齐时优先盯“下一次发出去的请求体”而不是内部控制流;当前 `pycodex` 已把 steer/queue 语义下沉到 `AgentRuntime`,并让 `AgentLoop.run_turn(texts)` 一次接收一批 user texts,这样下一次请求的 `input` 可以把多个 steer 文本按顺序并到 history 尾部。`CliSessionView.handle_event()` 把 spinner 再次拉起。要保证输入不被遮挡,需要让 view 知道“当前正在输入”,并在 input-active 期间抑制这些事件对 spinner 的 resume。
33
+ - steer 的最小交互反馈已经约定为两条显式状态文案:入队时打印 `[steer] queued: <prompt>`,该条 queued turn 真正开始执行时打印 `[steer] inserted: <prompt>`。后续如果补更复杂的 queue UI,也应保留这两个核心状态语义。
34
+ - 在当前 `pycodex` CLI 里,普通输入与 `/queue <message>` 只负责选择 runtime queue;真正的 steer/queue 差别由 `AgentRuntime.enqueue_user_turn(..., queue=...)` 决定。runtime 内部也应保持成两个同构 queue,而不是一个普通 queue 再叠一个 steer 专用旁路状态机。
35
+ - 对上游 steer 语义要非常谨慎:正常 active-turn steer 首先走的是 `inject_input(...)` + `pending_input`,不是立刻 `spawn_task(...)` / `TurnAbortReason::Replaced`。更准确的理解是“在最近一次 sampling 边界插入”,而不是“任意时刻硬打断当前模型/工具调用”。
36
+ - 用 `tests/fake_responses_server.py` 做 steer 时序对比时,不要把 proxy capture 文件的生成时刻当成“请求已到达 upstream”的信号;`build_proxy_handler(...)` 会等整条 upstream response 读完后才 `write_capture(...)`。如果要在第一条 request 仍未完成时注入 steer,应该同步等待 fake origin 自己收到第 1 条 POST。
37
+ - 在本机做 steer fake-server 对比时,不要把用户本地 `config.toml` 里的 `service_tier` / fast-mode 设置混进“默认 steer”结论。`tests/compare_steer_request_bodies.py` 现在会给 upstream 和 `pycodex` 都生成临时 config,并去掉顶层 `service_tier` 后再比较 request body。
38
+ - `x-codex-turn-metadata.workspaces` 的时机不是“整个 session 只发第一条请求”。当前对齐结论是:首个 turn 的后续 steer/follow-up request 也继续带 `workspaces`;切到后续新 turn 才省略。
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright 2025 OpenAI
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
@@ -0,0 +1,267 @@
1
+ Metadata-Version: 2.4
2
+ Name: python-codex
3
+ Version: 0.1.0
4
+ Summary: A minimal Python extraction of Codex's main agent loop
5
+ License-File: LICENSE
6
+ Requires-Python: >=3.10
7
+ Requires-Dist: debugpy>=1.8.20
8
+ Requires-Dist: fastapi>=0.115
9
+ Requires-Dist: loguru>=0.7.3
10
+ Requires-Dist: prompt-toolkit>=3.0
11
+ Requires-Dist: requests>=2.31
12
+ Requires-Dist: tomli>=2.0; python_version < '3.11'
13
+ Requires-Dist: uvicorn>=0.32
14
+ Description-Content-Type: text/markdown
15
+
16
+ # pycodex
17
+
18
+ PyPI distribution name: `python-codex`
19
+ Import path and CLI command remain `pycodex`.
20
+
21
+ 这个仓库把上游 Codex(`https://github.com/openai/codex`)里最核心的 agent
22
+ 闭环先抽成一个尽量小的 Python 版本,重点保留两层结构:
23
+
24
+ - `submission_loop`:顺序消费提交的操作。
25
+ - `run_turn`:在单个 turn 内持续执行 `模型采样 -> 工具调用 -> 把工具结果喂回模型`,直到拿到最终回答。
26
+
27
+ 对应的 Rust 参考点:
28
+
29
+ - `codex-rs/core/src/codex.rs` 里的 `submission_loop`
30
+ - `codex-rs/core/src/codex.rs` 里的 `run_turn`
31
+ - `codex-rs/core/src/codex.rs` 里的 `run_sampling_request`
32
+ - `codex-rs/core/src/tools/router.rs` 里的 `ToolRouter`
33
+ - `codex-rs/core/src/stream_events_utils.rs` 里的 `handle_output_item_done`
34
+
35
+ ## 快速开始
36
+
37
+ 先安装开发依赖:
38
+
39
+ ```bash
40
+ uv sync
41
+ ```
42
+
43
+ 试一下真实入口:
44
+
45
+ ```bash
46
+ uv run pycodex "Reply with exactly OK."
47
+ uv run pycodex
48
+ ```
49
+
50
+ ## 设计取舍
51
+
52
+ 这里不是对 Rust 版本做 1:1 移植,而是先收敛到一个最小可复用内核:
53
+
54
+ 1. 用一个很薄的 `ModelClient` 协议抽象模型侧。
55
+ 2. 用 `ToolRegistry` 管理工具规格和执行器。
56
+ 3. 用 `AgentLoop` 实现核心闭环。
57
+ 4. 用 `AgentRuntime` 保留外层提交队列,方便以后继续对齐 Rust 的 `submission_loop`。
58
+
59
+ 暂时刻意不包含:
60
+
61
+ - TUI / 流式增量渲染
62
+ - MCP / connectors / sandbox / approvals
63
+ - memory / compact / hooks / review mode
64
+ - 真实 OpenAI 适配器
65
+
66
+ 这些都可以后续继续往上叠,但当前项目先把最核心的“工具增强推理主循环”钉住。
67
+
68
+ ## 目录
69
+
70
+ - `pycodex/protocol.py`:最小的会话 item / prompt / event 协议
71
+ - `pycodex/model.py`:模型客户端协议和 Responses API 适配器
72
+ - `pycodex/cli.py`:`pycodex` 单轮命令行入口
73
+ - `pycodex/tools/base_tool.py`:`BaseTool`、`ToolRegistry`、`ToolContext`
74
+ - `pycodex/tools/`:具体工具实现
75
+ - `pycodex/agent.py`:主循环
76
+ - `pycodex/runtime.py`:外层提交队列
77
+ - `tests/test_agent.py`:核心行为测试
78
+
79
+ ## 当前对齐状态
80
+
81
+ 当前进度可以分成两层看:
82
+
83
+ - prompt/context 对齐:
84
+ - 非交互 `exec` 路径下,`instructions` 和 `input` 已经对齐到上游 Codex;
85
+ - 这一层现在主要由 `pycodex/context.py` 和 vendored prompt data 负责。
86
+ - turn-loop 语义对齐:
87
+ - `AgentLoop` 默认不再使用固定 12 轮上限;
88
+ - 现在和上游一样,按 “还有没有 follow-up / tool handoff” 自然收敛;
89
+ - 本地不再保留额外的 iteration-limit 参数。
90
+ - request-level 对齐:
91
+ - 非交互 `exec` 路径的 request body 已基本对齐;
92
+ - 默认 CLI 的 non-exec 首轮请求现在也已切到 `codex-tui` + `<collaboration_mode>` 这条上游路径;
93
+ - 默认 CLI 的两轮主线程对话 request/header 也已补抓并对齐,包括后续 turn 不再携带 `workspaces`;
94
+ - 当前剩余重点主要转向更外围的行为分支,而不是这条已比较路径上的 request/header。
95
+ - tool round-trip 对齐:
96
+ - `request_user_input` 的 Default-mode unavailable 路径已按真实 upstream capture 对齐;
97
+ - Plan-mode happy path 现在也已按 upstream 源码补齐到工具/协议层:会强制 `isOther=true`、要求非空 `options`,并以 JSON 字符串 + `success=true` 回传结构化答案;
98
+ - 新增了一个基于 `tests/fake_responses_server.py` proxy 模式的 deterministic round-trip compare 脚本 `tests/compare_request_user_input_roundtrip.py`;它在本机已安装的 `codex-cli 0.115.0` 上确认:Plan-mode live capture 里唯一剩余的 `function_call_output` schema 差异是 `pycodex` 多带了 `success=true`。
99
+
100
+ 更细的对齐说明见 `docs/ALIGNMENT.md`。
101
+
102
+ ## 真实模型联调
103
+
104
+ 如果本机已经有 Codex CLI 配置,可直接复用 `~/.codex/config.toml` 里的
105
+ `model`、`model_provider`、`base_url`、`env_key`:
106
+
107
+ ```python
108
+ from pycodex import ResponsesModelClient
109
+
110
+ client = ResponsesModelClient.from_codex_config()
111
+ ```
112
+
113
+ 当前实现走 OpenAI-compatible Responses API 的流式 `/responses` 接口。这个点
114
+ 已经用本机 `~/.codex/config.toml` 做过联调验证。
115
+
116
+ 通过 CLI 启动时,`pycodex` 还会在读取配置前加载同目录下的 `.env`
117
+ (通常是 `~/.codex/.env`),方便把 provider key 之类的环境变量放在那里。
118
+ 为对齐上游 Codex,`.env` 中以 `CODEX_` 开头的变量不会被导入。
119
+
120
+ ## pycodex
121
+
122
+ `pycodex` 现在默认是一个最小交互式入口,内部通过 `AgentRuntime` 驱动 turn
123
+ 提交循环,默认直接复用 `~/.codex/config.toml`:
124
+
125
+ ```bash
126
+ pycodex
127
+ pycodex "Summarize this repo in one sentence."
128
+ printf 'Reply with exactly OK.' | pycodex
129
+ pycodex --json "Reply with exactly OK."
130
+ pycodex --profile model_proxy "Reply with exactly OK."
131
+ pycodex --vllm-endpoint http://127.0.0.1:18000 "Reply with exactly OK."
132
+ pycodex doctor
133
+ ```
134
+
135
+ 当前行为:
136
+
137
+ - 没有 argv prompt 且 stdin 是 TTY 时,进入交互模式
138
+ - 有 argv prompt 或 stdin 管道输入时,执行单轮请求
139
+ - 交互模式下支持 `/exit` 和 `/quit`
140
+ - 交互模式下会显示简洁阶段事件流,例如工具执行状态和模型回看工具结果
141
+ - assistant 文本会按流式 delta 直接打印
142
+ - 交互模式下支持 `/history`、`/title` 和 `/model`
143
+ - `/model <name>` 会切换当前交互会话后续请求使用的模型;`/model` 会显示当前模型和可选模型
144
+ - 交互模式默认支持 steer:普通输入会走 runtime 的 steer 路径,当前请求会在下一个安全边界尽快停下,后续 steer 文本会按顺序并入下一次模型请求的 `input`;如需明确排队可用 `/queue <message>`,会打印 `[steer] queued: ...`,随后等该 turn 真正开始时再打印 `[steer] inserted: ...`
145
+ - 当前默认注册一组与原版 Codex 一一对应的本地工具子集:`shell`、`shell_command`、`exec_command`、`write_stdin`、`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`
146
+ - `--vllm-endpoint http://host:port` 会自动拉起一个本地 `responses_server` compat 层;当 path 为空时会内部补 `/v1`,再把 `/responses` 请求转到下游 `/v1/chat/completions`,并在 provider 侧适配 mock `web_search` 与 custom-tool function wrapper
147
+ - `pycodex doctor` 会检查配置、`.env`、API key、DNS、TCP/TLS,以及可选的 live Responses API 请求
148
+
149
+ 它目前主要用于:
150
+
151
+ - 验证 provider / model / auth 配置是否可用
152
+ - 调试 `ResponsesModelClient`
153
+ - 做最小单轮 / 多轮 smoke test
154
+
155
+ `doctor` 示例:
156
+
157
+ ```bash
158
+ pycodex doctor
159
+ pycodex doctor --skip-live
160
+ pycodex doctor --json
161
+ ```
162
+
163
+ ## 示例
164
+
165
+ ```python
166
+ import asyncio
167
+
168
+ from pycodex import (
169
+ AgentLoop,
170
+ BaseTool,
171
+ ContextManager,
172
+ ResponsesModelClient,
173
+ ToolRegistry,
174
+ )
175
+
176
+
177
+ class EchoTool(BaseTool):
178
+ name = "echo"
179
+ description = "Echo the provided text."
180
+ input_schema = {
181
+ "type": "object",
182
+ "properties": {"text": {"type": "string"}},
183
+ "required": ["text"],
184
+ }
185
+
186
+ async def run(self, context, args):
187
+ del context
188
+ return args["text"]
189
+
190
+
191
+ async def main() -> None:
192
+ model = ResponsesModelClient.from_codex_config()
193
+ context_manager = ContextManager.from_codex_config()
194
+
195
+ tools = ToolRegistry()
196
+ tools.register(EchoTool())
197
+
198
+ agent = AgentLoop(model, tools, context_manager)
199
+ result = await agent.run_turn("Call the echo tool with text=hello, then tell me what it returned.")
200
+ print(result.output_text)
201
+
202
+
203
+ asyncio.run(main())
204
+ ```
205
+
206
+ ## 对齐清单
207
+
208
+ 更细的说明见 `docs/ALIGNMENT.md`。这里保留一个高层 checklist,方便直接看当前进度。
209
+
210
+ ### Tools 对齐
211
+
212
+ 上游官方工具:
213
+
214
+ - [x] `shell` — 执行 argv 形式的 shell 命令。
215
+ - [x] `shell_command` — 执行字符串形式的 shell script。
216
+ - [x] `exec_command` — 启动带 session 的长命令执行。
217
+ - [x] `write_stdin` — 向已有执行 session 写入 stdin 或轮询输出。
218
+ - [x] `web_search` — 暴露 provider-native 的网页搜索能力。
219
+ - [x] `update_plan` — 更新任务计划并维护步骤状态。
220
+ - [x] `request_user_input` — 向用户发起结构化问题并等待回答。
221
+ - [x] `request_permissions` — 请求额外权限再继续执行。
222
+ - [x] `spawn_agent` — 创建并启动子 agent。
223
+ - [x] `send_input` — 给已有子 agent 继续发送输入。
224
+ - [x] `resume_agent` — 恢复已关闭的子 agent。
225
+ - [x] `wait_agent` — 等待子 agent 进入终态。
226
+ - [x] `close_agent` — 关闭不再需要的子 agent。
227
+ - [x] `apply_patch` — 用 freeform patch 精确修改文件。
228
+ - [x] `grep_files` — 按模式搜索文件内容。
229
+ - [x] `read_file` — 读取文件片段并保留行号语义。
230
+ - [x] `list_dir` — 列出目录树片段。
231
+ - [x] `view_image` — 把本地图片转成模型可见输入。
232
+
233
+ 尚未单独建模的上游官方低频 / 特殊模式工具:
234
+
235
+ - [ ] `wait_infinite` — 长时间阻塞等待外部事件或后续输入。
236
+ - [ ] `spawn_agents_on_csv` — 按 CSV 批量创建子 agent 任务。
237
+ - [ ] `report_agent_job_result` — 上报批处理 agent job 的结果。
238
+ - [ ] `js_repl` — JavaScript REPL / code-mode 主入口。
239
+ - [ ] `js_repl_reset` — 重置 `js_repl` 的运行状态。
240
+ - [ ] `artifacts` — 生成或管理结构化工件输出。
241
+ - [ ] `list_mcp_resources` — 列出 MCP 资源。
242
+ - [ ] `list_mcp_resource_templates` — 列出 MCP 资源模板。
243
+ - [ ] `read_mcp_resource` — 读取 MCP 资源内容。
244
+ - [ ] `multi_tool_use.parallel` — 并行包装多个 developer tools 调用。
245
+
246
+ 本仓库额外兼容层 / 过渡工具:
247
+
248
+ - [x] `exec` — 当前对 code-mode 的本地近似实现。
249
+ - [x] `wait` — 当前对 code-mode 等待行为的本地近似实现。
250
+
251
+ ### 行为对齐
252
+
253
+ - [x] `AgentLoop` / `AgentRuntime` 主循环骨架 — turn 闭环和提交队列已成立。
254
+ - [x] 非交互 `exec` 路径的 `instructions` 对齐 — base instructions 已对齐上游。
255
+ - [x] 非交互 `exec` 路径的 `input` 对齐 — prompt input 已对齐上游。
256
+ - [x] developer/contextual-user message 的 shape 对齐 — message/content 结构已对齐。
257
+ - [x] `AGENTS.md` + `<environment_context>` 注入逻辑对齐 — 上下文拼接顺序已对齐。
258
+ - [x] 非交互 `exec` 路径的工具子集对齐 — 暴露给模型的工具集合已收敛。
259
+ - [x] `include = ["reasoning.encrypted_content"]` — reasoning include 字段已对齐。
260
+ - [x] `prompt_cache_key` — 请求级 prompt cache key 已补齐。
261
+ - [x] `x-client-request-id` — 请求 id header 已补齐。
262
+ - [x] `x-codex-turn-metadata` — turn id / sandbox header 已补齐。
263
+ - [x] `originator` — mode-aware originator header 已补齐。
264
+ - [x] `user-agent` 精确字符串对齐 — 非交互 `exec` 路径已对齐上游字符串。
265
+ - [x] exec-mode tool schema 的逐字段对齐 — 当前通过工具层直接复用上游 snapshot。
266
+ - [ ] 交互模式与非 `exec` 路径的完整行为对齐 — non-exec 首轮 context 已切到 `codex-tui` 路径,但 REPL 连续多轮行为还未完全验证。
267
+ - [ ] sandbox / approvals / compact / memory 等外围行为对齐 — 外围系统仍在后续范围。