codex-proxy / docs /implementation-notes.md
icebear0828
Initial commit: Codex Proxy with quota API and Cloudflare bypass
5d0a52f
|
raw
history blame
8.24 kB
# Codex Proxy 实现记录
## 项目目标
将 Codex Desktop App(免费)的 API 访问能力提取出来,暴露为标准的 OpenAI `/v1/chat/completions` 兼容接口,使任何支持 OpenAI API 的客户端都能直接调用。
---
## 关键发现:WHAM API vs Codex Responses API
### 最初的方案(失败)
项目最初使用 **WHAM API**`/backend-api/wham/tasks`)作为后端。这是 Codex Cloud 模式使用的 API,工作流程为:
1. 创建 cloud environment(需要绑定 GitHub 仓库)
2. 创建 task → 得到 task_id
3. 轮询 task turn 状态直到完成
4. 从 turn 的 output_items 中提取回复
**失败原因**
- 免费账户没有 cloud environment
- `listEnvironments` 返回 500
- `worktree_snapshots/upload_url` 返回 404(功能未开启)
- 创建的 task 立即失败,返回 `unknown_error`
### 发现真正的 API
通过分析 Codex Desktop 的 CLI 二进制文件(`codex.exe`)中的字符串,发现 CLI 实际使用的是 **Responses API**,而不是 WHAM API。
进一步测试发现正确的端点是:
```
POST https://chatgpt.com/backend-api/codex/responses
```
#### 端点探索过程
| 端点 | 状态 | 说明 |
|------|------|------|
| `/backend-api/responses` | 404 | 不存在 |
| `api.openai.com/v1/responses` | 401 | ChatGPT token 没有 API scope |
| **`/backend-api/codex/responses`** | **400 → 200** | **正确端点** |
#### 必需字段(逐步试错发现)
1. 第一次请求 → `400: "Instructions are required"` → 需要 `instructions` 字段
2. 加上 instructions → `400: "Store must be set to false"` → 需要 `store: false`
3. 加上 store: false → `400: "Stream must be set to true"` → 需要 `stream: true`
4. 全部加上 → `200 OK`
---
## API 格式
### 请求格式
```json
{
"model": "gpt-5.1-codex-mini",
"instructions": "You are a helpful assistant.",
"input": [
{ "role": "user", "content": "你好" }
],
"stream": true,
"store": false,
"reasoning": { "effort": "medium" }
}
```
**关键约束**
- `stream` 必须为 `true`(不支持非流式)
- `store` 必须为 `false`
- `instructions` 必填(对应 system message)
### 响应格式(SSE 流)
Codex Responses API 返回标准的 OpenAI Responses API SSE 事件:
```
event: response.created
data: {"type":"response.created","response":{"id":"resp_xxx","status":"in_progress",...}}
event: response.output_text.delta
data: {"type":"response.output_text.delta","delta":"你","item_id":"msg_xxx",...}
event: response.output_text.delta
data: {"type":"response.output_text.delta","delta":"好","item_id":"msg_xxx",...}
event: response.output_text.done
data: {"type":"response.output_text.done","text":"你好!",...}
event: response.completed
data: {"type":"response.completed","response":{"id":"resp_xxx","status":"completed","usage":{...},...}}
```
主要事件类型:
- `response.created` — 响应开始
- `response.in_progress` — 处理中
- `response.output_item.added` — 输出项添加(reasoning 或 message)
- `response.output_text.delta`**文本增量(核心内容)**
- `response.output_text.done` — 文本完成
- `response.completed` — 响应完成(包含 usage 统计)
### 认证方式
使用 Codex Desktop App 的 ChatGPT OAuth JWT token,需要以下请求头:
```
Authorization: Bearer <jwt_token>
ChatGPT-Account-Id: <account_id>
originator: Codex Desktop
User-Agent: Codex Desktop/260202.0859 (win32; x64)
Content-Type: application/json
Accept: text/event-stream
```
---
## 代码实现
### 新增文件
#### `src/proxy/codex-api.ts` — Codex Responses API 客户端
负责:
- 构建请求并发送到 `/backend-api/codex/responses`
- 解析 SSE 流,逐个 yield 事件对象
- 错误处理(超时、HTTP 错误等)
```typescript
// 核心方法
async createResponse(request: CodexResponsesRequest): Promise<Response>
async *parseStream(response: Response): AsyncGenerator<CodexSSEEvent>
```
#### `src/translation/openai-to-codex.ts` — 请求翻译
将 OpenAI Chat Completions 请求格式转换为 Codex Responses API 格式:
| OpenAI Chat Completions | Codex Responses API |
|------------------------|---------------------|
| `messages[role=system]` | `instructions` |
| `messages[role=user/assistant]` | `input[]` |
| `model` | `model`(经过 resolveModelId 映射) |
| `reasoning_effort` | `reasoning.effort` |
| `stream` | 固定 `true` |
| — | `store: false`(固定) |
#### `src/translation/codex-to-openai.ts` — 响应翻译
将 Codex Responses SSE 流转换为 OpenAI Chat Completions 格式:
**流式模式** (`streamCodexToOpenAI`):
```
Codex: response.output_text.delta {"delta":"你"}
OpenAI: data: {"choices":[{"delta":{"content":"你"}}]}
Codex: response.completed
OpenAI: data: {"choices":[{"delta":{},"finish_reason":"stop"}]}
OpenAI: data: [DONE]
```
**非流式模式** (`collectCodexResponse`):
- 消费整个 SSE 流,收集所有 text delta
- 拼接为完整文本
- 返回标准 `chat.completion` JSON 响应(包含 usage)
### 修改文件
#### `src/routes/chat.ts` — 路由处理器(重写)
**之前**:使用 WhamApi 创建 task → 轮询 turn → 提取结果
**之后**:使用 CodexApi 发送 responses 请求 → 直接流式/收集结果
核心流程简化为:
```
1. 验证认证
2. 解析请求 (ChatCompletionRequestSchema)
3. translateToCodexRequest() 转换格式
4. codexApi.createResponse() 发送请求
5a. 流式:streamCodexToOpenAI() → 逐块写入 SSE
5b. 非流式:collectCodexResponse() → 返回 JSON
```
#### `src/index.ts` — 入口文件(简化)
移除了 WHAM environment 自动发现逻辑(不再需要)。
---
## 之前修复的 Bug(WHAM 阶段)
在切换到 Codex Responses API 之前,还修复了 WHAM API 相关的三个 bug:
1. **`turn_status` vs `status` 字段名不匹配** — WHAM API 返回 `turn_status`,但代码检查 `status`,导致轮询永远不匹配,超时 300 秒
2. **`getTaskTurn` 响应结构嵌套** — API 返回 `{ task, user_turn, turn }` 但代码把整个响应当作 `WhamTurn`,导致 `output_items``undefined`
3. **失败的 turn 返回 200 空内容** — 没有检查 `failed` 状态,直接返回空 content
这些修复在 `src/types/wham.ts``src/proxy/wham-api.ts``src/translation/stream-adapter.ts` 中。
---
## 测试结果
### 非流式请求
```bash
curl http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"gpt-5.1-codex-mini","messages":[{"role":"user","content":"Say hello"}]}'
```
```json
{
"id": "chatcmpl-3125ece443994614aa7b1136",
"object": "chat.completion",
"choices": [{"message": {"role": "assistant", "content": "Hello!"}, "finish_reason": "stop"}],
"usage": {"prompt_tokens": 22, "completion_tokens": 20, "total_tokens": 42}
}
```
响应时间:~2 秒
### 流式请求
```bash
curl http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"gpt-5.1-codex-mini","messages":[{"role":"user","content":"Say hello"}],"stream":true}'
```
```
data: {"choices":[{"delta":{"role":"assistant"}}]}
data: {"choices":[{"delta":{"content":"Hello"}}]}
data: {"choices":[{"delta":{"content":"!"}}]}
data: {"choices":[{"delta":{},"finish_reason":"stop"}}]}
data: [DONE]
```
首 token 时间:~500ms
---
## 文件结构总览
```
src/
├── proxy/
│ ├── codex-api.ts ← 新增:Codex Responses API 客户端
│ ├── client.ts (通用 HTTP 客户端,保留)
│ └── wham-api.ts (WHAM 客户端,保留但不再使用)
├── translation/
│ ├── openai-to-codex.ts ← 新增:Chat Completions → Codex 格式
│ ├── codex-to-openai.ts ← 新增:Codex SSE → Chat Completions 格式
│ ├── openai-to-wham.ts (旧翻译器,保留)
│ ├── stream-adapter.ts (旧流适配器,保留)
│ └── wham-to-openai.ts (旧翻译器,保留)
├── routes/
│ └── chat.ts ← 重写:使用 Codex API
├── index.ts ← 简化:移除 WHAM env 逻辑
└── ...
```