mirror of
https://github.com/zyycn/codex-proxy-rs.git
synced 2026-11-06 08:06:19 +00:00
fix: add resilient Responses WebSocket transport
Expose the official Responses WebSocket endpoint so Codex can retry interrupted turns without proxy-side response splicing. Preserve upstream frames under backpressure, report pump exit reasons, organize Responses handlers by transport, and poll fingerprint updates daily. Keep the warning policy in the backend manifest instead of a repository-wide Cargo config. Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
+14
-9
@@ -6,7 +6,7 @@
|
||||
|
||||
Codex Proxy RS 是一个单进程网关。Rust 进程同时提供:
|
||||
|
||||
- OpenAI Responses 兼容接口:`/v1/*`
|
||||
- OpenAI Responses 兼容接口:`/v1/*`,包括 HTTP/SSE 和官方 Responses WebSocket
|
||||
- 管理端接口:`/api/admin/*`
|
||||
- Vue 管理端静态资源
|
||||
- PostgreSQL、Redis 健康检查:`/healthz`
|
||||
@@ -294,10 +294,12 @@ api/
|
||||
├── client/
|
||||
│ ├── router.rs
|
||||
│ ├── auth.rs
|
||||
│ ├── responses.rs
|
||||
│ ├── responses/
|
||||
│ │ ├── mod.rs
|
||||
│ │ ├── sse.rs
|
||||
│ │ └── websocket.rs
|
||||
│ ├── models.rs
|
||||
│ ├── errors.rs
|
||||
│ └── sse.rs
|
||||
│ └── errors.rs
|
||||
└── admin/
|
||||
├── router.rs
|
||||
├── response.rs
|
||||
@@ -315,7 +317,7 @@ api/
|
||||
- `router.rs`:组合客户端 API、管理端 API、SPA 静态资源和 `/healthz`。
|
||||
- `middleware/request_id.rs`:接收或生成请求 ID,并写入响应头。
|
||||
- `middleware/trace.rs`:HTTP 访问日志。
|
||||
- `client`:`/v1` 路由、Bearer API Key 鉴权、Responses 请求解析和 SSE 输出。
|
||||
- `client`:`/v1` 路由、Bearer API Key 鉴权和 Responses 入站协议。`responses/mod.rs` 负责共享请求构造,`sse.rs` 与 `websocket.rs` 只处理各自的下游传输。
|
||||
- `admin`:`/api/admin` 路由、会话或管理员 API Key 鉴权、统一响应结构。所有管理端响应带 `Cache-Control: no-store`。
|
||||
- `assets.rs`:静态文件服务和 Vue Router history fallback;未知 `/api`、`/v1` 路径不会回退到 SPA。
|
||||
|
||||
@@ -370,11 +372,11 @@ bootstrap/
|
||||
|
||||
## 代理请求链路
|
||||
|
||||
一次 `POST /v1/responses` 请求经过以下步骤:
|
||||
一次 `POST /v1/responses` 请求或 WebSocket `response.create` 消息经过以下步骤:
|
||||
|
||||
1. 请求 ID 中间件建立全链路标识,访问日志记录基础 HTTP 信息。
|
||||
2. `client::auth` 从 `Authorization: Bearer sk_...` 取出完整 Key,并在 PostgreSQL 校验是否存在且启用。
|
||||
3. API 层解析请求,提取客户端 IP、User-Agent 和 transport-only 参数。
|
||||
2. `client::auth` 从 `Authorization: Bearer sk_...` 取出完整 Key,并在 PostgreSQL 校验是否存在且启用。WebSocket 在 HTTP upgrade 前完成鉴权。
|
||||
3. API 层解析 HTTP JSON 或官方 `response.create` 文本帧,提取客户端 IP、User-Agent 和 transport-only 参数。
|
||||
4. `models` 解析模型别名;`dispatch` 建立会话变体标识和 previous response 恢复计划。
|
||||
5. `AccountAttemptLedger` 按当前调度策略冻结完整候选顺序。会话亲和账号可以排在前面,但不会绕过可用性检查。
|
||||
6. 账号池租用并发槽位,必要时先刷新 quota;请求间隔在发送上游前执行。
|
||||
@@ -425,7 +427,9 @@ replay 按轮保存增量输入和输出,不保存每一轮累计的完整输
|
||||
|
||||
超过任一边界时不再生成新的 replay 节点,正常代理响应不因此失败。
|
||||
|
||||
流式请求只有在响应尚未提交给客户端时才能透明恢复或换号。首段预取会识别终止事件和账号级失败;一旦真实输出已经发给客户端,后续断流只能以 SSE 失败结束,不能在另一账号上重放后拼接到已有输出。
|
||||
流式请求只有在响应尚未提交给客户端时才能透明恢复或换号。首段预取会识别终止事件和账号级失败;一旦真实输出已经发给客户端,代理会用 `response.failed` 结束当前 SSE 或 WebSocket response,不能在另一账号上重放后拼接到已有输出。
|
||||
|
||||
Responses 协议没有按事件序号续传未完成 response 的请求字段。官方 Codex 在 turn 层持有完整会话历史和工具执行状态,收到可重试的断流后会建立新请求重新采样。代理保留这一责任边界:新请求仍进入账号池调度和会话恢复,但不会伪造同一个 response 的续传。
|
||||
|
||||
## 账号作用域身份
|
||||
|
||||
@@ -549,6 +553,7 @@ frontend/src/
|
||||
| --- | --- |
|
||||
| `GET /healthz` | PostgreSQL 和 Redis 健康检查,成功返回 `204` |
|
||||
| `POST /v1/responses` | Responses 非流式或 SSE 流式入口 |
|
||||
| `GET /v1/responses` | 官方 Responses WebSocket upgrade 入口 |
|
||||
| `POST /v1/responses/review` | Review 请求入口 |
|
||||
| `POST /v1/responses/compact` | Compact 请求入口 |
|
||||
| `GET /v1/models*` | 模型列表、详情、catalog 和运行信息 |
|
||||
|
||||
Reference in New Issue
Block a user