这是一个给 Codex App 使用的本地 Responses API 转发代理。
它放在 Codex App 和上游 API provider / 中转站之间:
Codex App -> http://127.0.0.1:8787 -> upstream provider
当前代理主要做四件事:
- 转发 Codex 的 Responses API 请求到真实上游。
- 从
POST /responses请求体中移除 image generation 相关工具声明,绕过部分中转站的生图权限预检。 - 默认给
reasoning注入summary: "auto",让上游返回 reasoning summary。 - 默认在代理控制台渲染非空 reasoning summary,补上 Codex 插件当前不显示 summary content 的问题。
某些配置下,Codex App 虽然没有显式要求生成图片,但 /responses 请求体里可能仍然带有 image generation 相关工具或能力声明。部分中转站会在模型真正开始回复前先做 group 权限校验,如果当前 key 或 group 没有开启生图权限,就会直接拒绝整个请求:
unexpected status 403 Forbidden: Image generation is not enabled for this group
这个代理会在转发前移除这些 image generation 声明,同时尽量保持其它请求数据不变。
另外,Codex 当前可能只发送:
{
"reasoning": {
"effort": "high"
},
"include": [
"reasoning.encrypted_content"
]
}但不会发送:
{
"reasoning": {
"summary": "auto"
}
}因此上游即使支持 reasoning summary,也可能不会返回 summary content。代理现在默认补上 reasoning.summary = "auto"。
npm run check默认监听:
http://127.0.0.1:8787
默认上游:
http://127.0.0.1:15721
启动:
npm start指定真实上游:
$env:UPSTREAM_BASE_URL = "https://api.example.com/v1"
npm start指定监听端口:
$env:LISTEN_PORT = "8787"
npm startPORT 也可以作为监听端口使用;如果同时设置 PORT 和 LISTEN_PORT,代码会优先读取 PORT。
不要让 Codex 直接请求中转站,而是把 provider 的 base_url 改为本地代理:
model_provider = "customapi"
[model_providers.customapi]
name = "customapi"
base_url = "http://127.0.0.1:8787"
experimental_bearer_token = "sk-your-relay-key"
requires_openai_auth = true
wire_api = "responses"
[features]
collaboration_modes = true
remote_connections = true
remote_control = true
goals = true
image_generation = false然后用 UPSTREAM_BASE_URL 指定代理真正转发到哪里:
$env:UPSTREAM_BASE_URL = "https://api.example.com/v1"
npm start其它 token、auth、feature 配置保持不变。修改 config.toml 后通常需要重启 Codex App。
代理会递归移除 JSON 请求体中满足以下条件的对象:
type = "image_generation"
type = "image_generation_call"
type = "image_generation_preview"
也会移除 name 中包含 image_generation 的对象。
默认会对 POST /responses 注入:
{
"reasoning": {
"summary": "auto"
}
}如果原请求已经有 reasoning.summary,代理不会覆盖。
关闭自动注入:
$env:FORCE_REASONING_SUMMARY = "0"
npm start指定其它 summary 模式:
$env:FORCE_REASONING_SUMMARY = "auto"
npm start当前推荐值是 auto。
代理默认会旁路解析 /responses 的上游响应。如果发现非空 reasoning summary,会打印到启动代理的控制台。
支持流式 SSE 事件:
response.reasoning_summary_text.delta
response.reasoning_summary_text.done
也支持非流式 JSON 中的:
{
"type": "reasoning",
"summary": [
{
"type": "summary_text",
"text": "..."
}
]
}输出示例:
[2026-06-20T16:01:04.759Z] reasoning summary:
**Title**
Hello world.
同一响应中如果既有流式 delta,又在最终 completed 事件中带完整 summary_text,代理会去重,避免重复打印。
关闭 summary 渲染:
$env:RENDER_REASONING_SUMMARY = "0"
npm start默认不抓包。
开启抓包:
$env:CAPTURE_REQUESTS = "1"
npm start默认抓包目录:
./captures
指定抓包目录:
$env:CAPTURE_REQUESTS = "1"
$env:CAPTURE_DIR = ".\captures-debug"
npm start每个抓包 JSON 会包含:
original_body
forwarded_body
body_changed_by_proxy
original_body_bytes
forwarded_body_bytes
headers
upstream_url
其中:
original_body = Codex 发给代理的原始请求体
forwarded_body = 代理改写后实际发给上游的请求体
可以用它确认 reasoning.summary 是否已经注入:
original_body.value.reasoning
forwarded_body.value.reasoning
默认会打码敏感请求头,例如:
authorization
cookie
openai-api-key
api-key
x-api-key
x-stainless-api-key
如果确实需要完整请求头,可以关闭请求头打码:
$env:CAPTURE_REDACT_HEADERS = "0"
npm start注意:抓包 body 会包含完整 prompt、上下文、工具定义和可能的敏感信息。captures/ 已加入 .gitignore,但仍应避免把抓包文件发给不可信的人或提交到仓库。
默认不会为每次 reasoning.summary 注入打印日志,避免控制台被刷屏。
如果需要调试请求改写细节:
$env:LOG_REQUEST_REWRITES = "1"
npm start开启后,代理会输出类似:
injected reasoning.summary="auto"
stripped 1 image-generation item(s)
| 变量 | 默认值 | 说明 |
|---|---|---|
LISTEN_HOST |
127.0.0.1 |
本地代理监听地址 |
LISTEN_PORT |
8787 |
本地代理监听端口 |
PORT |
空 | 监听端口;优先级高于 LISTEN_PORT |
UPSTREAM_BASE_URL |
http://127.0.0.1:15721 |
真实上游 API base URL |
FORCE_REASONING_SUMMARY |
auto |
给 /responses 注入 reasoning.summary;设为 0 可关闭 |
RENDER_REASONING_SUMMARY |
开启 | 渲染非空 reasoning summary 到代理控制台;设为 0 可关闭 |
CAPTURE_REQUESTS |
关闭 | 抓取请求参数;设为 1 / true / yes / on 开启 |
CAPTURE_DIR |
./captures |
抓包输出目录 |
CAPTURE_REDACT_HEADERS |
开启 | 抓包时打码敏感请求头;设为 0 可关闭 |
LOG_REQUEST_REWRITES |
关闭 | 输出请求改写细节;设为 1 / true / yes / on 开启 |
普通使用:
$env:UPSTREAM_BASE_URL = "https://api.example.com/v1"
npm start开启抓包并渲染 summary:
$env:UPSTREAM_BASE_URL = "https://api.example.com/v1"
$env:CAPTURE_REQUESTS = "1"
npm start只做转发和 image-generation 清理,不注入 summary、不渲染 summary:
$env:FORCE_REASONING_SUMMARY = "0"
$env:RENDER_REASONING_SUMMARY = "0"
npm start调试所有请求改写:
$env:CAPTURE_REQUESTS = "1"
$env:LOG_REQUEST_REWRITES = "1"
npm start这个代理只改写请求体和旁路观察响应,不会主动修改上游返回给 Codex 的响应内容。
如果已经注入 reasoning.summary = "auto",但上游仍不返回 summary,则问题更可能在上游 provider、relay 或模型兼容层,而不是本地代理。