Skip to content

Commit 3431cab

Browse files
author
SqlRush
committed
docs: DESIGN.zh-CN — full solution design (isolation, multi-model chat wire, routing, /model picker, release flow)
1 parent cd1a377 commit 3431cab

1 file changed

Lines changed: 211 additions & 0 deletions

File tree

docs/DESIGN.zh-CN.md

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
# codexgo 方案设计文档
2+
3+
> 版本:对应 codexgo v0.2.2 · 更新日期:2026-06-06
4+
> 维护者决策记录;英文技术细节见 `DEVIATIONS.md``docs/specs/`
5+
6+
---
7+
8+
## 1. 产品定位
9+
10+
**codexgo 是一个独立产品**,而非 OpenAI Codex 的替代品:
11+
12+
- 起点:对 OpenAI Codex CLI **0.136.0**(Rust,~935K 行)的逐字节级 Go 重写,
13+
通过差分测试(`internal/paritytest/`,对照真实 codex 二进制)验证忠实度;
14+
- 转折(2026-06-06 维护者决策):codexgo 与原生 codex **在同一台机器上完全
15+
隔离共存**,并走向**多模型后端**(GLM、DeepSeek 等非 OpenAI 厂商),
16+
不再追求 drop-in 替代;
17+
- 兼容性保留在**协议与文件格式层**(请求体、rollout JSONL、SQLite schema、
18+
config.toml 格式),便于持续吸收上游改进。
19+
20+
```
21+
┌─ 本地身份:完全独立(第 2 节)
22+
codexgo ──────┼─ 引擎内核:逐字节移植 + 差分验证(parity 资产)
23+
└─ 模型后端:多厂商直连(第 3 节,上游不具备)
24+
```
25+
26+
---
27+
28+
## 2. 本地隔离方案
29+
30+
目标:**对原生 codex 的配置、记忆、凭证、环境变量零接触、零冲突。**
31+
32+
| 维度 | 原生 codex | codexgo |
33+
|------|-----------|---------|
34+
| 可执行文件 | `/opt/homebrew/bin/codex` | `/opt/homebrew/bin/codexgo` |
35+
| 配置主目录 | `~/.codex/` | `~/.codexgo/` |
36+
| 环境变量 | `CODEX_*` | `CODEXGO_*`(读取与导出子进程两个方向全部改名) |
37+
| macOS 钥匙串 | service `codex` / `Codex MCP Credentials` | `codexgo` / `CodexGo MCP Credentials` |
38+
| 项目级目录 | `<repo>/.codex/` | `<repo>/.codexgo/`(不读 `.codex/`) |
39+
| 系统级配置 | `/etc/codex/` | `/etc/codexgo/` |
40+
| 插件清单 | `.codex-plugin/plugin.json` | `.codexgo-plugin/` 优先,`.codex-plugin/``.claude-plugin/` 兼容回退 |
41+
42+
实现集中在 `internal/brand/brand.go`(身份常量单一来源)。
43+
**线上身份**(OAuth client id、originator、User-Agent、登录端点)保持与上游
44+
一致,保证 ChatGPT 账号登录可用;界面文案品牌化为独立的后续专项。
45+
46+
登录互不影响:`codexgo login``~/.codexgo/auth.json`,与原生
47+
`~/.codex/auth.json` 完全独立;唯一注意点是两者的 OAuth 回调端口同为
48+
1455,不要**同一时刻**执行两边的 login。
49+
50+
---
51+
52+
## 3. 多模型方案(核心差异化能力)
53+
54+
### 3.1 背景:上游的限制
55+
56+
- codex 0.136 把 `wire_api = "chat"` **整体移除**(配置该值直接报迁移错误,
57+
见上游 discussion #7782),自定义 provider 只能讲 OpenAI **Responses API**
58+
(`/v1/responses`);
59+
- 实测(2026-06-06):GLM(open.bigmodel.cn/api/paas/v4)与 DeepSeek
60+
(api.deepseek.com/v1)的 `/responses` 均返回 404,**只提供
61+
`/chat/completions`**;
62+
- 因此原生 codex 直连这类厂商不可行,必须外挂翻译代理(LiteLLM 等)。
63+
64+
### 3.2 codexgo 的解法:原生 chat 协议层
65+
66+
上游已无 chat 实现可移植,该层为 codexgo 全新编写(v0.2.0):
67+
68+
```
69+
┌──────────────────────────────────────────┐
70+
引擎回合循环 │ 翻译层(本方案新增) │ 第三方厂商
71+
(协议无关) │ │
72+
│ 发出:internal/core/client_chat.go │
73+
对话历史 ────────────▶│ · Responses items → chat messages │───▶ POST /chat/completions
74+
11 个工具 ───────────▶│ · 工具 JSON-schema → chat function tools│ (GLM / DeepSeek / …)
75+
│ · developer 角色 → system │
76+
│ · 工具结果 → role:"tool" │
77+
│ │
78+
ResponseEvent ◀──────│ 收回:internal/api/chat_completions.go │◀─── SSE 流
79+
(与 Responses │ · content 增量 → OutputTextDelta │
80+
解析器同一事件语言) │ · reasoning_content → 推理增量 │
81+
│ · tool_calls 增量聚合 → FunctionCall │
82+
│ · finish → Completed{EndTurn} │
83+
└──────────────────────────────────────────┘
84+
```
85+
86+
**关键设计**:翻译层把 chat SSE 聚合成与 Responses 解析器**完全相同**的内部
87+
事件序列,引擎(回合循环、工具执行、沙箱、TUI)对协议差异零感知——因此
88+
第三方模型获得**完整智能体能力**。已实测 GLM 自主发起 `exec` 调用、执行
89+
shell 命令、根据结果续轮作答。
90+
91+
有意不做的部分:推理参数(effort/verbosity/service-tier)无 chat 等价物,
92+
不发送;reasoning 内容流式显示但不回传历史(chat 协议不接受);多模态图片
93+
暂不透传。
94+
95+
### 3.3 模型 → provider 自动路由
96+
97+
codexgo 扩展配置字段 `models = [...]`(上游没有;为空时不出现在序列化结果
98+
中,保证与上游配置互通):
99+
100+
```toml
101+
# ~/.codexgo/config.toml
102+
model = "gpt-5.5" # 默认模型,/model 选择会更新此键
103+
104+
[model_providers.glm]
105+
name = "GLM (Zhipu AI)"
106+
base_url = "https://open.bigmodel.cn/api/paas/v4"
107+
wire_api = "chat"
108+
experimental_bearer_token = "<key>" # key 直接放配置(0600),不进环境变量
109+
models = ["glm-5.1"] # ← codexgo 路由扩展
110+
111+
[model_providers.deepseek]
112+
name = "DeepSeek"
113+
base_url = "https://api.deepseek.com/v1"
114+
wire_api = "chat"
115+
experimental_bearer_token = "<key>"
116+
models = ["deepseek-v4-pro"]
117+
```
118+
119+
装配时构建「模型名 → provider 工厂」路由表
120+
(`appserver.NewModelRoutedClientFactory` + `cli.buildModelProviderRoutes`):
121+
**只改 `model` 一个键即切换后端**,`model_provider` 无需变动;未声明的模型
122+
落回默认 provider(ChatGPT 登录 / OPENAI_API_KEY)。
123+
124+
新增厂商 = 在配置里加一个 `[model_providers.X]` 块,任何 OpenAI 兼容
125+
chat API(Qwen、Moonshot、本地 vLLM/Ollama 网关……)均适用。
126+
127+
### 3.4 /model 选择器(TUI)
128+
129+
- 入口:TUI 内输入 `/model`;
130+
- 列表 = 内置 OpenAI 目录(gpt-5.5 / 5.4 / 5.4-mini / 5.3-codex / 5.2)
131+
+ 各 provider 的 `models` 声明,支持输入过滤,标记 `(current)`;
132+
- 选中后三件事原子发生:写回 `config.toml``model` 键(持久化)、
133+
以模型覆盖开启新会话(`thread/start``Model` 参数,路由立即生效)、
134+
底部状态栏同步;
135+
- 实现:`internal/tui/model_picker.go` + `ChatBottomPane` 的 overlay 栈。
136+
137+
> 教训记录:overlay 回调最初在 Update 调用栈内同步调用
138+
> `Program.Send`(无缓冲通道),导致事件循环死锁(选中即整屏冻结)。
139+
> 修复为回调延迟到 `tea.Cmd`(bubbletea 在事件循环外执行),由
140+
> `TestPickerAcceptDoesNotSendSynchronously` 以真实死锁形态锁定回归。
141+
142+
### 3.5 能力对比
143+
144+
| 能力 | 原生 codex 0.136 | codexgo v0.2.x |
145+
|------|------|------|
146+
| OpenAI 模型(ChatGPT 套餐/API key) || ✅(同一 OAuth 链路) |
147+
| Responses API 自定义厂商 |||
148+
| **chat-only 厂商直连(GLM/DeepSeek/…)** | ❌ 需外挂代理 | ✅ 原生 |
149+
| 第三方模型的完整智能体能力(工具/沙箱) | —(取决于代理翻译质量) | ✅ 实测 |
150+
| 模型名自动路由后端 || ✅(`models` 扩展) |
151+
| TUI 内 /model 即时切换+持久化 | 仅 OpenAI 目录 | ✅ 含第三方模型 |
152+
153+
---
154+
155+
## 4. 版本管理与发布流程(2026-06-06 起生效)
156+
157+
```
158+
改动 → 升 ./VERSION(semver:功能→次版本,修复→补丁)
159+
→ 本地 commit
160+
→ ./scripts/deploy-local.sh(按 VERSION 构建 → /opt/homebrew/bin/codexgo)
161+
→ 【维护者在本机验证】
162+
→ 通过后:git push + 打 v<VERSION> 标签
163+
```
164+
165+
- `VERSION` 文件是版本唯一来源,经 ldflags 注入二进制
166+
(`codexgo --version` 可见);
167+
- 未经验证的提交**不出本机**
168+
169+
---
170+
171+
## 5. 质量保障
172+
173+
- **单元/集成测试**:协议翻译、SSE 聚合、路由表、选择器交互、配置持久化、
174+
死锁回归均有专项测试;全量 `go test -race ./...` 常绿
175+
(已知例外:`internal/uds` 在本机因 sockaddr_un 路径长度限制失败,
176+
干净树上同样存在,非回归信号);
177+
- **差分测试**:`CODEX_PARITY_BIN` 指向真实 codex 0.136 二进制,
178+
请求体逐字节对比、TUI 首帧逐格对比(strict 等级含 SGR 属性);
179+
- **分叉登记**:所有有意偏离上游之处集中登记于 `DEVIATIONS.md`
180+
(身份隔离、wire_api chat、models 路由、/model 选择器、模拟光标等),
181+
目标是零未解释差异。
182+
183+
---
184+
185+
## 6. 已知限制与路线
186+
187+
|| 状态 |
188+
|----|------|
189+
| /model 选择器的推理力度(effort)选择 | 未做(上游选择器具备),按需跟进 |
190+
| TUI 斜杠命令覆盖 | 已接:/model /new /quit /clear /compact /rename /review;其余显示「not supported yet」提示,按使用优先级接线 |
191+
| 界面 "codex" 文案品牌化 | 独立专项(涉及 TUI golden 重录) |
192+
| 第三方模型多模态(图片) | chat 翻译层暂丢弃图片内容 |
193+
| chat 厂商的用量统计 | 依赖厂商在流中带 usage,缺失时容忍为空 |
194+
195+
---
196+
197+
## 7. 关键文件索引
198+
199+
| 文件 | 职责 |
200+
|------|------|
201+
| `internal/brand/brand.go` | 本地身份常量(目录/环境变量前缀/钥匙串/插件清单) |
202+
| `internal/api/chat_completions.go` | chat SSE 流客户端与事件聚合 |
203+
| `internal/core/client_chat.go` | Prompt → chat 请求翻译(消息/工具/角色) |
204+
| `internal/tools/chat_api.go` | 工具 spec → chat function tools |
205+
| `internal/appserver/model_client_factory.go` | wire 分支 + 模型路由工厂 |
206+
| `internal/cli/assembly.go` | 路由表构建、provider 认证链、默认模型解析 |
207+
| `internal/cli/model_picker_host.go` | 选择器条目构建 + config 持久化 |
208+
| `internal/tui/model_picker.go` | /model 选择器 UI |
209+
| `internal/tui/overlay_list.go` | 通用列表 overlay(回调延迟执行) |
210+
| `VERSION` / `scripts/deploy-local.sh` | 版本与本地部署 |
211+
| `DEVIATIONS.md` | 有意分叉登记(英文,权威) |

0 commit comments

Comments
 (0)