Files
codex-retry-gateway/README.md
T

424 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Codex Retry Gateway
- 文档层级:app 项目真源
- 应用性质:自有 infra 工具
- 源码来源:git submodule
- 运行关系:多实例服务
- 部署模型:managed_systemd_apps
- 对应服务:`services/pc/systemd-user/codex-retry-gateway/current/README.md``services/a100/systemd-user/codex-retry-gateway/current/README.md`
- 对应 stack:无
- Secret 边界:本地用户配置
- 数据来源:本地 Codex provider 配置、gateway profile state 与上游 OpenAI-compatible API 响应。
- 输出边界:仅返回代理后的 Codex API 响应和本地健康/状态信息。
- 写入边界:只写本地用户态 gateway 状态目录。
- 本地状态路径:`~/.codex-retry-gateway/`
- 备注:token 与 provider secret 只保留在目标用户配置或 secret 文件中,不进入 repo。
一个不依赖 `cc-switch` 路由模式的独立本地网关。
目标:
- 保持 Codex 继续使用现有 `auth.json`
- 只把 `config.toml` 的当前 provider `base_url` 改成本地网关
- 非流式命中 `reasoning_tokens = 516` 时返回 `502`
- 上游若返回明确的容量错误(默认匹配错误文案 `Selected model is at capacity. Please try a different model.``stream disconnected before completion: Concurrency limit exceeded for account, please retry later`,以及 Responses 错误码 `server_is_overloaded` / `slow_down`),也会自动重试;重试耗尽后转成本地 `502`
- 除了 `429/503` JSON 错误响应,也会识别 `200` 但返回体本质是错误、以及流式失败事件里携带同样文案的情况
- 流式命中时默认先缓存并判断;一旦命中 `516`,统一返回 `502`
- 流式成功响应在严格检查模式下仍会先缓存完成;成功后会按真实且规范化的 Responses 生命周期与输出顺序逐块回放给 Codex,每个 SSE 块之间至少间隔 5ms,并关闭 TCP 小包聚合,避免大量 delta 在同一事件循环突发到达;不会伪造缺少 response ID 的生命周期事件
- 默认同时拦截 root 路径和 `/v1` 路径:
- `/responses`
- `/chat/completions`
- `/v1/responses`
- `/v1/chat/completions`
限制:
- 这个网关不负责 `Responses``Chat Completions` 协议互转
- 如果你的上游本身不支持 Codex 当前使用的协议,这个网关不会替你补齐转换能力
## 默认路径
Windows:
- Codex 配置:`%USERPROFILE%\.codex\config.toml`
- Gateway 状态目录:`%USERPROFILE%\.codex-retry-gateway`
macOS / Linux:
- Codex 配置:`~/.codex/config.toml`
- Gateway 状态目录:`~/.codex-retry-gateway`
## 当前版本说明
- 这是一个可独立发布、独立运行的仓库
- 默认监听地址是 `http://127.0.0.1:4610`
- 默认示例上游见 `config.example.json`
- 实际运行时配置会写到当前用户目录下的 gateway 状态目录
## 一键启动并打开管理页
在仓库根目录执行:
Windows:
```powershell
powershell -ExecutionPolicy Bypass -File .\scripts\launch-ui.ps1
```
macOS / Linux:
```bash
bash ./scripts/launch-ui.sh
```
这个脚本是默认入口,执行后会自动完成:
- 第一次运行时:
- 备份当前用户目录下的 Codex `config.toml`
- 生成当前用户目录下的 gateway `config.json`
- 启动本地 gateway
- 把当前 `model_provider` 对应的 `base_url` 改到本地 gateway
- 之后再次运行时:
- 自动复用现有安装状态
- 自动重启或拉起 gateway
- 自动再次打开管理页
默认会打开:
```text
http://127.0.0.1:4610/__codex_retry_gateway/ui
```
如果你只想启动、不自动开浏览器:
```powershell
powershell -ExecutionPolicy Bypass -File .\scripts\launch-ui.ps1 -NoOpen
```
```bash
bash ./scripts/launch-ui.sh --no-open
```
常用参数:
- Windows 参数:
- `-CodexConfigPath`
- `-StateRoot`
- `-ListenHost`
- `-ListenPort`
- `-NoOpen`
- macOS / Linux 参数:
- `--codex-config-path`
- `--state-root`
- `--listen-host`
- `--listen-port`
- `--no-open`
macOS / Linux 说明:
- 需要 `bash`
- 需要 `Node.js 18+`
- Unix 入口会调用跨平台 `node` 管理核心,不依赖 PowerShell
- 推荐显式使用 `bash ...sh`
- 这样即使目录是从 Windows 或压缩包复制过来、没有可执行位,也能直接运行
## 手工安装入口
如果你明确只想做脚本级安装,不想自动打开 UI,也可以直接执行:
Windows:
```powershell
powershell -ExecutionPolicy Bypass -File .\scripts\install-for-current-provider.ps1
```
macOS / Linux:
```bash
bash ./scripts/install-for-current-provider.sh
```
## Linux systemd + profile 启动
在 Linux 上需要常驻运行时,推荐用 `scripts/run-profile.mjs` 作为 systemd 的 ExecStart。它会读取 profile env,生成运行时 `config.json`,并把当前 Codex provider 的 `base_url` 指向本机 gateway。
文本 profile env 默认放在:
```text
~/.config/codex-retry-gateway/profiles/<profile>.env
```
图片 profile env 独立放在:
```text
~/.config/codex-retry-gateway/image-profiles/<profile>.env
```
当前文本 profile 与当前图片 profile 分别由 state 中的 `profile_name``image_profile_name` 记录;切换其中任意一方不会改写另一方。首次升级时,当前文本 profile 中已有的 `CODEX_RETRY_GATEWAY_IMAGE_*` 字段会复制到同名图片 profile,旧文本文件会保留作为兼容回退,但后续 UI/TUI 保存文本 profile 不再写入图片字段。
常用字段:
- `CODEX_RETRY_GATEWAY_LISTEN_HOST`
- `CODEX_RETRY_GATEWAY_LISTEN_PORT`
- `CODEX_RETRY_GATEWAY_UPSTREAM_BASE_URL`
- `CODEX_RETRY_GATEWAY_REASONING_EQUALS`
- `CODEX_RETRY_GATEWAY_RETRYABLE_STATUS_CODES`
- `CODEX_RETRY_GATEWAY_RETRYABLE_ERROR_MESSAGES`
- `CODEX_RETRY_GATEWAY_UPSTREAM_AUTH_MODE`
- `CODEX_RETRY_GATEWAY_UPSTREAM_AUTH_ENV`
- `CODEX_RETRY_GATEWAY_UPSTREAM_AUTH_FILE`
- `CODEX_RETRY_GATEWAY_UPSTREAM_AUTH_JSON_PATH`
- `CODEX_RETRY_GATEWAY_UPSTREAM_AUTH_JSON_KEY`
- `CODEX_RETRY_GATEWAY_REQUEST_HISTORY_LIMIT`
`CODEX_RETRY_GATEWAY_UPSTREAM_AUTH_MODE` 支持:
- `passthrough`:默认模式,透传 Codex 发来的 `Authorization`
- `manual_bearer`:在 UI 中手动填入一次 token/password,写入用户级受限 secret 文件,并覆盖上游 `Authorization: Bearer ...`
- `fixed_bearer`:从环境变量或文件读取 token,并覆盖上游 `Authorization: Bearer ...`
- `auth_json`:从 Codex `auth.json` 的指定 key 读取 token,并覆盖上游 `Authorization: Bearer ...`
图片 profile 常用字段:
- `CODEX_RETRY_GATEWAY_IMAGE_BASE_URL`
- `CODEX_RETRY_GATEWAY_IMAGE_AUTH_MODE`
- `CODEX_RETRY_GATEWAY_IMAGE_AUTH_ENV`
- `CODEX_RETRY_GATEWAY_IMAGE_AUTH_FILE`
- `CODEX_RETRY_GATEWAY_IMAGE_AUTH_JSON_PATH`
- `CODEX_RETRY_GATEWAY_IMAGE_AUTH_JSON_KEY`
- 配置 `CODEX_RETRY_GATEWAY_IMAGE_BASE_URL` 后,`/images/*``/v1/images/*` 会改用该地址;root 图片路径会在上游规范化为 `/v1/images/*`,其他请求仍使用 `CODEX_RETRY_GATEWAY_UPSTREAM_BASE_URL`
- 图片认证字段与普通上游同义,使用 `CODEX_RETRY_GATEWAY_IMAGE_AUTH_*`;默认 `fixed_bearer``CODEX_RETRY_GATEWAY_IMAGE_API_KEY` 读取 key。
- 不要把 key 直接写进 profile env;优先通过 `IMAGE_AUTH_FILE` 或管理页的 `manual_bearer` 写入用户级受限 secret 文件。
示例:
```bash
node ./scripts/run-profile.mjs default
```
指定图片 profile
```bash
node ./scripts/run-profile.mjs default --image-profile images
```
如果已经由 state 记录了当前活跃 profile,也可以直接不传 profile 参数:
```bash
node ./scripts/run-profile.mjs --prefer-state-profile
```
## 测试与发布
这个服务会改写目标用户的 Codex `base_url`,属于当前操作链路上的高风险本地网关。后续变更默认按“先对侧宿主、再当前宿主”发布。
推荐顺序:
1. 先部署 `a100` 上的 `codex-retry-gateway`
2. 在对侧机器的目标用户环境里发起真实 Codex 冒烟请求,不要先改当前正在依赖本机 `4610` 的会话。
3. 确认健康检查、响应完整性与关键重试行为正常。
4. 通过后再部署本机,例如 `pc`
推荐部署命令:
```bash
uv run ansible-playbook playbooks/deploy_managed_systemd_apps.yml -i inventory/hosts.yml --limit a100 -e managed_systemd_apps_selected_names=codex-retry-gateway
```
验证通过后,再切到本机:
```bash
uv run ansible-playbook playbooks/deploy_managed_systemd_apps.yml -i inventory/hosts.yml --limit pc -e managed_systemd_apps_selected_names=codex-retry-gateway
```
推荐冒烟命令:
```bash
codex --dangerously-bypass-approvals-and-sandbox -c model="gpt-5.4" hello
```
建议同时检查:
- `curl http://<listen-host>:4610/__codex_retry_gateway/health`
- `/responses` 成功流能完整结束,不会卡 pending;`response.created` 必须带真实 response ID,随后依次可见 `response.in_progress`、输出 delta 与 `response.completed`
- 容量错误 `Selected model is at capacity. Please try a different model.` 仍能按网关策略自动重试
如果当前对话依赖本机 `4610`,不要把本机作为首个发布目标;先在另一台已接管相同 profile 的机器验证,再回到本机切换。
## 如何恢复
Windows:
```powershell
powershell -ExecutionPolicy Bypass -File .\scripts\restore-codex-config.ps1
```
macOS / Linux:
```bash
bash ./scripts/restore-codex-config.sh
```
这个脚本会:
- 停掉本地 gateway
- 用最近一次备份恢复当前用户目录下的 Codex `config.toml`
- 删除当前安装状态文件
## 管理页面
页面入口:
```text
http://127.0.0.1:4610/__codex_retry_gateway/ui
```
UI 现在是独立的 Vite + React + TypeScript 前端:
- 源码:`ui-src/`
- 构建产物:`public/ui/`
- 本地开发:`npm run dev:ui`
- 生产构建:`npm install && npm run build:ui`
gateway 运行时只负责 API 与静态文件服务,不再把复杂 UI 硬写在 `gateway.mjs` 字符串里。
页面里可以直接做这几件事:
- 看当前监听地址、真实上游、当前 provider、当前 Codex base URL
- 看基于持久请求记录累计的实时统计
- 代理请求总数
- 被检查响应总数
- 累计 input / output / total / reasoning tokens
- `516` 命中次数
- `516` 占比
- 看最近请求记录
- 请求时间戳、首字耗时、总耗时、请求体大小、路径、模型、状态码
- 相同重发请求会带相同的 `request_id`
- `usage` 中的 input / output / total / reasoning tokens
- 分别管理文本 profiles 与图片 profiles
- 各自新建 / 编辑 / 探测 / 切换 / 删除 profile env
- 文本切换 provider `base_url` 不会改写图片分流,图片切换只影响 `/images/*``/v1/images/*`
- 各自切换 `passthrough` / `manual_bearer` / `fixed_bearer` / `auth_json` 认证模式
-`reasoning_equals`
- 改 capacity error 的匹配状态码和错误文案
-`endpoints`
-`non_stream_status_code`
- 开关 `log_match`
- 动态查看当前 gateway 的实时日志
- 一键恢复 Codex 原设置
说明:
- 页面保存配置后会立即热生效,不需要重启 gateway
- 页面点“恢复 Codex 原设置并关闭网关”后,当前页面会失联,这是预期行为
- 日常恢复优先用 UI`restore-codex-config.ps1` 作为脚本级应急回滚入口保留
- UI 恢复不会再额外拉起恢复子进程,而是由当前 gateway 直接完成恢复并退出
- 统计口径默认按持久请求记录累计,重启后会从 `requests.sqlite` / `requests.jsonl` 回灌
- `516` 占比 = `reasoning_tokens = 516` 的响应次数 / 被检查响应总数
- 请求历史只记录元数据、请求体字节数和 token usage,不保存请求正文或响应正文;默认展示最近 200 条
- gateway 日志持久化到 `~/.codex-retry-gateway/logs/gateway.log`
- 请求记录持久化到 `~/.codex-retry-gateway/logs/requests.jsonl``~/.codex-retry-gateway/logs/requests.sqlite`,重启后会用于 UI 请求页以及 overview 的累计 token / reasoning 统计
- `manual_bearer` 的手动 token/password 只写入系统 secret 文件,API/UI 不读回明文;profile env 只保存 secret 文件路径
- 其他 profile env 不保存明文 `sk-...`;固定密钥请使用 env/file 引用,或用 `auth_json` 指向 `auth.json` 字段名
## 如何调整拦截条件
编辑:
```text
Windows: %USERPROFILE%\.codex-retry-gateway\config\config.json
macOS / Linux: ~/.codex-retry-gateway/config/config.json
```
常用字段:
- `reasoning_equals`
- 例如 `[516]`
- `retryable_status_codes`
- 默认 `[429, 503]`
- `retryable_error_messages`
- 默认包含 `Selected model is at capacity. Please try a different model.`
- 也默认包含 `stream disconnected before completion: Concurrency limit exceeded for account, please retry later`
- 只要上游 JSON 错误里包含这些文案之一,gateway 就会把上游错误翻成本地 `non_stream_status_code`
- `endpoints`
- 默认包含 root 与 `/v1` 两套路径
- `non_stream_status_code`
- 默认 `502`
- `stream_action`
- 默认 `strict_502`
- `strict_502`:先缓存整个流,命中 `516` 时统一返回 `502`
- `disconnect`:兼容旧行为;若命中发生在已透传 chunk 之后,则直接断开连接
- `log_match`
- 是否记录命中日志
改完后重启:
```powershell
powershell -ExecutionPolicy Bypass -File .\scripts\start-gateway.ps1 -RestartIfRunning
```
```bash
bash ./scripts/start-gateway.sh --restart-if-running
```
如果你已经打开管理页,优先直接在页面里改,通常不需要手改 `config.json`
## 其他机器如何应用
在其他 Windows 机器上:
1. 复制整个仓库目录
2. 确保本机有 `Node.js 18+`
3. 不需要安装 `cc-switch`,也不需要使用 `cc-switch` 路由模式
4. 在仓库根目录执行 `powershell -ExecutionPolicy Bypass -File .\scripts\launch-ui.ps1`
5. 如需回滚,优先在 UI 里点“恢复 Codex 原设置并关闭网关”;脚本级回滚仍可执行 `powershell -ExecutionPolicy Bypass -File .\scripts\restore-codex-config.ps1`
在其他 macOS / Linux 机器上:
1. 复制整个仓库目录
2. 确保本机有 `bash`
3. 确保本机有 `Node.js 18+`
4. 不需要安装 `cc-switch`,也不需要使用 `cc-switch` 路由模式
5. 在仓库根目录执行 `bash ./scripts/launch-ui.sh`
6. 如需回滚,优先在 UI 里点“恢复 Codex 原设置并关闭网关”;脚本级回滚仍可执行 `bash ./scripts/restore-codex-config.sh`
运行时状态默认写到当前用户目录:
```text
Windows: %USERPROFILE%\.codex-retry-gateway
macOS / Linux: ~/.codex-retry-gateway
```
## 已验证事项
- `test-gateway-e2e.ps1`
- 已通过
- 验证 `/responses``/chat/completions``/v1/responses``/v1/chat/completions`
- `node scripts/test-gateway-e2e.mjs`
- 已通过
- 验证 strict capture 下 `/responses` 生命周期不注入空事件,长流 delta 按序完整回放并以 `response.completed` 结束
- `test-install-restore.ps1`
- 已通过
- 验证安装、透传、UI 页面、热更新配置、实时日志、516 统计、恢复闭环
- `test-launch-ui.ps1`
- 已通过
- 验证首次一键启动自动安装、再次启动自动复用、UI 可访问、默认 516 拦截仍生效
- `test-launch-ui-unix.ps1`
- 已通过
- 在当前 Windows 主机的 Bash 环境里验证 Unix `.sh` 入口能完成启动、透传、恢复闭环
- `bash ./scripts/launch-ui.sh --no-open`
- 已通过
- 当前机器实测返回 `mode=reuse`
- 后续 `GET /__codex_retry_gateway/health``GET /__codex_retry_gateway/ui``GET /v1/models` 都返回 `200`
- `codex exec`
- 已通过
- 在 Bash 默认入口重新拉起 gateway 后,当前机器再次返回 `OK`
- 当前实机验证示例
- `GET http://127.0.0.1:4610/__codex_retry_gateway/health` 已通过
- `GET http://127.0.0.1:4610/v1/models` 已通过,并成功透传到配置里的真实上游
- `GET http://127.0.0.1:4610/__codex_retry_gateway/ui` 已实际打开并确认页面内容
- `codex exec` 历史现象
- gateway 关闭时,真实报错地址为 `http://127.0.0.1:4610/responses`
- gateway 恢复后,`codex exec` 已再次成功返回 `OK`