欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 八卦 > 从源码深入理解One-API框架:适配器模式实现LLM接口对接

从源码深入理解One-API框架:适配器模式实现LLM接口对接

2025/2/6 0:38:57 来源:https://blog.csdn.net/xu2439645715/article/details/145341879  浏览:    关键词:从源码深入理解One-API框架:适配器模式实现LLM接口对接

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/

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com