从源码深入理解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/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/8822.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[权限提升] 操作系统权限介绍

关注这个专栏的其他相关笔记:[内网安全] 内网渗透 - 学习手册-CSDN博客 权限提升简称提权,顾名思义就是提升自己在目标系统中的权限。现在的操作系统都是多用户操作系统,用户之间都有权限控制,我们通过 Web 漏洞拿到的 Web 进程的…

多模态论文笔记——ViViT

大家好,这里是好评笔记,公主号:Goodnote,专栏文章私信限时Free。本文详细解读多模态论文《ViViT: A Video Vision Transformer》,2021由google 提出用于视频处理的视觉 Transformer 模型,在视频多模态领域有…

【深度之眼cs231n第七期】笔记(三十一)

目录 强化学习什么是强化学习?马尔可夫决策过程(MDP)Q-learning策略梯度SOTA深度强化学习 还剩一点小尾巴,还是把它写完吧。(距离我写下前面那行字又过了好几个月了【咸鱼本鱼】)(汗颜&#xff…

[免费]基于Python的Django博客系统【论文+源码+SQL脚本】

大家好,我是java1234_小锋老师,看到一个不错的基于Python的Django博客系统,分享下哈。 项目视频演示 【免费】基于Python的Django博客系统 Python毕业设计_哔哩哔哩_bilibili 项目介绍 随着互联网技术的飞速发展,信息的传播与…

【Docker】Docker入门了解

文章目录 Docker 的核心概念Docker 常用命令示例:构建一个简单的 C 应用容器1. 创建 C 应用2. 创建 Dockerfile3. 构建镜像4. 运行容器 Docker 优势学习 Docker 的下一步 **一、Docker 是什么?****为什么 C 开发者需要 Docker?** **二、核心概…

如何跨互联网adb连接到远程手机-蓝牙电话集中维护

如何跨互联网adb连接到远程手机-蓝牙电话集中维护 --ADB连接专题 一、前言 随便找一个手机,安装一个App并简单设置一下,就可以跨互联网的ADB连接到这个手机,从而远程操控这个手机做各种操作。你敢相信吗?而这正是本篇想要描述的…

基于java线程池和EasyExcel实现数据异步导入

基于java线程池和EasyExcel实现数据异步导入 2.代码实现 2.1 controller层 PostMapping("import")public void importExcel(MultipartFile file) throws IOException {importService.importExcelAsync(file);}2.2 service层 Resource private SalariesListener sa…

linux asio网络编程理论及实现

最近在B站看了恋恋风辰大佬的asio网络编程,质量非常高。在本章中将对ASIO异步网络编程的整体及一些实现细节进行完整的梳理,用于复习与分享。大佬的博客:恋恋风辰官方博客 Preactor/Reactor模式 在网络编程中,通常根据事件处理的触…

Python爬虫学习第三弹 —— Xpath 页面解析 实现无广百·度

早上好啊,大佬们。上回使用 Beautiful Soup 进行页面解析的内容是不是已经理解得十分透彻了~ 这回我们再来尝试使用另外一种页面解析,来重构上一期里写的那些代码。 讲完Xpath之后,小白兔会带大家解决上期里百度搜索的代码编写,保…

docker安装MySQL8:docker离线安装MySQL、docker在线安装MySQL、MySQL镜像下载、MySQL配置、MySQL命令

一、镜像下载 1、在线下载 在一台能连外网的linux上执行docker镜像拉取命令 docker pull mysql:8.0.41 2、离线包下载 两种方式: 方式一: -)在一台能连外网的linux上安装docker执行第一步的命令下载镜像 -)导出 # 导出镜…

特权模式docker逃逸

目录 1.环境 2.上线哥斯拉 3.特权模式逃逸 1.判断是否为docker环境 2.判断是否为特权模式 3.挂载宿主机磁盘到docker 4.计划任务反弹shell 1.环境 ubuntu部署一个存在CVE-2017-12615的docker: (ip:192.168.117.147) kali(ip:192.168.117.128) 哥斯拉 2.上线哥斯拉…

Direct2D 极速教程(1) —— 画图形

极速导航 Direct2D 简介创建新项目:001-DrawGraphics弄一个白窗口在窗口上画图 Direct2D 简介 大家在学 WINAPI 的时候的时候有没有想过,怎么在一副窗口上画图呢?大家知道 Windows 系统是 GUI 图形用户界面 系统,以 Graphics 图形…

(长期更新)《零基础入门 ArcGIS(ArcScene) 》实验七----城市三维建模与分析(超超超详细!!!)

城市三维建模与分析 三维城市模型已经成为一种非常普遍的地理空间数据资源,成为城市的必需品,对城市能化管理至关重要。语义信息丰富的三维城市模型可以有效实现不同领域数据与IS相信息的高层次集成及互操作,从而在城市规划、环境模拟、应急响应和辅助决策等众多领域公挥作用、…

ArcGIS10.2 许可License点击始终启动无响应的解决办法及正常启动的前提

1、问题描述 在ArcGIS License Administrator中,手动点击“启动”无响应;且在计算机管理-服务中,无ArcGIS License 或者License的启动、停止、禁止等均为灰色,无法操作。 2、解决方法 ①通过cmd对service.txt进行手动服务的启动…

目标跟踪之sort算法(3)

这里写目录标题 1 流程1 预处理2 跟踪 2 代码 参考:sort代码 https://github.com/abewley/sort 1 流程 1 预处理 1.1 获取离线检测数据。1.2 实例化跟踪器。2 跟踪 2.1 轨迹处理。根据上一帧的轨迹预测当前帧的轨迹,剔除到当前轨迹中为空的轨迹得到当前…

物业巡更系统在现代社区管理中的优势与应用探讨

内容概要 在现代社区管理中,物业巡更系统正逐渐成为一种不可或缺的工具。结合先进的智能技术,这些系统能够有效地提升社区管理的各个方面,尤其是在巡检效率和信息透明度方面。通过实时记录巡检数据,物业管理人员能够确保工作人员…

深入探讨防抖函数中的 this 上下文

深入剖析防抖函数中的 this 上下文 最近我在研究防抖函数实现的时候,发现一个耗费脑子的问题,出现了令我困惑的问题。接下来,我将通过代码示例,深入探究这些现象背后的原理。 示例代码 function debounce(fn, delay) {let time…

进程通讯——类型和发展

进程常用交互方法如上

健康AI应用的逆袭:如何用“死亡时钟”撬动用户增长和媒体关注,实现应用榜快速排名第六

Death Clock:一款AI驱动的长寿应用 过去六个月里,我一直在为一款名为 Death Clock 的AI驱动长寿应用提供建议。健康类应用的增长向来十分困难,因为它们通常是单人使用的工具,且主要吸引年长的用户群体。然而,与创始人…

区块链在能源行业的应用场景

区块链技术在能源行业的应用正在逐步扩展,并且展现出巨大的潜力。它不仅能够促进能源交易的透明度和效率,还能为能源生产、分配、消费等多个环节提供创新解决方案。以下是对区块链在能源行业应用的一些深入探讨: 1. 能源交易 区块链可以实现…