问题概述
当前 Sub2API 会识别 Codex remote_compaction_v2 请求体中的:
{"type":"compaction_trigger"}
但识别后会无条件把请求从:
改写为:
POST /v1/responses/compact
这对于只支持旧版 /responses/compact 的上游是一种兼容方案,但对于已经原生支持新版 V2 协议的 OpenAI-compatible API Key 上游,会导致请求错误地进入旧版 compact 路由。
本次实际表现为:客户端发送的模型是 gpt-5.6-sol,经过 Sub2API 后,上游最终尝试调度 gpt-5.6-sol-openai-compact,并返回 model_not_found。
环境
- Sub2API:
0.1.150
- Codex CLI:
0.144.0-alpha.4
- Codex Feature:
remote_compaction_v2 = true
- 账号类型:OpenAI API Key
- 上游类型:自定义 OpenAI-compatible Responses API
- 模型:
gpt-5.6-sol
- 上游原生支持:
POST /v1/responses + compaction_trigger
API Key 已脱敏,不在 Issue 中提供。
新版 V2 请求
Codex 当前发送的是标准 V2 请求:
POST /v1/responses
Content-Type: application/json
Accept: text/event-stream
{
"model": "gpt-5.6-sol",
"instructions": "压缩对话上下文。",
"input": [
{
"role": "user",
"content": [
{
"type": "input_text",
"text": "用于验证新版远程压缩协议的上下文。"
}
]
},
{
"type": "compaction_trigger"
}
],
"stream": true
}
新版协议的压缩身份由 compaction_trigger 表示,模型字段仍应保持 gpt-5.6-sol,不需要使用 gpt-5.6-sol-openai-compact。
实际结果
普通请求
通过 Sub2API 请求:
{
"model": "gpt-5.6-sol",
"input": "只回复 OK",
"stream": false
}
结果:
HTTP 200
model = gpt-5.6-sol
status = completed
说明普通模型、账号和上游本身可用。
V2 压缩请求,stream=false
在 /v1/responses 请求体中加入:
{"type":"compaction_trigger"}
结果:
{
"error": {
"code": "model_not_found",
"message": "No available channel for model gpt-5.6-sol-openai-compact under group ...",
"type": "new_api_error"
}
}
相关请求 ID:
Sub2API Request ID:
200ffcd8-df86-485f-b9fb-d7e8494dbdde
Upstream Request ID:
202607100845101554353358268d9d6vIGMFIZL
V2 压缩请求,stream=true
使用 Codex 实际采用的 stream:true 请求时,连接被直接断开:
curl: (52) Empty reply from server
HTTP 000
这可能还会与当前的 compact SSE bridge/keepalive 错误处理相关,但不是本 Issue 的主要根因。
绕过 Sub2API 直连上游
相同的:
POST /v1/responses
model = gpt-5.6-sol
input 包含 compaction_trigger
stream = true
直连上游可以返回:
HTTP 200
response.output_item.done
item.type = compaction_summary
response.completed
因此上游已经支持原生 V2。失败是 Sub2API 将 V2 请求转换成旧 /responses/compact 后产生的。
当前源码行为
当前 normalizeOpenAIResponsesCompactRequest() 会检测普通 /responses 请求中的 compaction_trigger,随后直接改写 URL:
if !isCompactRequest &&
isBareOpenAIResponsesPath(c) &&
service.HasCompactionTriggerInInput(body) {
c.Request.URL.Path =
strings.TrimRight(c.Request.URL.Path, "/") + "/compact"
}
源码:
https://github.com/Wei-Shaw/sub2api/blob/main/backend/internal/handler/openai_gateway_handler.go#L583-L618
现有测试也明确要求:
/v1/responses
↓
/v1/responses/compact
并删除 stream、store、prompt_cache_key:
https://github.com/Wei-Shaw/sub2api/blob/main/backend/internal/handler/openai_gateway_compact_body_signal_test.go
之后 Sub2API 再把旧版 unary compact JSON 合成为新版 SSE:
https://github.com/Wei-Shaw/sub2api/blob/main/backend/internal/service/openai_compact_stream_bridge.go
因此当前实现并不是原生透传 remote_compaction_v2,而是:
V2 body signal
↓
强制转换为旧 /responses/compact
↓
调用旧 compact 上游
↓
再把 unary JSON 合成为 V2 SSE
与已有 Issue 的区别
#3777
#3777 解决的是:
- V2
compaction_trigger 未被识别
- compact SSE 输出项丢失
- Codex 报
expected exactly one compaction output item, got 0
其修复方案是把 body-signal V2 请求“提升”为旧 /responses/compact 请求。
本 Issue 指出该方案存在新的兼容边界:
并非所有上游都需要降级到 /responses/compact。已经支持原生 V2 的上游应保持 /responses + compaction_trigger,否则会错误进入 *-openai-compact 路由。
#3934
#3934 报告了 GPT-5.6 压缩失败现象,但没有说明 V2 被强制转换成旧 compact 端点的完整链路。
#3961
#3961 是 compact 失败后的 keepalive writer panic。本次 stream:true 出现 Empty reply from server 可能与其相关,但本 Issue 主要关注请求被错误降级的问题。
期望行为
Sub2API 应区分两类压缩能力。
原生 V2
POST /v1/responses
input 包含 compaction_trigger
应当:
- 保持上游路径
/v1/responses
- 保持
stream:true
- 保留
compaction_trigger
- 模型保持
gpt-5.6-sol
- 使用普通
model_mapping
- 不应用
compact_model_mapping
- 不进入旧版
requireCompact 调度
- 原样透传
compaction_summary/compaction SSE 输出项
旧版 compact
POST /v1/responses/compact
或者明确配置为只支持旧协议的账号,可以继续使用现有兼容链路:
/responses/compact
- unary JSON
- compact model mapping
- SSE bridge
建议方案
增加账号级压缩传输能力配置,例如:
compaction_transport = auto | native_v2 | legacy_compact
建议语义:
native_v2
/responses + compaction_trigger 原样透传
- 不改路径和模型
legacy_compact
- 使用当前
/responses/compact 兼容桥
auto
- API Key、自定义
base_url 优先探测原生 V2
- OAuth/ChatGPT internal endpoint 可继续使用旧 compact 兼容路径
由于当前 path 改写发生在账号选择之前,可能需要:
- 先识别并标记
compaction_trigger
- 账号选择时区分
native_v2 与 legacy_compact
- 选定账号后再决定是否改写为
/responses/compact
不建议仅通过:
gpt-5.6-sol-openai-compact -> gpt-5.6-sol
解决,因为这仍然走旧版端点,没有真正使用原生 V2,而且会继续依赖 SSE bridge。
验收标准
- 原生 V2 账号收到的路径仍是
/v1/responses
- 请求体保留
compaction_trigger 和 stream:true
- 上游模型仍为
gpt-5.6-sol
- 不出现
gpt-5.6-sol-openai-compact
- Codex 能收到一个
compaction_summary/compaction 输出项
- 最终收到
response.completed
- 旧
/responses/compact 客户端和 OAuth 兼容链路不回归
- 普通
/responses 请求不受影响
- V2 上游失败时返回有效错误或
response.failed,不直接断开连接
问题概述
当前 Sub2API 会识别 Codex
remote_compaction_v2请求体中的:{"type":"compaction_trigger"}但识别后会无条件把请求从:
改写为:
这对于只支持旧版
/responses/compact的上游是一种兼容方案,但对于已经原生支持新版 V2 协议的 OpenAI-compatible API Key 上游,会导致请求错误地进入旧版 compact 路由。本次实际表现为:客户端发送的模型是
gpt-5.6-sol,经过 Sub2API 后,上游最终尝试调度gpt-5.6-sol-openai-compact,并返回model_not_found。环境
0.1.1500.144.0-alpha.4remote_compaction_v2 = truegpt-5.6-solPOST /v1/responses + compaction_triggerAPI Key 已脱敏,不在 Issue 中提供。
新版 V2 请求
Codex 当前发送的是标准 V2 请求:
{ "model": "gpt-5.6-sol", "instructions": "压缩对话上下文。", "input": [ { "role": "user", "content": [ { "type": "input_text", "text": "用于验证新版远程压缩协议的上下文。" } ] }, { "type": "compaction_trigger" } ], "stream": true }新版协议的压缩身份由
compaction_trigger表示,模型字段仍应保持gpt-5.6-sol,不需要使用gpt-5.6-sol-openai-compact。实际结果
普通请求
通过 Sub2API 请求:
{ "model": "gpt-5.6-sol", "input": "只回复 OK", "stream": false }结果:
说明普通模型、账号和上游本身可用。
V2 压缩请求,stream=false
在
/v1/responses请求体中加入:{"type":"compaction_trigger"}结果:
{ "error": { "code": "model_not_found", "message": "No available channel for model gpt-5.6-sol-openai-compact under group ...", "type": "new_api_error" } }相关请求 ID:
V2 压缩请求,stream=true
使用 Codex 实际采用的
stream:true请求时,连接被直接断开:这可能还会与当前的 compact SSE bridge/keepalive 错误处理相关,但不是本 Issue 的主要根因。
绕过 Sub2API 直连上游
相同的:
直连上游可以返回:
因此上游已经支持原生 V2。失败是 Sub2API 将 V2 请求转换成旧
/responses/compact后产生的。当前源码行为
当前
normalizeOpenAIResponsesCompactRequest()会检测普通/responses请求中的compaction_trigger,随后直接改写 URL:源码:
https://github.com/Wei-Shaw/sub2api/blob/main/backend/internal/handler/openai_gateway_handler.go#L583-L618
现有测试也明确要求:
并删除
stream、store、prompt_cache_key:https://github.com/Wei-Shaw/sub2api/blob/main/backend/internal/handler/openai_gateway_compact_body_signal_test.go
之后 Sub2API 再把旧版 unary compact JSON 合成为新版 SSE:
https://github.com/Wei-Shaw/sub2api/blob/main/backend/internal/service/openai_compact_stream_bridge.go
因此当前实现并不是原生透传
remote_compaction_v2,而是:与已有 Issue 的区别
#3777
#3777
#3777解决的是:compaction_trigger未被识别expected exactly one compaction output item, got 0其修复方案是把 body-signal V2 请求“提升”为旧
/responses/compact请求。本 Issue 指出该方案存在新的兼容边界:
#3934
#3934
#3934报告了 GPT-5.6 压缩失败现象,但没有说明 V2 被强制转换成旧 compact 端点的完整链路。#3961
#3961
#3961是 compact 失败后的 keepalive writer panic。本次stream:true出现Empty reply from server可能与其相关,但本 Issue 主要关注请求被错误降级的问题。期望行为
Sub2API 应区分两类压缩能力。
原生 V2
应当:
/v1/responsesstream:truecompaction_triggergpt-5.6-solmodel_mappingcompact_model_mappingrequireCompact调度compaction_summary/compactionSSE 输出项旧版 compact
或者明确配置为只支持旧协议的账号,可以继续使用现有兼容链路:
/responses/compact建议方案
增加账号级压缩传输能力配置,例如:
建议语义:
native_v2/responses + compaction_trigger原样透传legacy_compact/responses/compact兼容桥autobase_url优先探测原生 V2由于当前 path 改写发生在账号选择之前,可能需要:
compaction_triggernative_v2与legacy_compact/responses/compact不建议仅通过:
解决,因为这仍然走旧版端点,没有真正使用原生 V2,而且会继续依赖 SSE bridge。
验收标准
/v1/responsescompaction_trigger和stream:truegpt-5.6-solgpt-5.6-sol-openai-compactcompaction_summary/compaction输出项response.completed/responses/compact客户端和 OAuth 兼容链路不回归/responses请求不受影响response.failed,不直接断开连接