LLMDock 是从 new-api 中拆分出的独立 Go 模块,提供常用大模型文本协议的 DTO、请求转换、响应转换和流式事件转换。
它只负责协议层的数据建模与语义转换,不包含 HTTP 服务、上游请求发送、渠道调度、鉴权、计费或数据库逻辑。因此可以脱离 new-api 主模块,嵌入其他 Go 网关或代理服务。
- 在 OpenAI Chat Completions、OpenAI Responses、Anthropic Messages 和 Gemini
generateContent之间转换 - 同时支持请求、非流式响应和增量流式响应
- 自动根据 DTO 类型识别源协议,并选择内置的直接或多跳转换路径
- 返回转换器 ID、质量等级、实际转换步骤和统一 usage,方便审计与调试
- 支持常用的文本、多模态内容、工具调用、推理内容和 usage 映射
- 作为独立 Go module 构建,不依赖 new-api 主模块、Gin、数据库或全局设置
以下四种文本协议支持任意两种格式之间的转换:
| 源格式 \ 目标格式 | OpenAI Chat | OpenAI Responses | Claude Messages | Gemini |
|---|---|---|---|---|
| OpenAI Chat | — | Good | Fair | Fair |
| OpenAI Responses | Good | — | Fair | Fair |
| Claude Messages | Fair | Fair | — | Discouraged |
| Gemini | Fair | Fair | Discouraged | — |
质量等级表示协议之间的语义匹配程度:
Good:两种协议的核心结构较接近Fair:主要能力可转换,但部分协议特性可能需要适配或无法完整保留Discouraged:目前需要经过中间协议转换,语义损失风险更高
请求、非流式响应和流式响应均覆盖上述矩阵。实际采用的路径可从转换结果的 Steps 和 Quality 字段中读取。
LLMDock 要求 Go 1.25.1 或更高版本。
go get github.com/baseredge/llmdock@latest主要包:
| 包 | 用途 |
|---|---|
llmdock/dto |
各协议的请求、响应、流式事件和 usage DTO |
llmdock/types |
协议格式、错误、文件来源及共享类型 |
llmdock/relayconvert |
请求、响应和流式转换入口 |
llmdock/relayconvert/convmeta |
与宿主实现解耦的转换上下文和选项 |
llmdock/reasonmap |
不同协议之间的结束原因映射 |
下面将 OpenAI Chat Completions 请求转换为 Claude Messages 请求:
package main
import (
"context"
"fmt"
"github.com/baseredge/llmdock/dto"
"github.com/baseredge/llmdock/relayconvert"
"github.com/baseredge/llmdock/relayconvert/convmeta"
"github.com/baseredge/llmdock/types"
)
func main() {
maxTokens := uint(1024)
request := &dto.GeneralOpenAIRequest{
Model: "claude-sonnet-4-5",
Messages: []dto.Message{
{Role: "user", Content: "Hello!"},
},
MaxTokens: &maxTokens,
}
meta := &convmeta.Values{
OriginModelName: "client-model",
UpstreamModelName: request.Model,
ChannelMetaAttached: true,
}
result, err := relayconvert.ConvertRequest(
context.Background(),
meta,
types.RelayFormatClaude,
request,
)
if err != nil {
panic(err)
}
claudeRequest, ok := result.Value.(*dto.ClaudeRequest)
if !ok {
panic(fmt.Sprintf("unexpected result type %T", result.Value))
}
fmt.Printf("model=%s messages=%d\n", claudeRequest.Model, len(claudeRequest.Messages))
}ConvertRequest 根据请求的具体 DTO 类型推断源格式。传入原始 JSON、map[string]any 或不受支持的 DTO 会返回错误。
响应转换使用相同的目标格式模型:
result, err := relayconvert.ConvertResponse(
ctx,
meta,
types.RelayFormatOpenAI,
claudeResponse,
)
if err != nil {
return err
}
openAIResponse := result.Value.(*dto.OpenAITextResponse)
usage := result.Usage支持的响应 DTO:
| 格式 | 非流式响应 | 流式事件 |
|---|---|---|
| OpenAI Chat | dto.OpenAITextResponse |
dto.ChatCompletionsStreamResponse |
| OpenAI Responses | dto.OpenAIResponsesResponse |
dto.ResponsesStreamResponse |
| Claude Messages | dto.ClaudeResponse |
dto.ClaudeResponse |
| Gemini | dto.GeminiChatResponse |
dto.GeminiChatResponse |
流式转换可能需要跨事件保存工具调用、usage 和结束状态。每条上游流应创建独立的 ResponseStreamState,并在上游结束后调用 FinalizeStreamResponse:
state, err := relayconvert.NewResponseStreamState(
types.RelayFormatOpenAI,
types.RelayFormatOpenAIResponses,
relayconvert.ResponseStreamOptions{
ID: "resp_123",
Model: "gpt-4.1",
IncludeUsage: true,
},
)
if err != nil {
return err
}
for _, chunk := range upstreamChunks {
results, err := relayconvert.ConvertStreamResponseChunk(ctx, meta, state, chunk)
if err != nil {
return err
}
for _, result := range results {
emit(result.Value)
}
}
finalResults, err := relayconvert.FinalizeStreamResponse(ctx, meta, state)
if err != nil {
return err
}
for _, result := range finalResults {
emit(result.Value)
}
usage := state.Usage()LLMDock 不负责 SSE 的读取和写入。宿主需要将每个 SSE 事件解析为对应 DTO,并将转换结果重新编码后发送给下游。不要省略 FinalizeStreamResponse,部分转换器会在该阶段补发终止事件或最终 usage。
大多数基础转换可以传入 nil 作为 convmeta.Meta。需要模型映射、推理适配、安全设置或流式状态时,应使用 convmeta.Values,或在宿主中实现 convmeta.Meta。
常用选项通过 convmeta.Options 按请求传入:
meta := &convmeta.Values{
Options: &convmeta.Options{
Claude: convmeta.ClaudeOptions{
DefaultMaxTokens: func(model string) int {
return 4096
},
},
Gemini: convmeta.GeminiOptions{
ThinkingAdapterEnabled: true,
},
},
}需要注意:
- OpenAI Chat 或 OpenAI Responses 转 Claude 时,Claude 请求必须具有
max_tokens。源请求未提供时,需要配置Claude.DefaultMaxTokens,否则转换会返回错误。 - LLMDock 不负责选择渠道或映射模型名。调用转换前,应将请求中的
Model设置为目标上游使用的模型名。 - 自定义
convmeta.Meta的指针实现必须保证所有方法对 nil receiver 安全,完整约束见convmeta.Meta的接口注释。
某些跨协议的图片转换需要下载 URL 内容或解析 data URL。宿主应在启动时配置媒体解析器:
relayconvert.SetMediaResolver(relayconvert.MediaResolver{
GetBase64Data: getBase64Data,
DecodeBase64FileData: decodeBase64FileData,
})两个回调的签名由 relayconvert.MediaResolver 定义。需要媒体解析而未配置对应回调时,转换会明确返回错误;LLMDock 本身不会发起网络请求。
请求转换返回 relayconvert.RequestResult,响应转换返回 relayconvert.ResponseResult。除 Value 外,建议关注:
From/To:源格式和目标格式Converter:所选转换器 IDQuality:转换质量等级Steps:直接转换或多跳转换的实际路径Usage:响应转换后的统一 token usageStream:结果是否来自流式转换
如果需要固定转换路径,可使用 ConvertRequestVia;如果需要按转换器 ID 执行,可使用 ConvertRequestByID、ConvertResponseByID 和 NewResponseStreamStateByID。
LLMDock 内置了一个极简、零外部依赖的 WebUI 代理网关(内存占用仅 ~10MB),支持将任何上游大模型聚合为标准 OpenAI 兼容接口,并提供可视化配置后台:
# 启动 WebUI 代理后台
go run cmd/webui/main.go启动后访问:👉 http://127.0.0.1:3000
- 多渠道 × 多模型矩阵管理:支持挂载 Claude、DeepSeek、OpenAI、Gemini、Ollama 等多个上游渠道;
- 细粒度参数配置:独立调节每个模型的上下文容量(
200K/256K/512K/1M)、最大输出上限(64K/128K/32K/16K/8K); - 🧠 全规格思考强度矩阵 (Extended Thinking / Reasoning Effort):一键配置
🚫关闭、⚡动态自适应、🟢轻度(~2K)、🟢低(~4K)、🟡中(~8K)、🟣高(~16K-32K)、🔴满血极限(~64K)或精确自定义 Token 预算; - IDE & 编码工具无缝接入:所有启用的模型自动暴露在
http://127.0.0.1:3000/v1,可直接在 VS Code Continue、Cline、Roo Code、Cursor、Aider 中使用; - 实时测速与连通性检测:支持单模型与全量模型并发测速,毫秒级反馈延迟。
LLMDock 必须始终保持独立可构建。修改模块后,在 llmdock 目录运行:
go test ./...
go build ./cmd/webui转换矩阵由 golden tests 覆盖。确认协议输出变化是预期行为后,可更新快照:
go test ./relayconvert -run TestGolden -updateLLMDock 遵循 MIT License。