mirror of
https://github.com/songquanpeng/one-api.git
synced 2025-11-13 20:03:44 +08:00
Refactor codebase, introduce relaymode package, update constants and improve consistency
- Refactor constant definitions and organization - Clean up package level variables and functions - Introduce new `relaymode` and `apitype` packages for constant definitions - Refactor and simplify code in several packages including `openai`, `relay/channel/baidu`, `relay/util`, `relay/controller`, `relay/channeltype` - Add helper functions in `relay/channeltype` package to convert channel type constants to corresponding API type constants - Remove deprecated functions such as `ResponseText2Usage` from `relay/channel/openai/helper.go` - Modify code in `relay/util/validation.go` and related files to use new `validator.ValidateTextRequest` function - Rename `util` package to `relaymode` and update related imports in several packages
This commit is contained in:
70
relay/adaptor/xunfei/adaptor.go
Normal file
70
relay/adaptor/xunfei/adaptor.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package xunfei
|
||||
|
||||
// import (
|
||||
// "github.com/Laisky/errors/v2"
|
||||
// "github.com/gin-gonic/gin"
|
||||
// "github.com/songquanpeng/one-api/relay/channel"
|
||||
// "github.com/songquanpeng/one-api/relay/channel/openai"
|
||||
// "github.com/songquanpeng/one-api/relay/model"
|
||||
// "github.com/songquanpeng/one-api/relay/util"
|
||||
// "io"
|
||||
// "net/http"
|
||||
// "strings"
|
||||
// )
|
||||
|
||||
// type Adaptor struct {
|
||||
// request *model.GeneralOpenAIRequest
|
||||
// }
|
||||
|
||||
// func (a *Adaptor) Init(meta *util.RelayMeta) {
|
||||
|
||||
// }
|
||||
|
||||
// func (a *Adaptor) GetRequestURL(meta *util.RelayMeta) (string, error) {
|
||||
// return "", nil
|
||||
// }
|
||||
|
||||
// func (a *Adaptor) SetupRequestHeader(c *gin.Context, req *http.Request, meta *util.RelayMeta) error {
|
||||
// channel.SetupCommonRequestHeader(c, req, meta)
|
||||
// // check DoResponse for auth part
|
||||
// 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")
|
||||
// }
|
||||
// a.request = request
|
||||
// return nil, nil
|
||||
// }
|
||||
|
||||
// func (a *Adaptor) DoRequest(c *gin.Context, meta *util.RelayMeta, requestBody io.Reader) (*http.Response, error) {
|
||||
// // xunfei's request is not http request, so we don't need to do anything here
|
||||
// dummyResp := &http.Response{}
|
||||
// dummyResp.StatusCode = http.StatusOK
|
||||
// return dummyResp, nil
|
||||
// }
|
||||
|
||||
// func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, meta *util.RelayMeta) (usage *model.Usage, err *model.ErrorWithStatusCode) {
|
||||
// splits := strings.Split(meta.APIKey, "|")
|
||||
// if len(splits) != 3 {
|
||||
// return nil, openai.ErrorWrapper(errors.New("invalid auth"), "invalid_auth", http.StatusBadRequest)
|
||||
// }
|
||||
// if a.request == nil {
|
||||
// return nil, openai.ErrorWrapper(errors.New("request is nil"), "request_is_nil", http.StatusBadRequest)
|
||||
// }
|
||||
// if meta.IsStream {
|
||||
// err, usage = StreamHandler(c, *a.request, splits[0], splits[1], splits[2])
|
||||
// } else {
|
||||
// err, usage = Handler(c, *a.request, splits[0], splits[1], splits[2])
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
// func (a *Adaptor) GetModelList() []string {
|
||||
// return ModelList
|
||||
// }
|
||||
|
||||
// func (a *Adaptor) GetChannelName() string {
|
||||
// return "xunfei"
|
||||
// }
|
||||
9
relay/adaptor/xunfei/constants.go
Normal file
9
relay/adaptor/xunfei/constants.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package xunfei
|
||||
|
||||
var ModelList = []string{
|
||||
"SparkDesk",
|
||||
"SparkDesk-v1.1",
|
||||
"SparkDesk-v2.1",
|
||||
"SparkDesk-v3.1",
|
||||
"SparkDesk-v3.5",
|
||||
}
|
||||
306
relay/adaptor/xunfei/main.go
Normal file
306
relay/adaptor/xunfei/main.go
Normal file
@@ -0,0 +1,306 @@
|
||||
package xunfei
|
||||
|
||||
// import (
|
||||
// "crypto/hmac"
|
||||
// "crypto/sha256"
|
||||
// "encoding/base64"
|
||||
// "encoding/json"
|
||||
// "fmt"
|
||||
// "github.com/gin-gonic/gin"
|
||||
// "github.com/gorilla/websocket"
|
||||
// "io"
|
||||
// "net/http"
|
||||
// "net/url"
|
||||
// "one-api/common"
|
||||
// "strings"
|
||||
// "time"
|
||||
// )
|
||||
|
||||
// // https://console.xfyun.cn/services/cbm
|
||||
// // https://www.xfyun.cn/doc/spark/Web.html
|
||||
|
||||
// type XunfeiMessage struct {
|
||||
// Role string `json:"role"`
|
||||
// Content string `json:"content"`
|
||||
// }
|
||||
|
||||
// type XunfeiChatRequest struct {
|
||||
// Header struct {
|
||||
// AppId string `json:"app_id"`
|
||||
// } `json:"header"`
|
||||
// Parameter struct {
|
||||
// Chat struct {
|
||||
// Domain string `json:"domain,omitempty"`
|
||||
// Temperature float64 `json:"temperature,omitempty"`
|
||||
// TopK int `json:"top_k,omitempty"`
|
||||
// MaxTokens int `json:"max_tokens,omitempty"`
|
||||
// Auditing bool `json:"auditing,omitempty"`
|
||||
// } `json:"chat"`
|
||||
// } `json:"parameter"`
|
||||
// Payload struct {
|
||||
// Message struct {
|
||||
// Text []XunfeiMessage `json:"text"`
|
||||
// } `json:"message"`
|
||||
// } `json:"payload"`
|
||||
// }
|
||||
|
||||
// type XunfeiChatResponseTextItem struct {
|
||||
// Content string `json:"content"`
|
||||
// Role string `json:"role"`
|
||||
// Index int `json:"index"`
|
||||
// }
|
||||
|
||||
// type XunfeiChatResponse struct {
|
||||
// Header struct {
|
||||
// Code int `json:"code"`
|
||||
// Message string `json:"message"`
|
||||
// Sid string `json:"sid"`
|
||||
// Status int `json:"status"`
|
||||
// } `json:"header"`
|
||||
// Payload struct {
|
||||
// Choices struct {
|
||||
// Status int `json:"status"`
|
||||
// Seq int `json:"seq"`
|
||||
// Text []XunfeiChatResponseTextItem `json:"text"`
|
||||
// } `json:"choices"`
|
||||
// Usage struct {
|
||||
// //Text struct {
|
||||
// // QuestionTokens string `json:"question_tokens"`
|
||||
// // PromptTokens string `json:"prompt_tokens"`
|
||||
// // CompletionTokens string `json:"completion_tokens"`
|
||||
// // TotalTokens string `json:"total_tokens"`
|
||||
// //} `json:"text"`
|
||||
// Text Usage `json:"text"`
|
||||
// } `json:"usage"`
|
||||
// } `json:"payload"`
|
||||
// }
|
||||
|
||||
// func requestOpenAI2Xunfei(request GeneralOpenAIRequest, xunfeiAppId string, domain string) *XunfeiChatRequest {
|
||||
// messages := make([]XunfeiMessage, 0, len(request.Messages))
|
||||
// for _, message := range request.Messages {
|
||||
// if message.Role == "system" {
|
||||
// messages = append(messages, XunfeiMessage{
|
||||
// Role: "user",
|
||||
// Content: message.Content,
|
||||
// })
|
||||
// messages = append(messages, XunfeiMessage{
|
||||
// Role: "assistant",
|
||||
// Content: "Okay",
|
||||
// })
|
||||
// } else {
|
||||
// messages = append(messages, XunfeiMessage{
|
||||
// Role: message.Role,
|
||||
// Content: message.Content,
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// xunfeiRequest := XunfeiChatRequest{}
|
||||
// xunfeiRequest.Header.AppId = xunfeiAppId
|
||||
// xunfeiRequest.Parameter.Chat.Domain = domain
|
||||
// xunfeiRequest.Parameter.Chat.Temperature = request.Temperature
|
||||
// xunfeiRequest.Parameter.Chat.TopK = request.N
|
||||
// xunfeiRequest.Parameter.Chat.MaxTokens = request.MaxTokens
|
||||
// xunfeiRequest.Payload.Message.Text = messages
|
||||
// return &xunfeiRequest
|
||||
// }
|
||||
|
||||
// func responseXunfei2OpenAI(response *XunfeiChatResponse) *OpenAITextResponse {
|
||||
// if len(response.Payload.Choices.Text) == 0 {
|
||||
// response.Payload.Choices.Text = []XunfeiChatResponseTextItem{
|
||||
// {
|
||||
// Content: "",
|
||||
// },
|
||||
// }
|
||||
// }
|
||||
// choice := OpenAITextResponseChoice{
|
||||
// Index: 0,
|
||||
// Message: Message{
|
||||
// Role: "assistant",
|
||||
// Content: response.Payload.Choices.Text[0].Content,
|
||||
// },
|
||||
// FinishReason: stopFinishReason,
|
||||
// }
|
||||
// fullTextResponse := OpenAITextResponse{
|
||||
// Object: "chat.completion",
|
||||
// Created: common.GetTimestamp(),
|
||||
// Choices: []OpenAITextResponseChoice{choice},
|
||||
// Usage: response.Payload.Usage.Text,
|
||||
// }
|
||||
// return &fullTextResponse
|
||||
// }
|
||||
|
||||
// func streamResponseXunfei2OpenAI(xunfeiResponse *XunfeiChatResponse) *ChatCompletionsStreamResponse {
|
||||
// if len(xunfeiResponse.Payload.Choices.Text) == 0 {
|
||||
// xunfeiResponse.Payload.Choices.Text = []XunfeiChatResponseTextItem{
|
||||
// {
|
||||
// Content: "",
|
||||
// },
|
||||
// }
|
||||
// }
|
||||
// var choice ChatCompletionsStreamResponseChoice
|
||||
// choice.Delta.Content = xunfeiResponse.Payload.Choices.Text[0].Content
|
||||
// if xunfeiResponse.Payload.Choices.Status == 2 {
|
||||
// choice.FinishReason = &stopFinishReason
|
||||
// }
|
||||
// response := ChatCompletionsStreamResponse{
|
||||
// Object: "chat.completion.chunk",
|
||||
// Created: common.GetTimestamp(),
|
||||
// Model: "SparkDesk",
|
||||
// Choices: []ChatCompletionsStreamResponseChoice{choice},
|
||||
// }
|
||||
// return &response
|
||||
// }
|
||||
|
||||
// func buildXunfeiAuthUrl(hostUrl string, apiKey, apiSecret string) string {
|
||||
// HmacWithShaToBase64 := func(algorithm, data, key string) string {
|
||||
// mac := hmac.New(sha256.New, []byte(key))
|
||||
// mac.Write([]byte(data))
|
||||
// encodeData := mac.Sum(nil)
|
||||
// return base64.StdEncoding.EncodeToString(encodeData)
|
||||
// }
|
||||
// ul, err := url.Parse(hostUrl)
|
||||
// if err != nil {
|
||||
// fmt.Println(err)
|
||||
// }
|
||||
// date := time.Now().UTC().Format(time.RFC1123)
|
||||
// signString := []string{"host: " + ul.Host, "date: " + date, "GET " + ul.Path + " HTTP/1.1"}
|
||||
// sign := strings.Join(signString, "\n")
|
||||
// sha := HmacWithShaToBase64("hmac-sha256", sign, apiSecret)
|
||||
// authUrl := fmt.Sprintf("hmac username=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", apiKey,
|
||||
// "hmac-sha256", "host date request-line", sha)
|
||||
// authorization := base64.StdEncoding.EncodeToString([]byte(authUrl))
|
||||
// v := url.Values{}
|
||||
// v.Add("host", ul.Host)
|
||||
// v.Add("date", date)
|
||||
// v.Add("authorization", authorization)
|
||||
// callUrl := hostUrl + "?" + v.Encode()
|
||||
// return callUrl
|
||||
// }
|
||||
|
||||
// func xunfeiStreamHandler(c *gin.Context, textRequest GeneralOpenAIRequest, appId string, apiSecret string, apiKey string) (*OpenAIErrorWithStatusCode, *Usage) {
|
||||
// domain, authUrl := getXunfeiAuthUrl(c, apiKey, apiSecret)
|
||||
// dataChan, stopChan, err := xunfeiMakeRequest(textRequest, domain, authUrl, appId)
|
||||
// if err != nil {
|
||||
// return errorWrapper(err, "make xunfei request err", http.StatusInternalServerError), nil
|
||||
// }
|
||||
// setEventStreamHeaders(c)
|
||||
// var usage Usage
|
||||
// c.Stream(func(w io.Writer) bool {
|
||||
// select {
|
||||
// case xunfeiResponse := <-dataChan:
|
||||
// usage.PromptTokens += xunfeiResponse.Payload.Usage.Text.PromptTokens
|
||||
// usage.CompletionTokens += xunfeiResponse.Payload.Usage.Text.CompletionTokens
|
||||
// usage.TotalTokens += xunfeiResponse.Payload.Usage.Text.TotalTokens
|
||||
// response := streamResponseXunfei2OpenAI(&xunfeiResponse)
|
||||
// jsonResponse, err := json.Marshal(response)
|
||||
// if err != nil {
|
||||
// common.SysError("error marshalling stream response: " + err.Error())
|
||||
// return true
|
||||
// }
|
||||
// c.Render(-1, common.CustomEvent{Data: "data: " + string(jsonResponse)})
|
||||
// return true
|
||||
// case <-stopChan:
|
||||
// c.Render(-1, common.CustomEvent{Data: "data: [DONE]"})
|
||||
// return false
|
||||
// }
|
||||
// })
|
||||
// return nil, &usage
|
||||
// }
|
||||
|
||||
// func xunfeiHandler(c *gin.Context, textRequest GeneralOpenAIRequest, appId string, apiSecret string, apiKey string) (*OpenAIErrorWithStatusCode, *Usage) {
|
||||
// domain, authUrl := getXunfeiAuthUrl(c, apiKey, apiSecret)
|
||||
// dataChan, stopChan, err := xunfeiMakeRequest(textRequest, domain, authUrl, appId)
|
||||
// if err != nil {
|
||||
// return errorWrapper(err, "make xunfei request err", http.StatusInternalServerError), nil
|
||||
// }
|
||||
// var usage Usage
|
||||
// var content string
|
||||
// var xunfeiResponse XunfeiChatResponse
|
||||
// stop := false
|
||||
// for !stop {
|
||||
// select {
|
||||
// case xunfeiResponse = <-dataChan:
|
||||
// if len(xunfeiResponse.Payload.Choices.Text) == 0 {
|
||||
// continue
|
||||
// }
|
||||
// content += xunfeiResponse.Payload.Choices.Text[0].Content
|
||||
// usage.PromptTokens += xunfeiResponse.Payload.Usage.Text.PromptTokens
|
||||
// usage.CompletionTokens += xunfeiResponse.Payload.Usage.Text.CompletionTokens
|
||||
// usage.TotalTokens += xunfeiResponse.Payload.Usage.Text.TotalTokens
|
||||
// case stop = <-stopChan:
|
||||
// }
|
||||
// }
|
||||
|
||||
// xunfeiResponse.Payload.Choices.Text[0].Content = content
|
||||
|
||||
// response := responseXunfei2OpenAI(&xunfeiResponse)
|
||||
// jsonResponse, err := json.Marshal(response)
|
||||
// if err != nil {
|
||||
// return errorWrapper(err, "marshal_response_body_failed", http.StatusInternalServerError), nil
|
||||
// }
|
||||
// c.Writer.Header().Set("Content-Type", "application/json")
|
||||
// _, _ = c.Writer.Write(jsonResponse)
|
||||
// return nil, &usage
|
||||
// }
|
||||
|
||||
// func xunfeiMakeRequest(textRequest GeneralOpenAIRequest, domain, authUrl, appId string) (chan XunfeiChatResponse, chan bool, error) {
|
||||
// d := websocket.Dialer{
|
||||
// HandshakeTimeout: 5 * time.Second,
|
||||
// }
|
||||
// conn, resp, err := d.Dial(authUrl, nil)
|
||||
// if err != nil || resp.StatusCode != 101 {
|
||||
// return nil, nil, err
|
||||
// }
|
||||
// data := requestOpenAI2Xunfei(textRequest, appId, domain)
|
||||
// err = conn.WriteJSON(data)
|
||||
// if err != nil {
|
||||
// return nil, nil, err
|
||||
// }
|
||||
|
||||
// dataChan := make(chan XunfeiChatResponse)
|
||||
// stopChan := make(chan bool)
|
||||
// go func() {
|
||||
// for {
|
||||
// _, msg, err := conn.ReadMessage()
|
||||
// if err != nil {
|
||||
// common.SysError("error reading stream response: " + err.Error())
|
||||
// break
|
||||
// }
|
||||
// var response XunfeiChatResponse
|
||||
// err = json.Unmarshal(msg, &response)
|
||||
// if err != nil {
|
||||
// common.SysError("error unmarshalling stream response: " + err.Error())
|
||||
// break
|
||||
// }
|
||||
// dataChan <- response
|
||||
// if response.Payload.Choices.Status == 2 {
|
||||
// err := conn.Close()
|
||||
// if err != nil {
|
||||
// common.SysError("error closing websocket connection: " + err.Error())
|
||||
// }
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// stopChan <- true
|
||||
// }()
|
||||
|
||||
// return dataChan, stopChan, nil
|
||||
// }
|
||||
|
||||
// func getXunfeiAuthUrl(c *gin.Context, apiKey string, apiSecret string) (string, string) {
|
||||
// query := c.Request.URL.Query()
|
||||
// apiVersion := query.Get("api-version")
|
||||
// if apiVersion == "" {
|
||||
// apiVersion = c.GetString("api_version")
|
||||
// }
|
||||
// if apiVersion == "" {
|
||||
// apiVersion = "v1.1"
|
||||
// common.SysLog("api_version not found, use default: " + apiVersion)
|
||||
// }
|
||||
// domain := "general"
|
||||
// if apiVersion != "v1.1" {
|
||||
// domain += strings.Split(apiVersion, ".")[0]
|
||||
// }
|
||||
// authUrl := buildXunfeiAuthUrl(fmt.Sprintf("wss://spark-api.xf-yun.com/%s/chat", apiVersion), apiKey, apiSecret)
|
||||
// return domain, authUrl
|
||||
// }
|
||||
61
relay/adaptor/xunfei/model.go
Normal file
61
relay/adaptor/xunfei/model.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package xunfei
|
||||
|
||||
// import (
|
||||
// "github.com/songquanpeng/one-api/relay/model"
|
||||
// )
|
||||
|
||||
// type Message struct {
|
||||
// Role string `json:"role"`
|
||||
// Content string `json:"content"`
|
||||
// }
|
||||
|
||||
// type ChatRequest struct {
|
||||
// Header struct {
|
||||
// AppId string `json:"app_id"`
|
||||
// } `json:"header"`
|
||||
// Parameter struct {
|
||||
// Chat struct {
|
||||
// Domain string `json:"domain,omitempty"`
|
||||
// Temperature float64 `json:"temperature,omitempty"`
|
||||
// TopK int `json:"top_k,omitempty"`
|
||||
// MaxTokens int `json:"max_tokens,omitempty"`
|
||||
// Auditing bool `json:"auditing,omitempty"`
|
||||
// } `json:"chat"`
|
||||
// } `json:"parameter"`
|
||||
// Payload struct {
|
||||
// Message struct {
|
||||
// Text []Message `json:"text"`
|
||||
// } `json:"message"`
|
||||
// } `json:"payload"`
|
||||
// }
|
||||
|
||||
// type ChatResponseTextItem struct {
|
||||
// Content string `json:"content"`
|
||||
// Role string `json:"role"`
|
||||
// Index int `json:"index"`
|
||||
// }
|
||||
|
||||
// type ChatResponse struct {
|
||||
// Header struct {
|
||||
// Code int `json:"code"`
|
||||
// Message string `json:"message"`
|
||||
// Sid string `json:"sid"`
|
||||
// Status int `json:"status"`
|
||||
// } `json:"header"`
|
||||
// Payload struct {
|
||||
// Choices struct {
|
||||
// Status int `json:"status"`
|
||||
// Seq int `json:"seq"`
|
||||
// Text []ChatResponseTextItem `json:"text"`
|
||||
// } `json:"choices"`
|
||||
// Usage struct {
|
||||
// //Text struct {
|
||||
// // QuestionTokens string `json:"question_tokens"`
|
||||
// // PromptTokens string `json:"prompt_tokens"`
|
||||
// // CompletionTokens string `json:"completion_tokens"`
|
||||
// // TotalTokens string `json:"total_tokens"`
|
||||
// //} `json:"text"`
|
||||
// Text model.Usage `json:"text"`
|
||||
// } `json:"usage"`
|
||||
// } `json:"payload"`
|
||||
// }
|
||||
Reference in New Issue
Block a user