bug(llm-pi-ai): openai-responses 路由无法关闭 session_id 请求头 #2822
Replies: 2 comments
|
已在当前 master 逐条核实,两处关键定位依然准确(行号有小幅漂移,更新如下):
...options.sessionId === undefined ? {} : { sessionId: String(options.sessionId) },
const compatProfile: z<PiAiCompatProfile> = z.object({
thinkingFormat: z.union(SUPPORTED_THINKING_FORMATS),
supportsReasoningEffort: z.boolean(),
})
你提出的 |
|
这份报告的单变量对照做得很干净。两件:一条你说"无可用 workaround"的 workaround,和一份对你根因判断的独立佐证。 一、有 workaround:在中间放一个只删这一个 header 的透传代理你的结论是:
在 DSH 的配置面里确实无法移除,这一点你说得对。但你可以在 DSH 和网关之间放一个自己的本地转发器,让它在转发前把这个 header 摘掉——DSH 那边只需要把 我们仓库里有个现成的透传器可以拿来改( delete headers['session_id'](它默认就删掉了逐跳 header 并原样带上 这条路的性质要说清楚:它是绕过,不是修复,而且多一跳本地转发。但对"现在就想让这条路由能用"来说,它是可用的;而且它顺带给你一个额外好处——你可以直接看到 DSH 到底发了哪些 header,用来验证修复落地后 (如果你不想引第三方文件,任何一个能改写请求头的本地反代都行,nginx 的 二、你对
|
Uh oh!
There was an error while loading. Please reload this page.
openai-responses路由必发session_id,拒绝下划线 header 名的网关返回 400,导致部分第三方中转/路由不可用。复现步骤:
api: openai-responses的自定义路由,baseURL指向拒绝含下划线 header 名的第三方 OpenAI 兼容网关。实际结果:
请求在到达鉴权与模型之前返回
400 Bad Request。对真实第三方 Responses endpoint 做过单变量对照:两次请求的 URL、模型、鉴权、请求正文字段完全一致,仅改变是否携带
session_idheader。不带
session_id(仍带x-client-request-id、正文prompt_cache_key、stream: true):HTTP 200,text/event-stream,收到response.created/response.output_text.delta/response.completed,获得 Response ID,模型返回预期文本。带
session_id:HTTP 400,text/plain,正文Bad Request,无 Response ID,无 SSE 事件。失败由该 header 触发,与 API key、endpoint、模型 id、请求正文、流式协议无关。endpoint、凭据、provider 名和模型 id 不在此披露。
成因:
packages/llm/llm-pi-ai/src/adapter.ts:317将 Agent 请求的sessionId透传给 pi-ai。pi-ai 0.82.1dist/api/openai-responses.js:166-175按compat.sessionAffinityFormat分派 header;detectSessionAffinityFormat(同文件:34-36)仅在 provider 为openrouter或 baseURL 含openrouter.ai时返回openrouter,其余一律返回openai,即发送session_id。自定义网关必然落入该分支。pi-ai 已定义
SessionAffinityFormat = "openai" | "openai-nosession" | "openrouter"(dist/types.d.ts:41),并在OpenAICompletionsCompat(:463)与OpenAIResponsesCompat(:472)上都接受sessionAffinityFormat。Harness 侧的PiAiCompatProfile(src/catalog.ts)与配置 schemacompatProfile(src/config.ts:188)只暴露thinkingFormat和supportsReasoningEffort,该字段无从配置。两个协议在此处不对称:Completions 侧的 affinity header 受
sendSessionAffinityHeaders开关控制且检测默认为false(dist/api/openai-completions.js:489、:1174);Responses 侧没有这个开关,只要有sessionId就发。无可用 workaround:profile 的
headers只能新增或覆盖 header 的值,无法移除 header 名。把session_id置空仍会发出含下划线的 header 名,网关同样拒绝;pi-ai 将 options headers 最后合并(openai-responses.js:178-180),覆盖顺序也不改变这一点。预期结果:
compat可在 provider 级和模型级配置sessionAffinityFormat。openai-nosession省略session_id,保留x-client-request-id。正文prompt_cache_key由cacheRetention决定(openai-responses.js:204),不受此字段影响。未配置时维持 pi-ai 的自动检测。解析优先级:模型级
compat→ provider 级compat→ 已安装 catalog 条目 → pi-ai 自动检测。环境:
master(47f943859b)@deepseek-ai/dsh-llm-pi-ai0.1.0-rc.5@earendil-works/pi-ai0.82.1参考
SessionAffinityFormat/OpenAIResponsesCompatsession_idheader, rejected by strict HTTP gateways (Envoy, nginx) earendil-works/pi#3579All reactions