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/