让 Codex CLI 接上多种上游后端的本地网关。
Codex CLI 只能走 OpenAI Responses API。本网关在本地提供 Responses 兼容端点(/v1/responses、/v1/models),按源配置把请求转到不同上游,再以 Responses SSE 回给 Codex。Codex 全程无感。
支持三类上游(可混排故障转移):
backend_type |
上游 | 路径 |
|---|---|---|
a(默认) |
Anthropic Messages | Responses → Messages → Responses SSE |
c |
OpenAI Chat Completions | Responses → Chat → Responses SSE |
r |
OpenAI Responses 透传 | 最小改写透传 + 出站 model 别名回写 |
Codex CLI
│ POST /v1/responses (OpenAI Responses 格式)
▼
codex-api-gateway ── 协议适配 / 透传 + 多源路由 + 熔断
├─ a → Anthropic Messages (/v1/messages, SSE)
├─ c → OpenAI Chat Completions (/chat/completions, SSE)
└─ r → OpenAI Responses 透传 (/responses, SSE)
除默认 Anthropic(a)外,源可配置为 OpenAI Chat Completions 兼容后端(c)。Responses 透传见下一节 r:
sources:
- name: openai-compat
base_url: https://api.openai.com/v1 # 填 OpenAI SDK 的 base_url,不要带 /chat/completions
api_key: ${OPENAI_API_KEY}
backend_type: c
model_map: { gpt-5: gpt-4o }
default_model: gpt-4obase_url 示例:OpenAI …/v1、DeepSeek https://api.deepseek.com、智谱 https://open.bigmodel.cn/api/paas/v4、火山 https://ark.cn-beijing.volces.com/api/v3、百炼 https://dashscope.aliyuncs.com/compatible-mode/v1。客户端仍只访问网关的 /v1/responses。
当上游本身提供 OpenAI Responses API 时,配置 backend_type: r:网关映射 model、强制 stream,
并将上游 SSE 转回客户端(出站 response.model 回写为客户端模型别名)。可与 a/c 混排故障转移。
sources:
- name: openai-responses
base_url: https://api.openai.com/v1
api_key: ${OPENAI_API_KEY}
backend_type: r
model_map: { gpt-5: gpt-5 }
default_model: gpt-5- 多后端协议适配:
aAnthropic Messages 直转、cChat Completions 转换、rResponses 透传;客户端始终只走/v1/responsesSSE。 - 多源路由:多源按配置顺序优先级,运行时重建;a/c/r 可混排。
- 手动停用源:管理页一键停用/启用单源,即时写盘并热重载;停用源不参与调度,仍保留在配置与观测中。
- 首字节前故障转移:上游未开始流式输出前可切换到下一个源;出流后仅收到状态事件(
response.created/in_progress)、未产出任何内容事件(空响应)时仍可切换,一旦产出首个内容事件即锁定该源。 - 断路器:失败降级 → 熔断 → 冷却 → 半开探测 → 恢复,逐源可覆盖参数。
- 模型白名单:
/v1/models只返回models段显式声明的模型,不暴露上游别名。 - 结构化日志:等级过滤 + text/json 输出,全走
slog。 - 配置热重载:管理页保存,或编辑
config.yaml/ 同级base_instructions.md后fsnotify自动生效,无需重启。 - H5 管理页:观测台、配置编辑、中英文/明暗主题,挂载在根路径
/。所有配置都在网页里完成,无需手动写 YAML。 - 系统托盘:启动即常驻(含
-d后台模式),点开即用;headless / 托盘宿主异常时自动降级为信号模式,不影响服务运行;GATEWAY_NO_TRAY=1可显式禁用。 - 双击即用:打包为单文件后双击运行即可,首次启动自动生成默认配置,无需命令行、无需提前准备配置文件。
- 环境变量:YAML 内联
${ENV}展开 +CODEX_API_GATEWAY_前缀覆盖(可选,网页配置已足够)。
不需要命令行。 构建(或下载)出二进制后,双击运行即可。
- 把
codex-api-gateway二进制放到任意目录,双击打开。- 首次运行会在同目录自动生成
config.yaml(最小默认配置,未含任何上游源)。 - 进程启动后常驻在系统托盘,点托盘图标的「打开」菜单即可进入管理页。
- 首次运行会在同目录自动生成
- 浏览器打开管理页
http://localhost:8383/(或托盘菜单「打开」)。- 在配置管理里添加上游源(粘贴 API Key、填
base_url、设model_map),保存即热重载生效。 - 未配置上游源前,转发请求会返回 503,配好即恢复。
- 在配置管理里添加上游源(粘贴 API Key、填
- 把 Codex 的 base URL 指向网关根(含
/v1):
http://127.0.0.1:8383/v1
Codex 会自动在 base_url 后拼接 /responses 和 /models。不要把 base_url 写成 …/v1/responses——那样 /models 会打到 /v1/responses/models 返回 404,Codex 拉不到模型列表。
退出时右键托盘图标选「退出」,或直接关闭托盘进程即可。
托盘菜单提供 「开机自启」 勾选项(Linux / Windows / macOS):
- 右键托盘图标 → 勾选「开机自启」即写入系统登录自启;
- 再点一次取消勾选即关闭。
平台机制:
| 平台 | 注册位置 |
|---|---|
| Linux | ~/.config/autostart/codex-api-gateway.desktop |
| Windows | HKCU\...\CurrentVersion\Run |
| macOS | ~/Library/LaunchAgents/codex-api-gateway.plist |
自启命令为当前可执行文件 + -config <绝对路径> -chdir-home,进程启动后自动切换到用户目录($HOME,与直接从用户目录启动一致),登录图形会话后自动带上 DISPLAY/WAYLAND_DISPLAY,托盘「打开」管理页可正常冷启动浏览器。
不要用 systemd --user 无图形环境拉起网关——浏览器冷启动会静默失败。若以前启用过:
systemctl --user disable --now codex-api-gateway.service可选:task install-autostart 仍可安装应用菜单快捷方式(兼写 autostart desktop,与托盘开关读写同一文件)。
托盘菜单提供 「应用到 Codex」 勾选项:勾选后把 Codex CLI 的用户配置
$CODEX_HOME/config.toml 指向本网关(新增 model_providers.codex-api-gateway
并置 model_provider = "codex-api-gateway",顶层 model_catalog_json 指向
$CODEX_HOME/models.json,base_url 自动取当前监听端口);取消勾选恢复启用前的
model_provider 与 model_catalog_json 原值。
- 启用前的原值备份在
~/.codex/codex-api-gateway-backup.json,恢复后自动删除。 - 备份文件异常缺失时,取消勾选会移除网关注入键并回落到 Codex 默认 provider(对应 WARN 日志)。
config.toml不存在时不自动创建,请先运行一次 codex 生成配置。- 网关监听端口变更后,重新勾选一次即可刷新 provider 块的
base_url。 - 管理页新增/删除/排序模型并保存后,网关会同步刷新
$CODEX_HOME/models.json, 文件内容与/v1/models一致,Codex 通过model_catalog_json读取最新模型目录。
一般不需要手动配置:直接用上方托盘「应用到 Codex」勾选即可。以下内容保留给脚本化或特殊场景参考。
方式一:改 ~/.codex/config.toml
在 [model_providers] 下加一个自定义 provider,base_url 指向网关根(含 /v1,不要带 /responses 或 /models),wire_api 必须设为 responses(网关是 OpenAI Responses 兼容端点):
model_provider = "codex-api-gateway"
[model_providers.codex-api-gateway]
name = "Codex API Gateway"
base_url = "http://127.0.0.1:8383/v1"
wire_api = "responses" # provider 不声明 auth/env_key,网关 /v1/* 不校验入站 Authorizationmodel 填网关 models 段声明的别名(即 model_map 的键,例如 gpt-5),网关再映射成上游真实模型。
方式二:命令行 -c 覆盖(临时、单次会话)
不改配置文件,用 -c 传 TOML 键值(点路径表示嵌套),优先级高于 config.toml:
codex -c 'model_provider="codex-api-gateway"' \
-c 'model_providers.codex-api-gateway.name="Codex API Gateway"' \
-c 'model_providers.codex-api-gateway.base_url="http://127.0.0.1:8383/v1"' \
-c 'model_providers.codex-api-gateway.wire_api="responses"' \
-m gpt-5方式三:环境变量(仅覆盖单个 provider 字段,需配合 config.toml 的 provider 壳)
Codex 没有统一的 OPENAI_BASE_URL 环境变量,base_url 只能写在 [model_providers] 段。若整段配置都由脚本生成,可在脚本里把 base_url 写成变量渲染进 TOML,再 codex -c 覆盖。
验证:启动 Codex 后随便发条消息,网关管理页「观测台」应出现一条请求;若 Codex 报 404 / unauthorized,99% 是 base_url 多了 /responses 或 wire_api 没设 responses。
task build # 产出 ./codex-api-gateway
# 或
go build -o codex-api-gateway ./cmd/server想要发布包(双击即用),用
task build产出单文件二进制即可,无需任何运行前配置。 跨平台交叉编译示例:# macOS (Apple Silicon) GOOS=darwin GOARCH=arm64 go build -o codex-api-gateway-darwin-arm64 ./cmd/server # Windows GOOS=windows GOARCH=amd64 go build -o codex-api-gateway.exe ./cmd/server # Linux GOOS=linux GOARCH=amd64 go build -o codex-api-gateway-linux-amd64 ./cmd/server
管理页是唯一的配置入口。双击启动后,浏览器打开 http://localhost:<listen>/(默认 http://localhost:8383/,或点托盘「打开」菜单)即进入 H5 控制台。所有上游源、模型白名单、断路器参数、全局设置都能在网页里图形化增删改,保存即热重载,不用碰 YAML、不用命令行。
- 观测台:6 张 Token 指标卡(上游调用量 / 输入 / 输出 / 缓存创建 / 缓存命中 / 缓存 Token 命中率)+
供应商 × 模型用量聚合表 + 最近 1000 条请求历史(按模型/供应商/Code 过滤,耗时渐变色条)。指标按上游尝试聚合;总输入已按协议归一化,Anthropic 为input + cache_read + cache_creation,Chat/Responses 使用已含缓存 Token 的input;命中率为缓存读取 Token / 归一化总输入 Token。 - 实时推送:指标经 SSE(
/admin/api/events)每 3 秒推送,无需手动刷新。 - 配置管理:图形化编辑供应商(含
model_map列表式编辑、单源断路器、停用/启用、上移/下移排序)、模型白名单、全局参数、基线指令文件(config 同级 base_instructions.md)。 - 个性化:中英文切换、亮/暗主题,均记忆在 localStorage。
- 性能隔离:指标采集走独立 goroutine + 带缓冲 channel,请求路径只做一次非阻塞投递,channel 满即丢弃,绝不拖慢转发;管理端异常不影响
/v1/*。指标为进程生命周期内的内存累计值,重启后清零。
JSON 接口(前端调用,也可独立集成):
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /admin/api/metrics |
当前指标快照 |
| GET | /admin/api/config |
读取配置视图 |
| POST | /admin/api/config |
全量覆盖写回并热重载 |
| GET | /admin/api/guidance |
读取基线指令文件(config 同级 base_instructions.md)内容 |
| POST | /admin/api/guidance |
保存基线指令文件(config 同级 base_instructions.md) |
| POST | /admin/api/config/reload |
手动触发从磁盘 reload |
| POST | /admin/api/sources/promote |
手动将源提升回 normal |
| POST | /admin/api/sources/disabled |
即时停用/启用单源(写盘 + 热重载) |
| POST | /admin/api/sources/test |
用 base_url + api_key 探测上游连通性与 key 有效性 |
| POST | /admin/api/sources/reorder |
调整源顺序(写盘 + 热重载) |
| POST | /admin/api/sources |
追加新源(写盘 + 热重载) |
| POST | /admin/api/sources/delete |
按 name 删除源(写盘 + 热重载) |
| POST | /admin/api/upstream-models |
用连接参数试拉上游模型列表(未落盘配置) |
| GET | /admin/api/models |
按源名拉取上游模型列表 |
| POST | /admin/api/models/reorder |
调整模型白名单顺序(写盘 + 热重载) |
| POST | /admin/api/models/add |
追加模型白名单项(写盘 + 热重载) |
| POST | /admin/api/models/delete |
删除模型白名单项(写盘 + 热重载) |
| GET | /admin/api/version |
返回构建注入版本号 |
| GET | /admin/api/events |
SSE 推送 metrics 快照(每 3s 一次) |
完整示例见 config.example.yaml。
server:
listen: ":8383"
max_body_mb: 32 # /v1/responses 请求体上限(MiB),超限 413
read_header_timeout: 10s # 读完请求头超时;不影响 SSE 长流本机防误伤:限制超大 body,避免长历史/大图 base64 撑爆内存;read_header_timeout 防止半开连接挂住,不设置全局写超时以免掐断长 SSE。
logging:
level: info # debug | info | warn | error
format: text # text | json
# file: gateway.log
# max_size_mb: 50 # 单文件滚动阈值(仅 file 模式)
# max_backups: 3 # 保留历史文件个数| 等级 | 内容 |
|---|---|
debug |
输入项、会话回填、上游流事件、SSE 转换、上游错误详情 |
info |
启动、请求进入、转换完成、上游流锁定、请求完成 |
warn |
请求解析失败、上游失败、断路器跳过、重试 |
error |
服务端写出失败、请求最终失败、HTTP 服务退出 |
普通场景直接在管理页填写 API Key 即可,无需环境变量。仅当你希望密钥不落盘、或用脚本批量覆盖配置时,才用以下两种方式(后者优先级更高;加载顺序:先展开 ${ENV} 并加载 YAML,再应用 CODEX_API_GATEWAY_ 覆盖)。
# 方式一:YAML 内联展开
sources:
- name: anthropic-official
api_key: ${ANTHROPIC_KEY}# 方式二:CODEX_API_GATEWAY_ 前缀覆盖(层级用双下划线 __,字段单下划线保留)
export CODEX_API_GATEWAY_LOGGING__LEVEL=debug
export CODEX_API_GATEWAY_SOURCES__0__API_KEY=sk-ant-...
export CODEX_API_GATEWAY_BREAKER__MAX_RETRIES=2sources:
- name: anthropic-official
base_url: https://api.anthropic.com
api_key: ${ANTHROPIC_KEY}
model_map: { gpt-5: claude-sonnet-4-20250514 }
default_model: claude-sonnet-4-20250514sources列表顺序即优先级,第一个源优先尝试。base_url写上游根地址,不含/v1/messages。model_map把 Codex/OpenAI 侧别名映射到上游真实模型名。default_model用于请求模型未命中model_map时兜底。disabled: true人工停用该源(不参与调度);管理页可即时切换,也可直接改 YAML。
建议让 Codex 使用 gpt-5、gpt-5.5 这类别名,再经 model_map 映射到上游专有模型(如 glm-5.2)。直接把 Codex 模型名设成上游专有名,Codex 可能缺本地元数据。
breaker:
first_byte_timeout: 12s
request_timeout: 120s
degrade_threshold: 3
degrade_interval: 1m
degraded_recovery_threshold: 1
circuit_interval: 30m
circuit_recovery_threshold: 1
recovery: normal
max_retries: 0状态流转:normal → degraded → circuitOpen → halfOpen → normal/degraded
| 参数 | 含义 |
|---|---|
first_byte_timeout |
等待上游首个流式事件的最长时间,超时计为失败 |
request_timeout |
单个源单笔上游调用的总时长上限(默认 120s,0 或缺省走默认,负值拒绝)。与首字节超时不同:不被首个事件停止,到点即终止该笔调用并返回 failed 终态/504;未出内容时该源按失败计熔断并允许换源,已出内容时源锁定、流以 failed 收尾不换源 |
degrade_threshold |
连续失败达阈值后降级,再达阈值后熔断 |
degrade_interval |
降级源超过此间隔无新失败后恢复到原始优先级位置进入机会窗口(状态仍保持 degraded;机会内失败重新排到队尾,连续 N 次机会失败后熔断) |
degraded_recovery_threshold |
degraded 恢复到 normal 所需的连续成功次数(降级恢复阈值) |
circuit_interval |
熔断后进入半开探测前的等待时间 |
circuit_recovery_threshold |
halfOpen 恢复到 normal/degraded 所需的连续探测成功次数(熔断恢复阈值) |
recovery |
半开探测成功后恢复到 normal 或 degraded |
max_retries |
所有源全部失败后的整轮重试次数(0=不重试;仅全局,单源不覆盖) |
request_timeout 支持单源覆盖,零值继承全局:
单源可覆盖部分断路器参数,零值字段继承全局:
sources:
- name: zhipu
base_url: https://open.bigmodel.cn/api/anthropic
breaker:
first_byte_timeout: 8s
request_timeout: 90s
circuit_interval: 10s核心转发入口。请求体为 OpenAI Responses API 格式;按命中源的 backend_type 走 a/c/r 适配或透传,响应始终为 OpenAI Responses SSE 流。
返回 Codex ModelsResponse 格式({ "models": [ModelInfo] })而非 OpenAI 的 { data: [] },Codex 据此直接解析 ModelInfo 能力字段(如 supports_search_tool)。只返回 models 段显式声明的模型,不拉取上游 /v1/models,也不暴露 model_map 别名。
分层、单向依赖的 Go 服务,禁止反向引用:
L5 观测/管理 internal/admin internal/metrics
L4 编排 internal/server
L3 运行时 internal/scheduler internal/backend(a/c/r 适配器)
L2 转换 convert/streamconv(a) chatconvert/chatstreamconv(c) 透传无 L2(r)
L1 客户端 anthropic chatclient responsesclient
L0 基础 config logging model breaker toolcatalog
两条贯穿路径:
- 配置生效路径(单一真相源):磁盘
config.yaml(及同级base_instructions.md)→config.Load→holder.Replace→scheduler.Reload。管理页保存与外部编辑都走写盘 → fsnotify → 这条链路。 - 请求转发路径:
/v1/responses→server→scheduler.ExecuteGeneric→ 按源选backend(a/c/r)→ 上游 SSE → 回写 Responses SSE。任何失败以 error / SSE 错误事件返回,不 panic 逃逸。
普通用户无需此节——双击二进制即可运行。以下仅面向想从源码改代码的开发者。
优先使用 Taskfile:
task build # 构建二进制 ./codex-api-gateway(双击即用)
task run # 前台开发调试:用当前目录 config.yaml 跑起来
task up # 后台启动(-d,类似 docker compose up -d)
task stop # 按 gateway.pid 停止
task restart # stop 后后台启动,并等 /v1/models 健康
task test # 运行全部测试
task test-race # race detector
task cover # 覆盖率
task check # gofmt 检查 + go vet + go test未安装 Task 时直接用 Go:
go test ./...
go build -o codex-api-gateway ./cmd/server # 构建双击即用二进制
go run ./cmd/server # 开发调试:默认读 ./config.yaml
go run ./cmd/server -config config.yaml -d # 后台启动(写 gateway.pid / gateway.log)Codex CLI 会把 /v1/models 的返回缓存在 ~/.codex/models_cache.json。当网关 models 段或某个源的 model_map 发生变化(新增/改名模型别名)后,Codex 可能仍用旧缓存、拉不到新模型。此时删掉缓存文件,下次启动 Codex 会自动重新拉取:
rm -f ~/.codex/models_cache.json| 症状 | 可能原因 | 处理 |
|---|---|---|
| Codex 报 404 | base_url 写成 …/v1/responses |
改成 …/v1,Codex 自己拼 /responses 和 /models |
| Codex 报 unauthorized | requires_openai_auth 没设 true 或 wire_api 没设 responses |
见「配置 Codex CLI 指向网关」 |
| 转发返回 503 | 网关未配置任何上游源,或全部源被停用 | 管理页「配置管理」加源/启用源并保存 |
| Codex 拉不到新模型 | models_cache.json 旧缓存 |
删 ~/.codex/models_cache.json 重启 Codex |
本网关是 Codex CLI → 多上游 的协议适配 / 透传层,不是 OpenAI 全量 Responses 平台,也不是 session 运行时:
- 客户端自带完整
input回灌:网关无 session store,不按previous_response_id补历史。- 仅 a 源:非空
previous_response_id通常 WARN + 忽略(字段不进 Anthropic)。 - 配置含启用中的 r 源时:字段透传上游,日志说明「网关不代补会话」,不写「数据被丢弃」。
- 仅 a 源:非空
- a 路径:Responses ↔ Anthropic Messages 直转,不经 Chat 中枢。
- c / r:并行 Backend;c 做 Chat 形状转换,r 做 Responses 最小改写透传(model 映射、强制
stream、出站 model 别名回写)。
下列限制主要针对 a(Anthropic) 路径的协议映射天花板。c / r 见 协议覆盖矩阵 专节;r 以形状透传为主,能力由上游决定。
- 多模态:
input_image.file_id不支持(网关无 OpenAI 凭据拉取文件;仅接受 base64 / URL)。 - web_search:出站完整(事件链 +
url_citation,流式与终态 item annotations 都写);历史回灌为server_tool_use+ 空web_search_tool_result+ sources 可见文本——OpenAI wire 无 Anthropic required 的encrypted_content,无法做官方级 result round-trip。 - code interpreter:网关整体不支持(a 声明 fail-fast;c 声明 Debug 跳过;历史与回程静默忽略/skip),不再映射 Anthropic code_execution。
- structured output:
text.format.json_schema/json_objecta/c 统一忽略(output_config仅保留effort),不写output_config.format或response_format。 - redacted_thinking:网关只接受明文 thinking;出站
redacted_thinking块跳过,入站 redacted 密文(无 summary 文本的encrypted_content)忽略,明文thinking签名回灌保留。 - MCP:
- MCP 由 Codex 客户端本地执行:
allowed_tools展开为扁平mcp__<server>__<tool>function 声明,回程走function_call,不再注入 Anthropic beta MCP 配置 mcp_call历史 → 标准tool_use+tool_result(扁平名直接回填)mcp_list_tools历史 → DEBUG 丢弃(opencode 无此类型,Codex 不把 AdditionalTools 转消息,工具经请求 tools/ToolSpec 声明)mcp_approval_request/response不实现:Anthropic 无审批协议,历史 WARN + 丢弃- 连接字段(server_url/headers/require_approval/connector_id/tunnel_id)不注入或 fail-fast,交给客户端本地连接执行
- MCP 由 Codex 客户端本地执行:
- tool_choice
allowed_tools:仅auto/required映射;条目按 type/namespace/name 精确匹配已声明工具;含 hosted/MCP 条目时 fail-fast。 - 无等价历史 item(
file_search_call/computer_call*/image_generation_call/program*/item_reference/additional_tools):WARN + 丢弃,不进 system context。 - 无等价请求参数(a 路径):
background/conversation/context_management/max_tool_calls/prompt/ deprecateduser等按 WARN + 忽略;moderation/top_logprobs/prompt_cache_*/safety_identifier/service_tier仅 c 路径透传(a 路径按 INFO 提示忽略)。
完整状态见协议覆盖矩阵(含 Anthropic a、Chat Completions c 与 Responses 透传 r 专节)。