Skip to content

Navigation Menu

Sign in
Sign up

Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

LLMDock

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:目前需要经过中间协议转换,语义损失风险更高

请求、非流式响应和流式响应均覆盖上述矩阵。实际采用的路径可从转换结果的 StepsQuality 字段中读取。

安装

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:所选转换器 ID
  • Quality:转换质量等级
  • Steps:直接转换或多跳转换的实际路径
  • Usage:响应转换后的统一 token usage
  • Stream:结果是否来自流式转换

如果需要固定转换路径,可使用 ConvertRequestVia;如果需要按转换器 ID 执行,可使用 ConvertRequestByIDConvertResponseByIDNewResponseStreamStateByID

⚡ WebUI 智能多渠道与多模型网关

LLMDock 内置了一个极简、零外部依赖的 WebUI 代理网关(内存占用仅 ~10MB),支持将任何上游大模型聚合为标准 OpenAI 兼容接口,并提供可视化配置后台:

快速启动

# 启动 WebUI 代理后台
go run cmd/webui/main.go

启动后访问:👉 http://127.0.0.1:3000

网关核心特性

  1. 多渠道 ×ばつ 多模型矩阵管理:支持挂载 Claude、DeepSeek、OpenAI、Gemini、Ollama 等多个上游渠道;
  2. 细粒度参数配置:独立调节每个模型的上下文容量(200K / 256K / 512K / 1M)、最大输出上限(64K / 128K / 32K / 16K / 8K);
  3. 🧠 全规格思考强度矩阵 (Extended Thinking / Reasoning Effort):一键配置 🚫关闭⚡动态自适应🟢轻度(~2K)🟢低(~4K)🟡中(~8K)🟣高(~16K-32K)🔴满血极限(~64K) 或精确自定义 Token 预算;
  4. IDE & 编码工具无缝接入:所有启用的模型自动暴露在 http://127.0.0.1:3000/v1,可直接在 VS Code Continue、Cline、Roo Code、Cursor、Aider 中使用;
  5. 实时测速与连通性检测:支持单模型与全量模型并发测速,毫秒级反馈延迟。

开发

LLMDock 必须始终保持独立可构建。修改模块后,在 llmdock 目录运行:

go test ./...
go build ./cmd/webui

转换矩阵由 golden tests 覆盖。确认协议输出变化是预期行为后,可更新快照:

go test ./relayconvert -run TestGolden -update

许可证

LLMDock 遵循 MIT License

About

⚓ Ultra-lightweight universal LLM gateway, protocol converter & multi-model expansion dock in Pure Go

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

AltStyle によって変換されたページ (->オリジナル) /