Skip to content

Latest commit

 

History

History
 
 

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

RelayKit

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

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

安装

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

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

开发

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

GOWORK=off go test ./...
GOWORK=off go build ./...

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

GOWORK=off go test ./relayconvert -run TestGolden -update

版本与兼容性

RelayKit 当前使用 v0.x 版本。公开 API 和 DTO 仍可能在小版本中调整,升级前请检查发布说明和实际序列化结果。协议之间并非完全同构,建议对业务实际使用的工具调用、多模态、推理和流式场景增加端到端测试。

许可证

RelayKit 是 new-api 项目的一部分,遵循项目根目录中的 GNU Affero General Public License v3.0