Helicone Worker 内置 LLM 协议转换网关 llmmapper 模块解析与同步维护指南
【免费下载链接】helicone🧊 Open source LLM observability platform. One line of code to monitor, evaluate, and experiment. YC W23 🍓项目地址: https://gitcode.com/GitHub_Trending/he/helicone
导读
Helicone 的 Worker(Cloudflare Worker 侧 AI Gateway 实现)内置了一个名为llmmapper的 LLM 协议转换模块,它允许客户端以某一家大模型厂商的 API 协议发起请求,由网关在请求/响应链路上实时转换为另一家厂商的协议格式,从而实现 OpenAI、Anthropic、Google Gemini 以及 OpenAI Chat Completions 与 Responses API 之间的无缝互通。本文以 worker/src/lib/clients/llmmapper/README.md 为骨架,结合仓库内该模块的完整源码与测试用例,深入讲解其入口分发逻辑、四条协议转换路由(oai2ant、oai2google、oaiChat2responses)、流式 SSE 转换原理,以及从上游 llmmapper 仓库同步更新的维护流程,读完后你将能够理解并复用它来扩展新的协议转换能力。
一、模块定位:从上游 llmmapper 仓库同步而来的协议转换层
仓库内 worker/src/lib/clients/llmmapper/README.md 只有短短几行,却明确了该目录的来源与维护契约:
providers与routers目录是从 llmmapper 仓库复制粘贴而来的;- 一旦上游 llmmapper 仓库发生任何变更,本仓库都必须同步更新以保持一致;
- 同步方式:把上游仓库的
providers、routers两个目录整体复制进本目录,随后执行yarn lint-fix修复 lint 报错。
从当前仓库的实际目录结构看,该目录内保存的是router/(路由转换实现)与llmmapper.ts(入口),而真正执行协议转换的转换器(toOpenAI、toAnthropic、AnthropicToOpenAIStreamConverter、ChatToResponsesStreamConverter等)则统一封装在 monorepo 的 packages/llm-mapper 包中,通过@helicone-package/llm-mapper/transform/...路径被引用。也就是说:worker/src/lib/clients/llmmapper/负责"网关侧的路由编排与响应封装",而转换细节沉淀在packages/llm-mapper中,两者共同构成完整的协议转换能力。这也解释了为什么同步时只需要关注providers(厂商数据)与routers(路由转换)两个目录。
二、网关入口:llmmapper() 的请求分发逻辑
入口文件 worker/src/lib/clients/llmmapper/llmmapper.ts 定义了llmmapper(targetUrl, init)函数,其职责是根据目标 URL 的路径前缀决定走哪条转换路由:
- 路径以
/oai2ant开头:先通过tryJSONParse解析请求体(解析失败直接返回400 Invalid body),随后依据请求体中的stream字段分流:stream为真:调用antStream2oaiStream,走Anthropic 流式 SSE → OpenAI 流式 SSE转换;- 否则:调用
ant2oai,走Anthropic 非流式 JSON → OpenAI JSON转换;
- 其他路径:返回
404 Unsupported path。
这个入口函数被 worker/src/lib/clients/ProviderClient.ts 中的callWithMapper挂载:当请求的目标主机名是gateway.llmmapper.com时,Worker 不会直接fetch上游,而是把请求体与头信息交给llmmapper()处理;若请求没有 body,则返回404 Unsupported, must have body;一旦转换过程中抛出异常,则返回状态码10502(Helicone LLMMapper gateway error)。由此可以推断:llmmapper 网关以"特殊主机名 + 路径前缀"作为路由标识,是对外暴露协议转换能力的一组虚拟端点。
三、路由一:/oai2ant —— Anthropic 与 OpenAI 协议互转
3.1 非流式转换 ant2oai
worker/src/lib/clients/llmmapper/router/oai2ant/nonStream.ts 实现了非流式转换ant2oai,完整链路如下:
- 使用
toAnthropic(body)把 OpenAI Chat 风格的请求体转换为 Anthropic Messages API 格式(转换器来自@helicone-package/llm-mapper/transform/providers/openai/request/toAnthropic); - 从请求头中提取
Authorization,剥离Bearer前缀后作为 Anthropic 的x-api-key;若请求头缺少anthropic-version,则回退到默认值2023-06-01; - 以
POST https://api.anthropic.com/v1/messages转发请求; - 响应侧通过
ant2oaiResponse使用toOpenAI()(来自.../anthropic/response/toOpenai)把 Anthropic 响应体反向转换为 OpenAI 格式,并透传状态码与响应头;若转换失败则原样返回上游响应,保证错误信息不丢失。
值得注意的细节是:Authorization的提取同时兼容了"已有Bearer前缀"与"裸 key"两种写法,且Content-Type被强制设为application/json,这是直接面向生产可用的边界处理。
3.2 流式转换 antStream2oaiStream 与 Bedrock 事件流支持
流式场景远比非流式复杂,worker/src/lib/clients/llmmapper/router/oai2ant/stream.ts 给出了完整的工程实现:
antStream2oaiStream:同样先做请求体转换与鉴权头处理,再请求 Anthropic/v1/messages;上游返回非 2xx 时直接透传错误体;无响应体时返回500 No response body;ant2oaiStreamResponse:把上游流式响应交给ant2oaiStream转换,同时改写响应头:content-type设为text/event-stream; charset=utf-8、cache-control: no-cache、connection: keep-alive,并删除content-length(因为流式输出长度未知),这正是 OpenAI SSE 客户端的标准期望;- 流式转换核心
ant2oaiStream依据上游content-type自动选择两条解码路径:- Anthropic SSE 路径(
readAnthropicSSE):用TextDecoder边读边缓冲,按\n\n切分 SSE 消息,交由AnthropicToOpenAIStreamConverter.processLines逐块转换为 OpenAI chunk,再以data: {...}\n\n形式重新编码输出,流结束时追加data: [DONE]\n\n; - Bedrock Event Stream 路径(
readBedrockEventStream):当content-type为application/vnd.amazon.eventstream时启用。实现借助@smithy/eventstream-codec的EventStreamCodec按帧解码——每条消息最小长度为 16 字节(4 字节总长 + 4 字节头长 + 4 字节 prelude CRC + 4 字节 CRC,源码注释MINIMUM_EVENT_STREAM_MESSAGE_LENGTH = 16印证了这一点),逐帧识别:event-type头,跳过非chunk事件、解出error事件并抛出,再把 payload 中的 base64bytes字段解码后送入同一转换器。
- Anthropic SSE 路径(
也就是说,同一套 OpenAI 流式输出协议,背后可以同时承接 Anthropic SSE 与 AWS Bedrock 二进制事件流两种上游格式,这是该模块最具工程价值的部分之一。
四、路由二:/oai2google —— Google Gemini 响应转 OpenAI
worker/src/lib/clients/llmmapper/router/oai2google/nonStream.ts 与 worker/src/lib/clients/llmmapper/router/oai2google/stream.ts 提供了 Google → OpenAI 方向的转换:
goog2oaiResponse:解析 Google 响应体(类型GoogleResponseBody),用toOpenAI()(来自.../google/response/toOpenai)转换为 OpenAI JSON 返回,失败时回退原响应;goog2oaiStream/goog2oaiStreamResponse:使用GoogleToOpenAIStreamConverter完成 SSE 流转 SSE,缓冲切分与响应头改写逻辑与 oai2ant 流式路径保持一致(text/event-stream; charset=utf-8、no-cache、keep-alive、删除content-length、结束追加data: [DONE])。
从源码结构看,oai2google目前实现的是响应方向的转换,与 oai2ant 的"请求 + 响应双方向"略有不同,这是使用时需要注意的能力边界。
五、路由三:/oaiChat2responses —— Chat Completions 升级为 Responses API
OpenAI Responses API 是 Chat Completions 的后继接口形态,worker/src/lib/clients/llmmapper/router/oaiChat2responses/nonStream.ts 与 worker/src/lib/clients/llmmapper/router/oaiChat2responses/stream.ts 负责把旧协议的请求/响应翻译为新协议:
oaiChat2responsesResponse:读取响应体文本,按OpenAIResponseBody解析后交给toResponses()(来自.../responses/openai/response/toResponses),产出object: "response"形态的新协议响应,并透传状态码与响应头;oaiChat2responsesStream:逐行消费上游data:SSE 块,跳过[DONE]与注释行,将每个ChatCompletionChunk交给ChatToResponsesStreamConverter.convert生成一组事件,再编码为event: <type>+data: {...}双行格式的 SSE 输出——这种"命名事件"格式正是 OpenAI SDK 对 Responses 流的标准要求,同时记录是否已发出response.completed事件;oaiChat2responsesStreamResponse:沿用统一的响应头改写模式封装转换后的流。
该路由的协议语义(response.output_text.delta、response.completed等事件名)在测试中有明确验证,见下文。
六、测试验证:协议转换的正确性保障
转换正确性是此类网关的命脉,仓库在 worker/test/ai-gateway/map-responses.spec.ts 中为 Chat → Responses 路由提供了两组 vitest 用例:
- 非流式用例:构造一个包含
tool_calls(calc({"x":1}))、finish_reason: "tool_calls"、usage的chat.completion响应,断言转换结果object为response、输出数组中角色为assistant、文本内容变为{ type: "output_text", text: "Hi from chat", annotations: [] }、函数调用被映射为{ call_id, name, arguments },且usage字段由prompt_tokens/completion_tokens正确映射为input_tokens/output_tokens; - 流式用例:依次喂入"角色建立 chunk → 文本 delta + finish → usage chunk → [DONE]",断言输出流为
text/event-stream,且包含event: response.created、event: response.output_text.delta、event: response.output_text.done、event: response.completed等事件。
这些用例直接印证了上一节描述的字段映射与事件语义,可作为日后扩展新路由时的测试范式参考。
七、同步维护流程:如何与上游 llmmapper 保持对齐
回到 worker/src/lib/clients/llmmapper/README.md 声明的维护契约,仓库内该目录的实际维护方式可归纳为三步:
- 整体覆盖:将上游 llmmapper 仓库的
providers与routers两个目录整体复制到worker/src/lib/clients/llmmapper/下(当前仓库对应目录为router/与入口llmmapper.ts),保持目录结构一一对应; - 契约校验:确认目录内对
@helicone-package/llm-mapper各 transform 转换器的引用路径不变——因为转换器本体维护在 packages/llm-mapper(如transform/providers/anthropic/response/toOpenai、transform/providers/openai/request/toAnthropic、transform/providers/responses/streamedResponse/toResponses等),同步上游仅需聚焦路由编排层; - 质量门禁:运行
yarn lint-fix自动修复 lint 错误,随后建议运行 worker/test/ai-gateway/map-responses.spec.ts 等测试确认转换行为未回归。
该流程的本质是"路由编排本地化、转换器库共享化":协议细节集中在 llm-mapper 包中演进,网关侧只保留薄薄的路由分发与响应封装,从而让同步成本降到最低。若你需要为本网关新增一条转换路由,参照现有router/oai2ant的目录结构与 ProviderClient.ts 的挂载方式即可平滑扩展。
【免费下载链接】helicone🧊 Open source LLM observability platform. One line of code to monitor, evaluate, and experiment. YC W23 🍓项目地址: https://gitcode.com/GitHub_Trending/he/helicone
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考