ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

big-AGI 迁移实录:OpenRouter 从 Chat Completions 切换到 Responses API 的协议等价性验证指南

2026/9/17 21:43:00 拓冰建站 浏览量
big-AGI 迁移实录:OpenRouter 从 Chat Completions 切换到 Responses API 的协议等价性验证指南 big-AGI 迁移实录OpenRouter 从 Chat Completions 切换到 Responses API 的协议等价性验证指南【免费下载链接】big-AGIAI suite powered by state-of-the-art models and providing advanced AI/AGI functions. Includes AI personas, AGI functions, world-class Beam multi-model chats, text-to-image, voice, response streaming, code highlighting and execution, PDF import, presets for developers, much more. Deploy on-prem or in the cloud.项目地址: https://gitcode.com/GitHub_Trending/bi/big-AGI本指南基于 big-AGI 仓库内的方向性技术记录 LLM-openrouter-responses.md完整还原了将 OpenRouter 接入方从 Chat Completions 协议切换到POST /api/v1/responsesResponses 方言的单边切换决策、协议实测细节、等价性矩阵Parity Matrix与代码移植清单Port List。读者将掌握OpenRouter 对 OpenAI Responses 规范的具体实现差异厂商前缀工具、reasoning 条目、缓存断点、无状态限制哪些原 Chat Completions 定制可以原样保留、哪些需要重新映射、哪些必须改代码、哪些直接丢失以及 big-AGI 中 dispatch / adapter / parser / wiretypes 四层代码各自需要落地的改动点。背景一次单向的协议切换OpenRouter 是 big-AGI 的 AIXAI eXchange服务层所支持的 OpenAI 兼容厂商之一OpenAIDialects成员。此前 OpenRouter 走的是 Chat Completions 路径对应源码 openai.chatCompletions.ts、openai.parser.ts 与 chatGenerate.dispatch.ts而迁移的目标是让 OpenRouter 改走POST /api/v1/responses。这次切换是单向one-way的一个 dialect 只会服务一种协议绝不会两种并存。因此迁移前必须回答一个关键问题——Chat Completions 路径上积累的所有 OpenRouter 定制在 Responses 方言下是否还有等价物本文档即是一份等价性记录parity record针对 Chat Completions 路径上的每一处 OpenRouter 定制逐一给出其在 Responses 下的实测对应形态。所有结论均来自 2026-09-09 在真实链路上的实测约 150 个请求、20 个模型、8 个厂商多数请求以debug: { echo_upstream_body: true }仅流式方式读取 OpenRouter 实际向上游发送的请求体。协议层OpenRouter 如何实现 Open Responses 规范基于开放规范的实现与厂商扩展OpenRouter 实现的是 Open Responses 规范openresponses.org——即 OpenAI Responses 规范加上厂商前缀条目vendor-prefixed itemsopenrouter:web_searchopenrouter:shellopenrouter:image_generation以及可能携带原始content的 reasoning 条目此外OpenRouter 在 reasoning 条目上额外增加了规范之外的format和signature字段。这一点直接影响了 big-AGI 解析器对 reasoning 条目的捕获与回放策略详见下文「Reasoning 条目」。无状态约束Responses 方言对 OpenRouter 而言是**纯无状态stateless**的设置store: true或非空的previous_response_id会返回400 invalid_prompt不存在 resume、delete 句柄因此必须强制关闭enableResumability/responses/compact端点直接 404。也就是说所有对话历史必须以输入条目input items的形式随请求发送无法依赖服务端存储。线上传输格式与事件流链路上采用标准 SSE包含:注释行与既有 demuxer 兼容无需新解析器。实测事件序列为response.created → in_progress → output_item.added → content_part.* → output_text.delta → function_call_arguments.* → output_item.done → completed | incomplete随后以data: [DONE]结束。用量usage信息始终出现在终止事件上字段包括input_tokens_details.cached_tokenscache_write_tokensoutput_tokens_details.reasoning_tokenscost数字cost_detailsis_byok注意官方文档中response.done与content_part.delta的事件示例已经过时实测线上并不出现这两个事件。条目 IDitem ids是合成的、不透明的、按响应一次性生成的如rs_tmp_*、msg_tmp_*、fc_tmp_*、ig_tmp_*不能跨请求引用。错误形态流开始前的错误JSON 格式携带数字型code与metadata.raw上游真实原因与_forwardOpenRouterDataError当前解包的形态一致且在stream: true下同样如此。Schema 校验失败错误是不透明的例如{ code: invalid_prompt, message: Invalid Responses API request }校验器详情偶尔出现在metadata.raw中。流式过程中的失败response.failed事件加上顶层error_type字段该字段在文档中有记载但实测未触发。未知参数的行为差异两种过滤机制的取舍值得注意未知的顶层参数会被直接透传并可能在上游 400实测foo_bar参数确实到达了 OpenAI而provider.require_parameters: true走的是另一条路——它让 OpenRouter在路由层就过滤掉无法处理所请求参数的端点报错为 No endpoints found that can handle the requested parameters。big-AGI 的 Chat Completions 适配器在限制性工具策略下会设置provider.require_parameters见 openai.chatCompletions.tswiretypes 层将其声明为require_parameters: z.boolean().optional()见 openai.wiretypes.ts注释明确指出其语义是仅路由到支持全部请求参数的提供商严格模式。稳定性与首字节延迟15/15 个受控流式请求全部完整完成一次约 40 请求的并发突发中出现两条被截断的流无终止事件TTFB首字节延迟与 Chat Completions 持平。X-OpenRouter-Metadataprovider 标签的载体设置请求头X-OpenRouter-Metadata: enabled后OpenRouter 会在响应体中加入openrouter_metadata包含所选 provider、strategy、region、attempt、generation_time、pipeline 等字段流式时出现在response.completed事件上。关键的兼容性约束该请求头不在 CORS 允许列表中——预检preflight仅允许X-Openrouter-Title、X-Title、X-Openrouter-Categories、HTTP-Referer、X-Session-IdX-Provider-Name虽然被 CORS 暴露但从未被发送响应体本身没有provider字段。结论服务端路径server path可以通过请求头保留 provider 标签而 CSF客户端直连路径会丢失它——这是等价性矩阵中loss级的两个损失之一。等价性矩阵30 项定制的逐条对照矩阵状态标记的语义如下状态含义ok同样形态即可工作原样迁移remap可工作但需要换成不同的请求形态port我方代码必须改动loss无等价物功能丢失以下完整列出全部对照结果并对关键条目补充源码级解释。Chat Completions 定制Responses 等价物状态粘性session_idadapter同名字段。带session_id时 3/3 次命中同一 provider不带时 1/3DeepSeek15 个端点okprovider.require_parametersdispatch相同okApp 归属请求头CSF相同我方请求头集合预检返回 204*okbody 中的providerparser仅 Metadata 请求头见协议层portCSF 下 loss通过顶层verbosity控制 Anthropic effort顶层verbosity对 Claude 被丢弃、对 GPT 直接 400moved to text.verbosity。reasoning.effort映射到output_config.effortlow、high、xhigh、max 已验证text.verbosity同时映射到它和 OpenAI 的text.verbosityremapAnthropicreasoning: { enabled, max_tokens }相同上游变成thinking: { type: enabled, budget_tokens }okAnthropicreasoning: { enabled: true }相同上游变成thinking: { type: adaptive }。在带预算的模型Haiku 4.5high上 effort 变为budget_tokens: 6400ok思考时删除temperatureOpenRouter 上游自己会丢弃它我方保留删除行为无害okFableenabled: false拒绝同样返回 400 Reasoning is mandatory for this endpointKimi K2.7 也如此。Sonnet 5 可干净禁用thinking: { type: disabled }okGeminireasoning: { enabled: true, max_tokens }相同上游映射为thinkingBudget。坑max_tokens不带enabled时在 2.5 Flash 上变成thinkingBudget: 0在 2.5 Pro 与 3.x 上 400 mandatory而 Chat Completions 能正确映射。务必总是发送enabled: trueokGemini effort 等级3.x 上为thinkingLevel2.5 上high变为thinkingBudget: 24576okOpenAI 兼容reasoning: { enabled, effort }相同。none通过minimal在 GPT-5.5 上变成lowmax在 GPT-5.6 上通过DeepSeek 收到reasoning_effortokreasoning.mode: pro相同响应model报告-proidokreasoning_effort去重不需要Responses 中只有reasoningokreasoning_details[]解析Reasoning 条目见后文。我方 parser 丢弃response.reasoning_text.deltaportReasoning 回放Chat Completions 从不回放reasoning_details。Responses 可整条回放、跨厂商翻译、容忍剥离后的历史port gain增益content 部件上的cache_control块级cache_control被接受但无效4.7k prompt、每个位置都测0 次缓存写入。可行形态prompt_cache_breakpoint: { mode: explicit }放在 system / developer / user 消息的input_text块上写入 4706、命中读取 4687顶层cache_control: { type: ephemeral }自动标记最后一个可缓存块写入 4697、重复时命中读取。assistantoutput_text上的标记被忽略remapSystem prompt 断点instructions无法携带标记。要么把 system prompt 作为 system 角色消息条目发送OpenRouter 会映射到 Anthropicsystem要么依赖顶层标记remap最多 4 个断点的裁剪仍然必需5 个标记会触发 Anthropic 的 A maximum of 4 blocks with cache_control 400okFable 强制tool_choice降级required与{ type: function }同样 400none与auto正常okplugins: [{ id: web }]被接受但 OpenRouter 已弃用。tools: [{ type: openrouter:web_search, engine?, max_results? }]在 Claude 与 DeepSeek 上可用产生一个openrouter:web_search条目action、id、status随后经response.output_text.annotation.added返回url_citation注解起止索引实测为 0。裸{ type: web_search }在 Claude 上也可用在 GPT 上保持原生形态web_search_call条目remapinclude: [web_search_call.action.sources]只有五个 include 值存在file_search_call.results、message.input_image.image_url、computer_call_output.output.image_url、reasoning.encrypted_content、code_interpreter_call.outputs此值 400。只对 OpenAI 模型发送它OpenAI-only托管code_interpreterGPT 会执行code_interpreter_call条目。Claude 接受该工具但编造输出。openrouter:shell对所有人执行条目openrouter:shellremapmodalities: [image]image_config相同上游变为responseModalitiesimageConfig。输出为image_generation_call条目result是 data URLdata:image/png;base64,...output_format: null事件序列image_generation_call.in_progress | generating | completed。我方 parser 期望的是裸 base64 加output_formatport小改动video_urlpart{ type: input_video, video_url: url }OpenRouter 扩展。Chat Completions 的 part 形态直接 400remapmodalities: [audio]输出任何形态下 Responses 都 400。Chat Completions 目前可流式输出音频openai/gpt-audio、gpt-audio-mini、两个 Lyria idloss图像、PDF、音频输入input_imagedata URL、input_filefile_dataClaude/Gemini/GPT、input_audioGemini均可用okusage.cost、cache_write_tokens终止事件usage下同名字段未观察到image_tokensok结束原因end_turn、COMPLETE、eos、network_error被status与incomplete_details取代okdelta.images[]image_generation_call条目port小改动流前错误转发同形态见协议层ok: OPENROUTER PROCESSING注释行相同okstream_options.include_usage不需要okResume 与 delete 句柄不可能存在无状态无损失Chat Completions 也没有okopenrouter.models.ts、vendor 文件、图片端点不动。/models没有按模型标注 Responses 能力supported_parameters仍沿用 Chat Completions 的名字。所有列出的模型实测可用包括openrouter/auto、openrouter/free、:freeid 与~别名ok额外验证项矩阵之外还验证了以下行为configuration_update输入条目Fable 5.1parallel_tool_calls: falsetool_choice: nonestrict工具text.format的 json_schema 与 json_objectClaude 会忽略 json_objectreasoning.summary在 GPT 上的表现只有当 prompt 确实需要思考时才返回 summary partsmodelsfallback 数组service_tier。不生效的能力reasoning.excludecontent 仍被返回、stopLlama 上被丢弃、DeepSeek provider 上 400。big-AGI 两者都不发送。源码层面的印证Responses 适配器通过「方言怪癖表」来承载这类逐厂商差异而不是散落一堆isDialectX标志_RSP_DIALECT_QUIRKS见 openai.responsesCreate.ts为每个方言覆盖默认值_RSP_QUIRKS_DEFAULT字段包括vndNamespace、emitMessagePhase、reasoningContextAllTurns、webSearchToolfull | bare | none、codeInterpreterTool、imageGenWebP、toolChoiceOnlyAuto、minOutputTokens。OpenRouter 迁移后将在这里新增一行openrouter配置这正是文档「Port list」中 adapter 改动的落点。Reasoning 条目格式、载体与流事件Responses 方言中思考内容以独立条目存在{ type: reasoning, id, status, format, summary[], content[]?, encrypted_content?, signature? }不同厂商使用不同的format、不同的内容载体与不同的流事件厂商format载体流事件OpenAIopenai-responses-v1encrypted_content包装形态blob.base64 { endpoint_slug }仅在reasoning.summary时附带 summaryreasoning_summary_text.deltaxAIxai-responses-v1encrypted_content summaryreasoning_summary_text.deltaAnthropicanthropic-claude-v1content[].reasoning_textsignaturereasoning_text.deltaGeminigoogle-gemini-v1content[].reasoning_text工具调用回合时encrypted_contentthought signaturereasoning_text.deltaDeepSeek、Qwen、Kimi、GLM、MiniMax、路由厂商unknowncontent[].reasoning_textreasoning_text.delta实测补充的三条重要行为自适应思考的 Claude4.6、Sonnet 5、Fable在平凡 prompt 下、任何 effort 都不会发出 reasoning 条目——这是模型行为而非端点差异非平凡 prompt 在任意 effort 下都会思考。Gemini在output[]顺序中先发message再发reasoning。回放整条原样回放均可工作——同模型往返6 个厂商、跨厂商回放9 个组合例如把 Claude 条目放进 GPT 历史、把 GPT 条目放进 Claude 历史、以及剥离 reasoning 后的历史全部完整完成。include: [reasoning.encrypted_content]在处处无害。reasoning.context仅 GPT-5.6 支持更低版本上游 400。与解析器的关系big-AGI 的 Responses 事件解析器 openai.responses.parser.ts 目前对response.reasoning_text.delta与.done仅做占位处理标记为FIXME并打印 DEV 日志尚未转发为 reasoning 文本见 L667-L679而对reasoning_summary_text.delta已实现为追加 reasoning 文本。这正是文档「Reasoning items → 我们的 parser 丢弃response.reasoning_text.delta→port」对应的现状。在请求侧适配器已支持reasoning: { effort, mode, context }的组装mode: proGPT-5.6替代独立-pro模型、context: all_turnsgpt-5.4 才接受、Azure 可能滞后、effort 为none时不发送见 openai.responsesCreate.ts以及始终把reasoning.encrypted_content加入include列表。回放侧则依赖_vnd.namespace.reasoningItem中同时存在encryptedContent与id才构造reasoning输入条目裸 id 会在无状态模式下 404 Item with id rs_... not found裸加密内容则是撕裂句柄见 openai.responsesCreate.ts。各厂商的加密 key 与私有条目 id 不同因此每个厂商都拥有独立的_vnd命名空间blob 绝不跨厂商流通。Port List四层代码的落地清单Dispatch 层chatGenerate.dispatch.ts将openrouter加入RESPONSES_ONLY_DIALECTS集合。当前集合仅含metaai、sakanaai、xai见 L37-L41注释说明该集合的含义是无论逐模型的vndOaiResponsesAPI标志如何所有模型只服务 Responses API 的方言。路由处isResponsesAPI !!model.vndOaiResponsesAPI || RESPONSES_ONLY_DIALECTS.has(dialect)L290将据此为 OpenRouter 选择 Responses 请求构造器与事件解析器。enableResumability强制为false无状态限制。仅在服务端路径发送X-OpenRouter-Metadata: enabledCSF 因 CORS 限制无法携带。Adapter 层openai.responsesCreate.ts在_RSP_DIALECT_QUIRKS中新增openrouter行vndNamespace: openrouterweb search 使用openrouter:web_search工具形态而不是原生web_search/web_search_previewOpenRouter 的 web 工具线上形态定义在 aix.wiretypes.openrouter.tswiretypes 中也预留了openrouter:web_search字面量openai.wiretypes.tscodeInterpreterTool: false——改用openrouter:shell提供代码执行imageGenWebP: falsereasoningContextAllTurns: false不携带 sources include把 Chat Completions 的 reasoning 块迁移过来用reasoning.effort取代原来的verbosity隧道Chat Completions 路径上 verbosity 经 OpenRouter 映射到 Anthropicoutput_config.effort见 openai.chatCompletions.ts在每一个max_tokens旁保留enabled: true保留session_id、require_parameters、image modalities image_config、input_video、Fable 强制工具降级缓存标记改写为 system 角色与 user 消息input_text块上的prompt_cache_breakpoint并执行最多 4 个断点的裁剪需要断点时system prompt 以 system 角色消息条目发送。Parser 层openai.responses.parser.ts把reasoning_text.delta与.done接入 reasoning 文本输出替换当前 FIXME 占位捕获完整 reasoning 条目format、signature、content、encrypted_content存入_vnd.openrouter并整条原样回放接受openrouter:*条目解析 data URL 形态的image_generation_call.result当前 parser 对image_generation_call已实现完整事件流in_progress → generating → [partial_image]* → completed最终结果在output_item.done处理见 L738-L764但result目前按裸 base64 output_format期望需要改为 data URL 形态从openrouter_metadata提取 provider 标签容忍 Gemini 的 message/reasoning 条目顺序。Wiretypes 层AixWire_Vendors.RSP_VENDORS、AixWire_Parts._vnd、DMessageFragmentVendorState三处注册openrouter。明确不动的部分openrouter.models.ts的参数规格、vendor 配置、速率限制器、OAuthopenrouter.oauth.ts、图像生成路由端点t2i/openrouterGenerateImages.ts全部保持原样。结论等价性成立代价可接受对照结果约 30 项定制中24 项为一对一保留ok或形态重映射remap5 项解析器移植port2 项损失audio 输出四个模型openai/gpt-audio、gpt-audio-mini、两个 Lyria id 在 Responses 下全部 400CSF 路径下丢失 provider 标签。整体评估等价性成立切换可行——且 Reasoning 能力反而得到增强统一条目形态、跨厂商回放、configuration_update支持缓存与搜索以新形态存活。待 parser 层工作落地后切换即为安全。如需进一步深入可继续阅读仓库内的相关实现与记录协议记录原文kb/modules/LLM-openrouter-responses.md请求构造与方言怪癖表src/modules/aix/server/dispatch/chatGenerate/adapters/openai.responsesCreate.ts事件解析与 reasoning/图像条目处理src/modules/aix/server/dispatch/chatGenerate/parsers/openai.responses.parser.ts路由分派与RESPONSES_ONLY_DIALECTSsrc/modules/aix/server/dispatch/chatGenerate/chatGenerate.dispatch.tsOpenRouter 参数规格保持不变src/modules/llms/server/openai/models/openrouter.models.tsOpenRouter web 工具线上形态src/modules/aix/server/api/aix.wiretypes.openrouter.ts【免费下载链接】big-AGIAI suite powered by state-of-the-art models and providing advanced AI/AGI functions. Includes AI personas, AGI functions, world-class Beam multi-model chats, text-to-image, voice, response streaming, code highlighting and execution, PDF import, presets for developers, much more. Deploy on-prem or in the cloud.项目地址: https://gitcode.com/GitHub_Trending/bi/big-AGI创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考