RelayKit 是从 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 字段中读取。
RelayKit 要求 Go 1.25.1 或更高版本。
go get github.com/QuantumNous/new-api/relaykit@latest主要包:
| 包 | 用途 |
|---|---|
relaykit/dto |
各协议的请求、响应、流式事件和 usage DTO |
relaykit/types |
协议格式、错误、文件来源及共享类型 |
relaykit/relayconvert |
请求、响应和流式转换入口 |
relaykit/relayconvert/convmeta |
与宿主实现解耦的转换上下文和选项 |
relaykit/reasonmap |
不同协议之间的结束原因映射 |
下面将 OpenAI Chat Completions 请求转换为 Claude Messages 请求:
package main
import (
"context"
"fmt"
"github.com/QuantumNous/new-api/relaykit/dto"
"github.com/QuantumNous/new-api/relaykit/relayconvert"
"github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta"
"github.com/QuantumNous/new-api/relaykit/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()RelayKit 不负责 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,否则转换会返回错误。 - RelayKit 不负责选择渠道或映射模型名。调用转换前,应将请求中的
Model设置为目标上游使用的模型名。 - 自定义
convmeta.Meta的指针实现必须保证所有方法对 nil receiver 安全,完整约束见convmeta.Meta的接口注释。
某些跨协议的图片转换需要下载 URL 内容或解析 data URL。宿主应在启动时配置媒体解析器:
relayconvert.SetMediaResolver(relayconvert.MediaResolver{
GetBase64Data: getBase64Data,
DecodeBase64FileData: decodeBase64FileData,
})两个回调的签名由 relayconvert.MediaResolver 定义。需要媒体解析而未配置对应回调时,转换会明确返回错误;RelayKit 本身不会发起网络请求。
请求转换返回 relayconvert.RequestResult,响应转换返回 relayconvert.ResponseResult。除 Value 外,建议关注:
From/To:源格式和目标格式Converter:所选转换器 IDQuality:转换质量等级Steps:直接转换或多跳转换的实际路径Usage:响应转换后的统一 token usageStream:结果是否来自流式转换
如果需要固定转换路径,可使用 ConvertRequestVia;如果需要按转换器 ID 执行,可使用 ConvertRequestByID、ConvertResponseByID 和 NewResponseStreamStateByID。
RelayKit 必须始终保持独立可构建。修改模块后,在 relaykit 目录运行:
GOWORK=off go test ./...
GOWORK=off go build ./...转换矩阵由 golden tests 覆盖。确认协议输出变化是预期行为后,可更新快照:
GOWORK=off go test ./relayconvert -run TestGolden -updateRelayKit 当前使用 v0.x 版本。公开 API 和 DTO 仍可能在小版本中调整,升级前请检查发布说明和实际序列化结果。协议之间并非完全同构,建议对业务实际使用的工具调用、多模态、推理和流式场景增加端到端测试。
RelayKit 是 new-api 项目的一部分,遵循项目根目录中的 GNU Affero General Public License v3.0。