本包是 RouteCodex V2 的“工具治理唯一入口 + 协议转换核心”,实现严格的“前半段/后半段”双向流水线,并通过 `defaultSseCodecRegistry` 统一治理所有 SSE ↔ JSON 转换。核心事实:
npm install rcc-llmswitch-core本包是 RouteCodex V2 的“工具治理唯一入口 + 协议转换核心”,实现严格的“前半段/后半段”双向流水线,并通过 defaultSseCodecRegistry 统一治理所有 SSE ↔ JSON 转换。核心事实:
1. 唯一入口:所有 HTTP 请求(Chat/Responses/Anthropic/Gemini)必须先进入前半段 Conversion,SSE 输入一律由 SSEInputNode → defaultSseCodecRegistry → 对应协议 JSON,再映射为标准 Chat 请求。
2. 唯一治理点:后半段 Chat Pipeline 是唯一可修改工具行为的位置;其他节点仅做形状转换或流式编解码。
3. 唯一出站规则:响应阶段的 output 格式 (JSON/SSE) 只看原入口端点与入站 streaming 标记;providerType 只能决定 inbound converter 与 provider 调用,不得改写 outbound 协议。
- 前半段(Front-Half,Conversion):按端点最小映射为 OpenAI Chat 标准形状(不做工具治理、不兜底)。
- 后半段(Back-Half,Chat Pipeline):统一工具治理、参数修复、MCP 两步暴露、流/非流一致化与最终响应组装。
遵循 AGENTS.md 的 9 大架构原则:职责单一、最小兼容、Fail Fast、无兜底、配置驱动、统一出口/入口。
版本提示:
- 从 0.2.95 起,Responses→Chat 的映射严格“只做形状转换”,不再注入兜底文本或工具;所有治理只在后半段进行。
- 从 0.3.20 起,Anthropic/Chat/Responses 三端在 V2 前后半段上实现双向闭环:请求侧统一 Anth/Chat/Responses→Chat→providerProtocol,响应侧统一 providerProtocol→Chat→入口协议,所有工具调用/结果在 Anth↔Chat↔Anth 与 Responses↔Chat↔Responses 流程中保持字段/分组不变。
- 自 0.4.2 起,Responses ↔ Anthropic 走完整的 Chat 桥后可直接互通:Responses upstream SSE → Chat canonical → Anthropic outbound,以及 Anthropic inbound → Chat canonical → Responses outbound,工具调用/函数输出在双向往返中保持 shape,不再需要任何协议猜测或字段兜底。
- 规划中:Gemini/Chat 双向闭环(gemini-messages 协议)将按与 Anthropic 完全平行的方式接入,仅在前半段/后半段增加独立 codec,不影响现有 Chat/Responses/Anthropic 行为。
👉 Hub Pipeline 是当前唯一入口。 如果 Host 需要直接编排 Virtual Router,请参见 docs/HUB_PIPELINE_USAGE.md,了解如何通过 bootstrapVirtualRouterConfig + HubPipeline 完成初始化与热更新。
``
入站请求(任意端点)
├─ Chat (/v1/chat) ┐
├─ Responses (/v1/responses) ├─ 前半段 Conversion → 规范化为 Chat 标准 JSON(非流)
└─ Anthropic (/v1/messages) ┘ - 仅做字段/形状映射;不做工具治理/文本收割
- 统一关闭上游直通,前半段需要时将 SSE 合成为非流 JSON
▼
Chat Back-Half(唯一治理入口)
- 工具治理(canonicalize/repair/去重/ID配对)
- MCP 两步暴露(列表→读取/模板)
- reasoning/think 标准化(按端点策略保留或过滤)
- finalize:确保 tool_calls / tool role、finish_reason、content 形状一致
▼
Provider(HTTP 通信,仅转发)
▼
出站响应(统一从 Chat 反向映射)
- Chat:直接输出标准 OpenAI Chat 形状
- Responses:从 Chat 还原 required_action / output / items
- Anthropic:映射为 Anthropic 支持的形状
`
要点:
- “后半段唯一治理点”:三端(Chat/Responses/Anthropic)最终都走同一套 Chat 后半段。
- Responses↔Anthropic 互通:Responses provider 输出先回落到 Chat,再由入口端点(例如 /v1/messages)决定重新构建 Anthropic wire 形状;反向同理,Anthropic 输入先 canonical 化为 Chat 后再落地 Responses 请求,由 outbound streaming 配置决定最终 SSE/JSON 行为。
- “前半段最小映射”:只做协议字段/工具形状转换,不做文本工具收割/兜底/治理。
- “流式一致”:默认不上游直通(upstream SSE OFF),前半段将流合成为非流 JSON,再走后半段,保证一致。
- 唯一入口:RouteCodex 主包只能通过 src/modules/llmswitch/bridge.ts → dist/bridge/routecodex-adapter.js 调用 conversion v3;禁止旁路 import。config/llmswitch/pipeline-config.json
- 配置驱动管线:(或通过 LLMSWITCH_PIPELINE_CONFIG 指定)声明每条入/出站线路的节点序列:SSE Input → Provider Input → Chat Process → Provider Output → SSE Output,Responses/Anthropic 线路结构一致。nodes/sse/*
- 节点职责
- :入站 SSE 正规化、出站 SSE 序列化以及纯透传,占位但不改写业务数据。nodes/input/*
- :OpenAI Chat / Responses / Anthropic 请求解析器,校验 model/messages,输出 canonical standardizedRequest。nodes/process/chat-process-node
- :唯一的工具治理点,负责 tool_calls 修复、MCP 两步暴露、上下文与 streaming 策略、passthrough 判定等。nodes/output/*
- :基于 processedRequest 生成 Provider 协议响应(choices、usage、content blocks)。nodes/response/*
- :出站方向入口,把 Provider 响应重新映射为 canonical,再交由 output/sse 节点返回。process
- 工具治理原则
- 除 Compatibility 层为了满足 Provider/OpenAI 形状所做的最小字段修剪外,任何模块都不得修改工具语义。
- Input/Output/SSE/Response 节点只做格式转换或序列化;所有工具解析/修复/透传/执行判定必须发生在 链路。chat-process-node
- 如需新增与工具相关的功能,必须在 中实现或扩展新的 process 节点,并由 pipeline 配置显式启用。
1) HTTP Server 入站(端点路由)
- 接收 /v1/chat、/v1/responses、/v1/messages 的原始请求体
- 写入 http-request 快照(可选:.parsed 摘要)
2) 前半段 Conversion(本包 v2/conversion/...)
- Chat:校验/轻量规范(保持 OpenAI Chat 标准)
- Responses:instructions + input 映射为 Chat.messages;function_call/tool_result → assistant.tool_calls / role='tool'(只形状,不治理)
- Anthropic:Claude 消息/工具映射为 OpenAI Chat(仅形状)
- SSE:如入站为 SSE,先在前半段合成为非流 JSON(默认),确保后续统一路径
3) 后半段 Chat Pipeline(本包 v2/conversion/openai-chat/...)
- request-shape:
- 统一 messages.content 为 string
- 删除不支持字段(如 stream)
- request-tools-stage:
- canonicalizeChatResponseTools:不变式(content=null,finish_reason=tool_calls)
- JSON/JSON5 风格参数修复,失败回退 "{}"
- 工具 ID 生成/去重
- MCP 两步暴露(仅在后半段)
- provider 调用(Provider 层):HTTP 转发 + 快照;不做工具处理
- response-shape:
- 统一 Chat 响应形状、finish_reason、content
- reasoning/think 清理或保留(按端点策略)
- response-tools-stage:
- 工具结果配对(role='tool' 与 tool_calls.id 对齐)
- Responses 反向桥接(仅映射,不治理):required_action + items/output 还原
4) 出站响应
- Chat:OpenAI Chat
- Responses:OpenAI Responses
- Anthropic:Anthropic Messages
function chatFrontHalf(payload):
assert(Array.isArray(payload.messages))
drop(payload.stream) // 统一非流
ensureOpenAIChatShape(payload)
return payload
`$3
`
function responsesFrontHalf(payload):
ctx = captureResponsesContext(payload)
tools = normalizeTools(payload.tools)
msgs = []
if ctx.instructions: msgs.push({role:'system', content:trim(ctx.instructions)}) for entry in payload.input:
switch(entry.type):
case 'function_call'|'tool_call':
name = entry.name || entry.function?.name
args = parseArguments(entry.arguments || entry.function?.arguments)
msgs.push({role:'assistant', tool_calls:[{id:genId(entry), type:'function', function:{name, arguments:stringify(args)}}]})
case 'function_call_output'|'tool_result'|'tool_message':
id = entry.tool_call_id || entry.call_id || entry.tool_use_id || entry.id
out = normalizeToolOutput(entry.output)
msgs.push({role:'tool', tool_call_id:id, content:String(out ?? '')})
default:
// 优先 entry.message.content[]
if entry.message?.content: text = collectText(entry.message.content)
else if entry.content: text = collectText(entry.content)
else if entry.text: text = entry.text
if text: msgs.push({role:normalizeRole(entry.role), content:text})
return { model, messages: msgs, tools: tools, tool_choice: payload.tool_choice }
`注意:不注入“伪 user”,不做文本工具收割/治理,严格只做形状转换。
$3
`
function anthropicFrontHalf(payload):
// Claude → OpenAI Chat 映射
for m in payload.messages:
map role/parts → Chat message
map tools → OpenAI function tools
drop(stream)
return chatPayload
`$3
Gemini REST 规范使用
contents[].role + parts[] + tools.functionDeclarations 的结构,与 Anthropic 的 Messages 协议类似,本包会以完全平行的方式接入一个 gemini-messages 协议线:目标:
- 仅在前半段/后半段增加
gemini-messages 的 codec,既有 Chat/Responses/Anthropic 行为不受影响。
- 非 passthrough 流水线继续遵循统一约束:entryProtocol → Chat → providerProtocol(请求)与 providerProtocol → Chat → entryProtocol(响应),中段只认 Chat。计划中的 main codec 文件:
-
src/conversion/codecs/gemini-openai-codec.ts
- buildOpenAIChatFromGeminiRequest(gReq):Gemini 请求 → OpenAI Chat 请求
- buildGeminiRequestFromOpenAIChat(chatReq):OpenAI Chat 请求 → Gemini 请求
- buildOpenAIChatFromGeminiResponse(gResp):Gemini 响应 → OpenAI Chat completion
- buildGeminiFromOpenAIChat(chatResp):OpenAI Chat completion → Gemini 响应(用于“入口/出口都是 Gemini”的回环测试与 /v1/gemini 出口)请求侧(Gemini → Chat)映射要点:
-
contents: Content[]
- Content.role:
- "user" → Chat role: "user"
- "model" → Chat role: "assistant"
- "system" → Chat role: "system"(或汇总进顶层 systemInstruction,再统一收敛为 Chat system 消息)
- "tool" → Chat role: "tool"(对应 functionResponse)
- Content.parts: Part[]:
- { text } → Chat message.content 文本(可继续使用 text-markup-normalizer 做轻量标准化,不做治理)
- { functionCall: { name, args } } →
- 映射为 Chat assistant.tool_calls[]:
- type: "function"
- function.name = name
- function.arguments = JSON 字符串(通过现有 jsonish.repairArgumentsToString 进行安全修复)
- { functionResponse: { name, response } } →
- 映射为 Chat role: "tool" 消息:
- tool_call_id 与前一轮 tool_calls 对齐(ID 生成与去重仍由后半段工具治理负责)
- content 为字符串化后的结果(不丢失结构,可放入 metadata 保留原始 JSON)
- 其它多模态部件(inlineData/fileData/...):
- 按“不丢失信息”原则,先透传到 Chat 消息的 metadata.vendor.gemini.parts[],后续再根据需要扩展。
- systemInstruction?: Content:
- 统一转换为 Chat 顶层 system 消息(或合并进 messages 中的 system 段),保持与 Anthropic 一致的“所有系统指令在 Chat 段收敛”策略。
- tools.functionDeclarations:
- 映射为 Chat tools[].function,字段基本一一对应(name/description/parameters)。
- 采样/安全配置:
- generationConfig.temperature/topP/topK/maxOutputTokens/stopSequences 映射为 Chat 顶层采样参数;
- safetySettings 透传到 metadata.vendor.gemini.safetySettings,保证信息不丢。响应侧(Gemini → Chat)映射要点:
-
candidates[0].content.parts[]:
- text 部分 → Chat choices[0].message.content(多段合并)
- functionCall 部分 → Chat choices[0].message.tool_calls[](与请求侧规则对应)
- finishReason → Chat finish_reason(STOP → "stop"、MAX_TOKENS → "length" 等)
- usageMetadata → Chat usage(prompt_tokens/output_tokens/total)Chat → Gemini(请求与响应):
- 与 Anthropic 完全平行,在
buildGeminiRequestFromOpenAIChat / buildGeminiFromOpenAIChat 内按 Chat canonical 重新投影回 Gemini wire 形状:
- system → systemInstruction 或系统 Content;
- assistant + tool_calls → role: "model", parts:[{ functionCall... }];
- role: "tool" → role: "tool", parts:[{ functionResponse... }];
- tools → functionDeclarations;
- 采样/安全配置 → generationConfig / safetySettings。与现有协议的隔离性:
-
gemini-messages 只在 providerProtocol 显式配置为 "gemini-messages" 时参与路由,且 codec 文件和分支独立存在:
- 不修改 openai-chat、openai-responses、anthropic-messages 的现有逻辑;
- 中段 Chat 工具治理/最终器不依赖具体 provider 协议,只认 canonical Chat completion。
- RouteCodex 主包侧仅需:
- providerType: "gemini" → providerProtocol: "gemini-messages";
- Provider 继续复用 OpenAIStandard,仅负责 HTTP 通信。测试与回环(规划):
- 类似 Anthropic/Responses,将新增:
-
gemini-request-closed-loop.ts:Gemini 请求 → Chat 请求 → Gemini 请求',检查字段/工具/role 数量与顺序一致。
- gemini-in-out-closed-loop.ts:通过 routecodex-adapter 的 processIncoming / processOutgoing 对真实 codex-samples 做闭环。
- 如有需要,扩展 streaming 测试,验证 streamGenerateContent → Chat SSE delta → Responses/Anthropic SSE 的一致性。
后半段:主要处理点与职责
- request-shape(v2/conversion/openai-chat/request-shape.ts)
- 统一 messages.content 为 string
- 删除不支持字段(如 stream)
- request-tools-stage(v2/conversion/openai-chat/request-tools-stage.ts)
- canonicalizeChatResponseTools:不变式(content=null,finish_reason=tool_calls)
- jsonish.repairArgumentsToString:把任意形态 arguments 修复为安全 JSON 字符串
- 工具 ID 生成/去重
- MCP 两步暴露(仅在后半段)
- response-shape(v2/conversion/openai-chat/response-shape.ts)
- Chat 响应标准化;think/推理文本按端点策略保留/清理
- response-tools-stage(v2/conversion/openai-chat/response-tools-stage.ts)
- role='tool' 与 tool_calls.id 配对
- Responses 反向桥接(仅映射,不治理):required_action + items/output 还原
流式(SSE)策略
- 默认不上游直通(provider 配置未显式允许时)。
- 前半段把 SSE 合成为非流 JSON;后半段统一处理,再需要时用本包 streaming 模块合成 Responses SSE。快照与排错
- 快照目录:~/.routecodex/codex-samples/
- _http-request.json / _http-request.parsed.json
- *_pipeline.llmswitch.request.post.json(进入后半段前的 Chat 形状)
- *_pipeline.provider.request.pre.json(上游请求体,顶层仅 Chat 字段)
- 常见 1214 根因:
- 缺失用户消息(Responses 输入未包含 user 文本,且前半段不兜底)
- 顶层出现 data/metadata/stream 等额外键(应移除“添加逻辑”,而非末端清理)设计原则与边界
- 工具治理唯一在后半段;前半段绝不进行文本工具收割/参数修复
- 兼容层只做 provider 特定最小映射;Provider 只做 HTTP 通信
- Fail Fast:形状不合规直接报错,不做隐藏兜底版本与构建
- 构建:npm run build
- 打包:npm pack
- 发布:npm publish`