Codex 第三方 API 兼容检测:别只看 HTTP 200,3 步验证 Responses API
很多人给 Codex 配第三方 API 时,只在浏览器里打开一下地址:页面能访问、HTTP 返回 200,就认为接口没问题。
结果真正启动 Codex 后,还是会遇到:
404 Not Found
stream disconnected before completion
response.failed event received
Missing environment variable
问题在于:“网站能访问”不等于“Codex 能调用”。
根据当前官方配置文档,Codex 自定义模型提供商使用的是 Responses API,wire_api 目前唯一支持的值也是 responses。因此,一个只兼容 /v1/chat/completions 的平台,即使普通 OpenAI SDK 可以调用,也不代表它能直接供 Codex 使用。
这篇文章不靠“看起来能通”判断,而是用三步依次检查:
/v1/responses路由和鉴权是否存在;- 非流式 Responses 返回结构是否正确;
- SSE 流式事件能否完整结束。
说明:本文包含作者使用和推广的 API 服务示例。路径检查使用 Genvis 的 OpenAI 兼容端点完成;其他服务可以替换 Base URL 和模型名后执行同样的检测。
[TOC]
一、一句话结论
Codex 第三方 API 至少要满足以下条件:
- 支持
POST /v1/responses; - 使用 Bearer Token 等客户端可配置的鉴权方式;
- 返回 Responses API 数据结构,而不是 Chat Completions 结构;
stream: true时返回合法的text/event-stream;- 流中能看到 Responses 事件,并最终正常出现完成事件;
- 配置中的模型名确实存在且当前 Key 有权调用。
仅仅出现 HTTP 200,最多只能证明“某个网页返回了内容”。
二、为什么普通 OpenAI 兼容接口不一定能给 Codex 用
大家最熟悉的旧式对话接口通常是:
POST /v1/chat/completions
Responses API 使用的是:
POST /v1/responses
两者不只是 URL 不同,请求和响应结构也不同。
Chat Completions 常见请求:
{
"model": "YOUR_MODEL_ID",
"messages": [
{ "role": "user", "content": "Reply OK" }
]
}
Responses API 的最小请求则是:
{
"model": "YOUR_MODEL_ID",
"input": "Reply OK"
}
OpenAI 官方 API 参考将创建响应定义为 POST /responses。当前 Codex 配置参考也明确写明,自定义提供商的 wire_api 只有 responses 这一种支持值,而且省略时默认就是它。
所以,下面几种情况都可能发生:
/v1/models可以获取模型列表,但/v1/responses是 404;/v1/chat/completions能对话,但 Codex 无法启动任务;- 非流式请求正常,切换
stream: true后立即断开; - 服务返回 HTTP 200,但响应体其实是网站首页 HTML;
- 能输出文字,却缺少 Codex 需要处理的 Responses 事件。
三、第一步:检查路由和鉴权
先不要急着修改 config.toml,直接从命令行检查端点。
macOS、Linux 或 Git Bash:
export API_BASE="https://genvis.xyz/v1"
export API_KEY="YOUR_API_KEY"
export MODEL="YOUR_MODEL_ID"
PowerShell:
$env:API_BASE="https://genvis.xyz/v1"
$env:API_KEY="YOUR_API_KEY"
$env:MODEL="YOUR_MODEL_ID"
先发送一个真正的 POST 请求:
curl -i --max-time 30 \
-X POST "$API_BASE/responses" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
--data "{\"model\":\"$MODEL\",\"input\":\"Reply OK\"}"
不要用浏览器地址栏或下面这种请求代替:
curl "$API_BASE/responses"
因为它发送的是 GET,而 Responses 创建接口要求 POST。GET /v1/responses 返回 404 或 405,并不能证明 POST /v1/responses 不存在。
我在无有效 Token 的情况下进行路径检查时,得到的是:
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{"error":{"message":"Invalid token", ...}}
这个结果说明请求已经进入 API 鉴权层,路由不是被前端网页接走;但它仍然不能证明模型可调用,因为还没有通过鉴权和模型检查。
不同返回值可以这样判断:
| 返回结果 | 通常说明什么 | 下一步 |
|---|---|---|
401 + JSON 错误 |
路由大概率存在,但 Key 缺失或错误 | 检查环境变量、Token 和请求头 |
403 + JSON 错误 |
Key 已识别,但权限、分组或访问策略拒绝 | 检查模型权限、IP 白名单和账号状态 |
404 + JSON 错误 |
路径错误,或服务没有实现 Responses API | 检查 Base URL 和接口能力 |
404/200 + HTML |
请求打到了官网、反向代理或前端回退页 | 检查 API 域名、路径和网关配置 |
400 unknown model |
路由和鉴权可能已通过,但模型名不可用 | 查询模型列表或控制台权限 |
405 Method Not Allowed |
请求方法错误 | 确认使用 POST |
四、第二步:检查非流式响应结构
通过鉴权后,正常的 Responses API 返回不应该是 Chat Completions 的 choices 数组,而应当具有 Responses 对象特征,例如:
{
"id": "resp_...",
"object": "response",
"status": "completed",
"model": "YOUR_MODEL_ID",
"output": [],
"usage": {
"input_tokens": 0,
"output_tokens": 0,
"total_tokens": 0
}
}
这里不要求字段顺序完全一致,也不要求所有可选字段都出现,但至少需要确认:
- 响应是 JSON,不是 HTML;
object是response;- 有明确的
status; - 文本位于 Responses 的
output结构中; - 错误时返回结构化错误,而不是网关生成的一段普通网页;
- Token 用量能正确记录,避免调用成功却无法计费或对账。
如果返回长这样:
{
"choices": [
{
"message": {
"role": "assistant",
"content": "OK"
}
}
]
}
这更像 Chat Completions 返回。普通 SDK 可能可以读取,但不能据此认为 Codex 的 Responses 调用链已经兼容。
五、第三步:检查 SSE 流式事件
Codex 是长时间运行的 Agent 客户端。只验证一次非流式回答还不够,还要验证流式连接。
执行:
curl -N -i --max-time 120 \
-X POST "$API_BASE/responses" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
--data "{\"model\":\"$MODEL\",\"input\":\"Reply with exactly OK\",\"stream\":true}"
首先检查响应头:
HTTP/1.1 200 OK
Content-Type: text/event-stream
然后检查事件流。事件内容会因模型和实现不同而变化,但应当符合 Responses API 的 SSE 结构,并能看到类似阶段:
event: response.created
data: {...}
event: response.output_text.delta
data: {...}
event: response.completed
data: {...}
OpenAI 官方文档说明,Responses 流式传输使用 Server-Sent Events,并通过 stream: true 开启。
下面这些情况都值得警惕:
- 状态码 200,但
Content-Type是text/html; - 返回一整块 JSON 后立即关闭,并不是真正 SSE;
- 只有文本片段,没有事件类型;
- 流到一半直接 EOF,没有完成事件;
- 完成事件中的状态仍是
failed或incomplete; - 服务长时间不发送数据,也没有心跳或合理超时;
- 网关把上游错误包装成 200 文本。
这类问题常常在 Codex 中表现为:
stream disconnected before completion
response.failed event received
因此,“能返回第一段文字”也不代表整个流式协议合格。
六、确认通过后,再配置 Codex
当前官方配置参考显示,用户级配置文件位于:
~/.codex/config.toml
Windows 通常对应:
%USERPROFILE%\.codex\config.toml
有一个很容易忽略的变化:model_provider 和 model_providers 属于机器本地提供商配置。官方文档说明,把它们写到项目目录的 .codex/config.toml 中会被忽略,因此第三方提供商应放在用户级配置里。
配置示例:
model = "YOUR_MODEL_ID"
model_provider = "genvis"
[model_providers.genvis]
name = "Genvis"
base_url = "https://genvis.xyz/v1"
env_key = "GENVIS_API_KEY"
wire_api = "responses"
wire_api = "responses" 当前可以省略,因为它就是默认值。但排错时建议显式写出来,避免以后看到配置时无法确认协议意图。
不要把 Key 直接写进 TOML:
# not recommended
experimental_bearer_token = "sk-xxxxxxxx"
官方配置参考也明确建议使用 env_key,而不是直接保存 Bearer Token。
macOS/Linux 设置环境变量:
export GENVIS_API_KEY="YOUR_API_KEY"
PowerShell:
$env:GENVIS_API_KEY="YOUR_API_KEY"
配置后要从同一个新终端启动 Codex。VS Code 已经打开时,插件进程可能仍然保留旧环境变量,需要彻底退出后重新打开。
七、Base URL 最常见的三个错误
1. 重复拼接 /v1
如果客户端会在 Base URL 后追加 /responses,那么:
Base URL: https://api.example.com/v1
final URL: https://api.example.com/v1/responses
但如果配置和网关同时追加版本路径,就可能变成:
https://api.example.com/v1/v1/responses
这种情况通常返回 404。
2. 把完整接口写成 Base URL
错误示例:
base_url = "https://api.example.com/v1/responses"
客户端继续追加 /responses 后,可能得到:
https://api.example.com/v1/responses/responses
Base URL 应填公共前缀,而不是某个具体操作接口,除非服务商文档明确要求特殊格式。
3. API 域名和官网域名混用
有些网站会把未知路径统一返回首页,因此错误请求也可能得到 HTTP 200。判断时必须同时看:
- 最终 URL;
- HTTP 状态码;
Content-Type;- 响应体;
- 是否为 SSE;
- 是否出现完整结束事件。
八、stream disconnected 不一定是换模型能解决
遇到流中断时,很多人的第一反应是切换模型。但可能的故障层至少有五层:
- 本机、代理、证书或网络连接;
- Codex 客户端版本和配置;
- API 网关的超时、缓冲或 SSE 转发;
- 上游模型的响应时间和流式实现;
- Responses 事件格式不完整。
当前官方 Codex 配置参考中,自定义提供商默认的 SSE 空闲超时是 300000 毫秒,流中断重试次数默认是 5。可以通过:
[model_providers.genvis]
stream_idle_timeout_ms = 300000
stream_max_retries = 5
进行明确配置,但不要把“无限加大超时”当成通用修复。
如果接口每次都在同一个固定时长中断,应检查代理或网关超时;如果第一条事件就解析失败,应优先检查协议;如果只有某个模型失败,应检查模型映射、上下文和上游能力。
九、一张表完成最终判断
| 检查项 | 合格表现 | 不合格表现 |
|---|---|---|
| 请求方法 | POST |
用浏览器或 GET 测试 |
| 路径 | /v1/responses |
只有 /v1/chat/completions |
| 鉴权 | 结构化 401/403 或正常通过 | HTML 登录页、重定向页面 |
| 非流式响应 | object: response |
只有 choices 或普通文本 |
| 流式响应头 | text/event-stream |
text/html、下载文件、普通 JSON |
| 流式过程 | Responses SSE 事件 | 无事件类型、半途 EOF |
| 结束状态 | response.completed |
failed、incomplete 或无结束事件 |
| 模型权限 | 当前 Key 可调用 | 余额有但分组无权限 |
| Codex 配置位置 | 用户级 ~/.codex/config.toml |
把 provider 写进项目级配置 |
| Key 保存 | 使用 env_key |
明文写进配置或 Git |
十、总结
判断一个第三方 API 能否供 Codex 使用,不应该问“这个地址能不能打开”,而应该依次问:
POST /v1/responses是否存在?- 鉴权和模型权限是否通过?
- 返回的是 Responses 对象还是 Chat Completions 对象?
stream: true是否得到真正的 SSE?- 事件流是否完整到达
response.completed? - Codex 的 provider 是否写在用户级配置,并从正确环境变量读取 Key?
完成这六项检查,才能把“配置问题”“接口不兼容”“模型无权限”和“流式链路中断”分开。否则不停换模型、改 TOML、重装客户端,很可能只是在重复试错。
如果你正在排查,可以只留下以下信息:操作系统、Codex 版本、最终请求路径、HTTP 状态码、Content-Type 和错误原文。不要发布 API Key、完整请求头或包含业务代码的原始日志。
参考资料
- OpenAI 官方 Codex Configuration Reference
- OpenAI 官方 Responses API:Create a model response
- OpenAI 官方 Streaming API responses
更新记录
- 2026-08-23:根据当日官方配置参考核对
model_providers、env_key、wire_api、SSE 超时和重试配置;完成无有效 Token 的路由与鉴权层检查。
葡萄城是专业的软件开发技术和低代码平台提供商,聚焦软件开发技术,以“赋能开发者”为使命,致力于通过表格控件、低代码和BI等各类软件开发工具和服务,一站式满足开发者需求,帮助企业提升开发效率并创新开发模式。
更多推荐
所有评论(0)