Skip to content

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