1. 概述
one-api 是一个开源的 API 框架,基于go语言开发,旨在提供统一的接口调用封装,支持多种 AI 服务平台的集成。通过 Gin 和 GORM 等框架,框架简化了多种 API 服务的调用流程。通过适配器模式实现了与多种 大模型API 服务的集成,而无需每次都重新编写调用逻辑。使得开发者能够专注于业务逻辑,而不是各个平台间的差异化处理。
在本文中,将深入解读 one-api 框架的工作原理,详细讲解框架的结构与实现,并通过集成阿里灵积DashScope大模型服务 API 为例,展示其适配器实现。
2. 路由与控制器
在 one-api 中,所有的 API 请求都会通过 Gin 框架来处理。以下是一个典型的请求路由:
relayV1Router.POST("/chat/completions", controller.Relay)
和大模型交互相关的核心逻辑都集中在 controller.Relay函数中。
如上图是Relay函数的实现,主要完成三个事情:
(1)获取relayMode
(2)根据repayMode请求LLM接口
(3)异常处理及重试
3. GetByPath函数
GetByPath
函数会根据请求路径返回不同的 relayMode
,比如文本生成、图像生成或音频处理等。
4. relayHelper函数
根据获取到的 relayMode,框架会调用不同的处理方法。具体代码如下:
如果 relayMode
为文本请求,则调用 controller.RelayTextHelper(c);
如果是图像生成请求,则调用 RelayImageHelper
等。
5. RelayTextHelper函数
RelayTextHelper 是 one-api 框架中负责处理文本请求的核心函数,它包含了一系列的操作步骤来完成整个请求的处理流程。
- 从请求中获取并验证 textRequest
- 获取待调用的模型名称
- 设置
system prompt
- 获取请求的配额和使用限制
- 预消耗配额
- 根据
APIType
获取适配器adaptor
- 构建对应adaptor的请求体
- 使用对应的adaptor的向大模型发起请求
- 处理封装请求结果
- 最终消耗扣减配额
整个过程绝大多数的过程都是在做参数转换及参数设置,比较关键的一步是“构建实际的请求体”。使用适配器模式对各种大模型接口进行适配,根据不同的apiType
得到不同的适配器对象,如openai.Adaptor、ali.Adaptor、baidu.Adaptor、zhipu.Adaptor等。
注:
apiType
的获取方式为:
meta.APIType = channeltype.ToAPIType(meta.ChannelType)
,即与在one-api管理页面上配置的渠道类型一致,如果配置的是“阿里通义千问”,那么apiType就为apitype.Ali
。具体可参见源码中的relay/channeltype/helper.go
中的ToAPIType(channelType int) int
函数,在此不做赘述。
Adaptor是一个接口,定义了如下方法:
接入第三方LLM接口的时候只需要实现对应的适配器即可完成对接,截止发文当日one-api已经集成了近40家AI厂商的大模型接口,包括国内的baidu、ali、doubao、zhipu等。
6. 阿里灵积DashScope适配
以下是阿里灵积DashScope大模型服务的适配器实现:
package alitype Adaptor struct {meta *meta.Meta
}func (a *Adaptor) Init(meta *meta.Meta) {a.meta = meta
}func (a *Adaptor) GetRequestURL(meta *meta.Meta) (string, error) {fullRequestURL := ""switch meta.Mode {case relaymode.Embeddings:fullRequestURL = fmt.Sprintf("%s/api/v1/services/embeddings/text-embedding/text-embedding", meta.BaseURL)case relaymode.ImagesGenerations:fullRequestURL = fmt.Sprintf("%s/api/v1/services/aigc/text2image/image-synthesis", meta.BaseURL)default:fullRequestURL = fmt.Sprintf("%s/api/v1/services/aigc/text-generation/generation", meta.BaseURL)}return fullRequestURL, nil
}func (a *Adaptor) SetupRequestHeader(c *gin.Context, req *http.Request, meta *meta.Meta) error {adaptor.SetupCommonRequestHeader(c, req, meta)if meta.IsStream {req.Header.Set("Accept", "text/event-stream")req.Header.Set("X-DashScope-SSE", "enable")}req.Header.Set("Authorization", "Bearer "+meta.APIKey)if meta.Mode == relaymode.ImagesGenerations {req.Header.Set("X-DashScope-Async", "enable")}if a.meta.Config.Plugin != "" {req.Header.Set("X-DashScope-Plugin", a.meta.Config.Plugin)}return nil
}func (a *Adaptor) ConvertRequest(c *gin.Context, relayMode int, request *model.GeneralOpenAIRequest) (any, error) {if request == nil {return nil, errors.New("request is nil")}switch relayMode {case relaymode.Embeddings:aliEmbeddingRequest := ConvertEmbeddingRequest(*request)return aliEmbeddingRequest, nildefault:aliRequest := ConvertRequest(*request)return aliRequest, nil}
}func (a *Adaptor) ConvertImageRequest(request *model.ImageRequest) (any, error) {if request == nil {return nil, errors.New("request is nil")}aliRequest := ConvertImageRequest(*request)return aliRequest, nil
}func (a *Adaptor) DoRequest(c *gin.Context, meta *meta.Meta, requestBody io.Reader) (*http.Response, error) {return adaptor.DoRequestHelper(a, c, meta, requestBody)
}func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, meta *meta.Meta) (usage *model.Usage, err *model.ErrorWithStatusCode) {if meta.IsStream {err, usage = StreamHandler(c, resp)} else {switch meta.Mode {case relaymode.Embeddings:err, usage = EmbeddingHandler(c, resp)case relaymode.ImagesGenerations:err, usage = ImageHandler(c, resp)default:err, usage = Handler(c, resp)}}return
}func (a *Adaptor) GetModelList() []string {return ModelList
}func (a *Adaptor) GetChannelName() string {return "ali"
}
这段代码实现了 Adaptor 接口,适配了阿里云 AI 服务的请求和响应处理。通过一系列方法,Adaptor 能够完成以下任务:
- 根据请求模式生成 API 请求 URL。
- 设置 HTTP 请求头,包括授权信息、流式请求标识等。
- 将通用请求数据转换为阿里云特定的请求格式。
- 向阿里云服务发起请求,并处理响应。
- 获取支持的模型列表和通道名称。
- 它为 one-api 框架提供了对阿里云 AI 服务的无缝集成,简化了与阿里云接口交互的代码。
总结
综合上述内容,通过一张时序图来说明整个调用流程:
参考说明
one-api:https://github.com/songquanpeng/one-api
gorm: https://gorm.io/zh_CN/docs/index.html
gin: https://gin-gonic.com/zh-cn/docs/