Merge branch 'alpha' into refactor/model-pricing
This commit is contained in:
@@ -56,7 +56,7 @@ func relayHandler(c *gin.Context, relayMode int) *types.NewAPIError {
|
|||||||
userGroup := c.GetString("group")
|
userGroup := c.GetString("group")
|
||||||
channelId := c.GetInt("channel_id")
|
channelId := c.GetInt("channel_id")
|
||||||
other := make(map[string]interface{})
|
other := make(map[string]interface{})
|
||||||
other["error_type"] = err.ErrorType
|
other["error_type"] = err.GetErrorType()
|
||||||
other["error_code"] = err.GetErrorCode()
|
other["error_code"] = err.GetErrorCode()
|
||||||
other["status_code"] = err.StatusCode
|
other["status_code"] = err.StatusCode
|
||||||
other["channel_id"] = channelId
|
other["channel_id"] = channelId
|
||||||
@@ -259,10 +259,10 @@ func getChannel(c *gin.Context, group, originalModel string, retryCount int) (*m
|
|||||||
}
|
}
|
||||||
channel, selectGroup, err := model.CacheGetRandomSatisfiedChannel(c, group, originalModel, retryCount)
|
channel, selectGroup, err := model.CacheGetRandomSatisfiedChannel(c, group, originalModel, retryCount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if group == "auto" {
|
return nil, types.NewError(errors.New(fmt.Sprintf("获取分组 %s 下模型 %s 的可用渠道失败(retry): %s", selectGroup, originalModel, err.Error())), types.ErrorCodeGetChannelFailed)
|
||||||
return nil, types.NewError(errors.New(fmt.Sprintf("获取自动分组下模型 %s 的可用渠道失败: %s", originalModel, err.Error())), types.ErrorCodeGetChannelFailed)
|
}
|
||||||
}
|
if channel == nil {
|
||||||
return nil, types.NewError(errors.New(fmt.Sprintf("获取分组 %s 下模型 %s 的可用渠道失败: %s", selectGroup, originalModel, err.Error())), types.ErrorCodeGetChannelFailed)
|
return nil, types.NewError(errors.New(fmt.Sprintf("分组 %s 下模型 %s 的可用渠道不存在(数据库一致性已被破坏,retry)", selectGroup, originalModel)), types.ErrorCodeGetChannelFailed)
|
||||||
}
|
}
|
||||||
newAPIError := middleware.SetupContextForSelectedChannel(c, channel, originalModel)
|
newAPIError := middleware.SetupContextForSelectedChannel(c, channel, originalModel)
|
||||||
if newAPIError != nil {
|
if newAPIError != nil {
|
||||||
|
|||||||
@@ -100,6 +100,10 @@ func Distribute() func(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if shouldSelectChannel {
|
if shouldSelectChannel {
|
||||||
|
if modelRequest.Model == "" {
|
||||||
|
abortWithOpenAiMessage(c, http.StatusBadRequest, "未指定模型名称,模型名称不能为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
var selectGroup string
|
var selectGroup string
|
||||||
channel, selectGroup, err = model.CacheGetRandomSatisfiedChannel(c, userGroup, modelRequest.Model, 0)
|
channel, selectGroup, err = model.CacheGetRandomSatisfiedChannel(c, userGroup, modelRequest.Model, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -107,7 +111,7 @@ func Distribute() func(c *gin.Context) {
|
|||||||
if userGroup == "auto" {
|
if userGroup == "auto" {
|
||||||
showGroup = fmt.Sprintf("auto(%s)", selectGroup)
|
showGroup = fmt.Sprintf("auto(%s)", selectGroup)
|
||||||
}
|
}
|
||||||
message := fmt.Sprintf("当前分组 %s 下对于模型 %s 无可用渠道", showGroup, modelRequest.Model)
|
message := fmt.Sprintf("获取分组 %s 下模型 %s 的可用渠道失败(distributor): %s", showGroup, modelRequest.Model, err.Error())
|
||||||
// 如果错误,但是渠道不为空,说明是数据库一致性问题
|
// 如果错误,但是渠道不为空,说明是数据库一致性问题
|
||||||
if channel != nil {
|
if channel != nil {
|
||||||
common.SysError(fmt.Sprintf("渠道不存在:%d", channel.Id))
|
common.SysError(fmt.Sprintf("渠道不存在:%d", channel.Id))
|
||||||
@@ -118,7 +122,7 @@ func Distribute() func(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if channel == nil {
|
if channel == nil {
|
||||||
abortWithOpenAiMessage(c, http.StatusServiceUnavailable, fmt.Sprintf("当前分组 %s 下对于模型 %s 无可用渠道(数据库一致性已被破坏)", userGroup, modelRequest.Model))
|
abortWithOpenAiMessage(c, http.StatusServiceUnavailable, fmt.Sprintf("分组 %s 下模型 %s 的可用渠道不存在(数据库一致性已被破坏,distributor)", userGroup, modelRequest.Model))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,9 +109,6 @@ func CacheGetRandomSatisfiedChannel(c *gin.Context, group string, model string,
|
|||||||
return nil, group, err
|
return nil, group, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if channel == nil {
|
|
||||||
return nil, group, errors.New("channel not found")
|
|
||||||
}
|
|
||||||
return channel, selectGroup, nil
|
return channel, selectGroup, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,16 @@ import (
|
|||||||
type Adaptor struct {
|
type Adaptor struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Adaptor) ConvertClaudeRequest(*gin.Context, *relaycommon.RelayInfo, *dto.ClaudeRequest) (any, error) {
|
func (a *Adaptor) ConvertClaudeRequest(c *gin.Context, info *relaycommon.RelayInfo, request *dto.ClaudeRequest) (any, error) {
|
||||||
//TODO implement me
|
openaiAdaptor := openai.Adaptor{}
|
||||||
panic("implement me")
|
openaiRequest, err := openaiAdaptor.ConvertClaudeRequest(c, info, request)
|
||||||
return nil, nil
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
openaiRequest.(*dto.GeneralOpenAIRequest).StreamOptions = &dto.StreamOptions{
|
||||||
|
IncludeUsage: true,
|
||||||
|
}
|
||||||
|
return requestOpenAI2Ollama(openaiRequest.(*dto.GeneralOpenAIRequest))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Adaptor) ConvertAudioRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.AudioRequest) (io.Reader, error) {
|
func (a *Adaptor) ConvertAudioRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.AudioRequest) (io.Reader, error) {
|
||||||
@@ -37,6 +43,9 @@ func (a *Adaptor) Init(info *relaycommon.RelayInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) {
|
func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) {
|
||||||
|
if info.RelayFormat == relaycommon.RelayFormatClaude {
|
||||||
|
return info.BaseUrl + "/v1/chat/completions", nil
|
||||||
|
}
|
||||||
switch info.RelayMode {
|
switch info.RelayMode {
|
||||||
case relayconstant.RelayModeEmbeddings:
|
case relayconstant.RelayModeEmbeddings:
|
||||||
return info.BaseUrl + "/api/embed", nil
|
return info.BaseUrl + "/api/embed", nil
|
||||||
@@ -55,7 +64,7 @@ func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayIn
|
|||||||
if request == nil {
|
if request == nil {
|
||||||
return nil, errors.New("request is nil")
|
return nil, errors.New("request is nil")
|
||||||
}
|
}
|
||||||
return requestOpenAI2Ollama(*request)
|
return requestOpenAI2Ollama(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Adaptor) ConvertRerankRequest(c *gin.Context, relayMode int, request dto.RerankRequest) (any, error) {
|
func (a *Adaptor) ConvertRerankRequest(c *gin.Context, relayMode int, request dto.RerankRequest) (any, error) {
|
||||||
@@ -76,11 +85,12 @@ func (a *Adaptor) DoRequest(c *gin.Context, info *relaycommon.RelayInfo, request
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (usage any, err *types.NewAPIError) {
|
func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (usage any, err *types.NewAPIError) {
|
||||||
if info.IsStream {
|
switch info.RelayMode {
|
||||||
usage, err = openai.OaiStreamHandler(c, info, resp)
|
case relayconstant.RelayModeEmbeddings:
|
||||||
} else {
|
usage, err = ollamaEmbeddingHandler(c, info, resp)
|
||||||
if info.RelayMode == relayconstant.RelayModeEmbeddings {
|
default:
|
||||||
usage, err = ollamaEmbeddingHandler(c, info, resp)
|
if info.IsStream {
|
||||||
|
usage, err = openai.OaiStreamHandler(c, info, resp)
|
||||||
} else {
|
} else {
|
||||||
usage, err = openai.OpenaiHandler(c, info, resp)
|
usage, err = openai.OpenaiHandler(c, info, resp)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func requestOpenAI2Ollama(request dto.GeneralOpenAIRequest) (*OllamaRequest, error) {
|
func requestOpenAI2Ollama(request *dto.GeneralOpenAIRequest) (*OllamaRequest, error) {
|
||||||
messages := make([]dto.Message, 0, len(request.Messages))
|
messages := make([]dto.Message, 0, len(request.Messages))
|
||||||
for _, message := range request.Messages {
|
for _, message := range request.Messages {
|
||||||
if !message.IsStringContent() {
|
if !message.IsStringContent() {
|
||||||
@@ -92,15 +92,15 @@ func ollamaEmbeddingHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *h
|
|||||||
var ollamaEmbeddingResponse OllamaEmbeddingResponse
|
var ollamaEmbeddingResponse OllamaEmbeddingResponse
|
||||||
responseBody, err := io.ReadAll(resp.Body)
|
responseBody, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, types.NewError(err, types.ErrorCodeBadResponseBody)
|
return nil, types.NewOpenAIError(err, types.ErrorCodeBadResponseBody, http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
common.CloseResponseBodyGracefully(resp)
|
common.CloseResponseBodyGracefully(resp)
|
||||||
err = common.Unmarshal(responseBody, &ollamaEmbeddingResponse)
|
err = common.Unmarshal(responseBody, &ollamaEmbeddingResponse)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, types.NewError(err, types.ErrorCodeBadResponseBody)
|
return nil, types.NewOpenAIError(err, types.ErrorCodeBadResponseBody, http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
if ollamaEmbeddingResponse.Error != "" {
|
if ollamaEmbeddingResponse.Error != "" {
|
||||||
return nil, types.NewError(fmt.Errorf("ollama error: %s", ollamaEmbeddingResponse.Error), types.ErrorCodeBadResponseBody)
|
return nil, types.NewOpenAIError(fmt.Errorf("ollama error: %s", ollamaEmbeddingResponse.Error), types.ErrorCodeBadResponseBody, http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
flattenedEmbeddings := flattenEmbeddings(ollamaEmbeddingResponse.Embedding)
|
flattenedEmbeddings := flattenEmbeddings(ollamaEmbeddingResponse.Embedding)
|
||||||
data := make([]dto.OpenAIEmbeddingResponseItem, 0, 1)
|
data := make([]dto.OpenAIEmbeddingResponseItem, 0, 1)
|
||||||
@@ -121,7 +121,7 @@ func ollamaEmbeddingHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *h
|
|||||||
}
|
}
|
||||||
doResponseBody, err := common.Marshal(embeddingResponse)
|
doResponseBody, err := common.Marshal(embeddingResponse)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, types.NewError(err, types.ErrorCodeBadResponseBody)
|
return nil, types.NewOpenAIError(err, types.ErrorCodeBadResponseBody, http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
common.IOCopyBytesGracefully(c, resp, doResponseBody)
|
common.IOCopyBytesGracefully(c, resp, doResponseBody)
|
||||||
return usage, nil
|
return usage, nil
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ func handleStreamFormat(c *gin.Context, info *relaycommon.RelayInfo, data string
|
|||||||
|
|
||||||
func handleClaudeFormat(c *gin.Context, data string, info *relaycommon.RelayInfo) error {
|
func handleClaudeFormat(c *gin.Context, data string, info *relaycommon.RelayInfo) error {
|
||||||
var streamResponse dto.ChatCompletionsStreamResponse
|
var streamResponse dto.ChatCompletionsStreamResponse
|
||||||
if err := json.Unmarshal(common.StringToByteSlice(data), &streamResponse); err != nil {
|
if err := common.Unmarshal(common.StringToByteSlice(data), &streamResponse); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,7 +174,7 @@ func handleFinalResponse(c *gin.Context, info *relaycommon.RelayInfo, lastStream
|
|||||||
case relaycommon.RelayFormatClaude:
|
case relaycommon.RelayFormatClaude:
|
||||||
info.ClaudeConvertInfo.Done = true
|
info.ClaudeConvertInfo.Done = true
|
||||||
var streamResponse dto.ChatCompletionsStreamResponse
|
var streamResponse dto.ChatCompletionsStreamResponse
|
||||||
if err := json.Unmarshal(common.StringToByteSlice(lastStreamData), &streamResponse); err != nil {
|
if err := common.Unmarshal(common.StringToByteSlice(lastStreamData), &streamResponse); err != nil {
|
||||||
common.SysError("error unmarshalling stream response: " + err.Error())
|
common.SysError("error unmarshalling stream response: " + err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -183,7 +183,7 @@ func handleFinalResponse(c *gin.Context, info *relaycommon.RelayInfo, lastStream
|
|||||||
|
|
||||||
claudeResponses := service.StreamResponseOpenAI2Claude(&streamResponse, info)
|
claudeResponses := service.StreamResponseOpenAI2Claude(&streamResponse, info)
|
||||||
for _, resp := range claudeResponses {
|
for _, resp := range claudeResponses {
|
||||||
helper.ClaudeData(c, *resp)
|
_ = helper.ClaudeData(c, *resp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,8 +145,10 @@ func OaiStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Re
|
|||||||
common.SysError("error handling stream format: " + err.Error())
|
common.SysError("error handling stream format: " + err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastStreamData = data
|
if len(data) > 0 {
|
||||||
streamItems = append(streamItems, data)
|
lastStreamData = data
|
||||||
|
streamItems = append(streamItems, data)
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -154,16 +156,18 @@ func OaiStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Re
|
|||||||
shouldSendLastResp := true
|
shouldSendLastResp := true
|
||||||
if err := handleLastResponse(lastStreamData, &responseId, &createAt, &systemFingerprint, &model, &usage,
|
if err := handleLastResponse(lastStreamData, &responseId, &createAt, &systemFingerprint, &model, &usage,
|
||||||
&containStreamUsage, info, &shouldSendLastResp); err != nil {
|
&containStreamUsage, info, &shouldSendLastResp); err != nil {
|
||||||
common.SysError("error handling last response: " + err.Error())
|
common.LogError(c, fmt.Sprintf("error handling last response: %s, lastStreamData: [%s]", err.Error(), lastStreamData))
|
||||||
}
|
}
|
||||||
|
|
||||||
if shouldSendLastResp && info.RelayFormat == relaycommon.RelayFormatOpenAI {
|
if info.RelayFormat == relaycommon.RelayFormatOpenAI {
|
||||||
_ = sendStreamData(c, info, lastStreamData, forceFormat, thinkToContent)
|
if shouldSendLastResp {
|
||||||
|
_ = sendStreamData(c, info, lastStreamData, forceFormat, thinkToContent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理token计算
|
// 处理token计算
|
||||||
if err := processTokens(info.RelayMode, streamItems, &responseTextBuilder, &toolCount); err != nil {
|
if err := processTokens(info.RelayMode, streamItems, &responseTextBuilder, &toolCount); err != nil {
|
||||||
common.SysError("error processing tokens: " + err.Error())
|
common.LogError(c, "error processing tokens: "+err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if !containStreamUsage {
|
if !containStreamUsage {
|
||||||
@@ -176,7 +180,6 @@ func OaiStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Re
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleFinalResponse(c, info, lastStreamData, responseId, createAt, model, systemFingerprint, usage, containStreamUsage)
|
handleFinalResponse(c, info, lastStreamData, responseId, createAt, model, systemFingerprint, usage, containStreamUsage)
|
||||||
|
|
||||||
return usage, nil
|
return usage, nil
|
||||||
|
|||||||
@@ -18,20 +18,19 @@ import (
|
|||||||
type Adaptor struct {
|
type Adaptor struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Adaptor) ConvertClaudeRequest(*gin.Context, *relaycommon.RelayInfo, *dto.ClaudeRequest) (any, error) {
|
func (a *Adaptor) ConvertClaudeRequest(c *gin.Context, info *relaycommon.RelayInfo, req *dto.ClaudeRequest) (any, error) {
|
||||||
//TODO implement me
|
adaptor := openai.Adaptor{}
|
||||||
panic("implement me")
|
return adaptor.ConvertClaudeRequest(c, info, req)
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Adaptor) ConvertAudioRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.AudioRequest) (io.Reader, error) {
|
func (a *Adaptor) ConvertAudioRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.AudioRequest) (io.Reader, error) {
|
||||||
//TODO implement me
|
//TODO implement me
|
||||||
return nil, errors.New("not implemented")
|
return nil, errors.New("not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Adaptor) ConvertImageRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.ImageRequest) (any, error) {
|
func (a *Adaptor) ConvertImageRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.ImageRequest) (any, error) {
|
||||||
//TODO implement me
|
adaptor := openai.Adaptor{}
|
||||||
return nil, errors.New("not implemented")
|
return adaptor.ConvertImageRequest(c, info, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Adaptor) Init(info *relaycommon.RelayInfo) {
|
func (a *Adaptor) Init(info *relaycommon.RelayInfo) {
|
||||||
@@ -47,7 +46,7 @@ func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) {
|
|||||||
} else if info.RelayMode == constant.RelayModeCompletions {
|
} else if info.RelayMode == constant.RelayModeCompletions {
|
||||||
return fmt.Sprintf("%s/v1/completions", info.BaseUrl), nil
|
return fmt.Sprintf("%s/v1/completions", info.BaseUrl), nil
|
||||||
}
|
}
|
||||||
return "", errors.New("invalid relay mode")
|
return fmt.Sprintf("%s/v1/chat/completions", info.BaseUrl), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Adaptor) SetupRequestHeader(c *gin.Context, req *http.Header, info *relaycommon.RelayInfo) error {
|
func (a *Adaptor) SetupRequestHeader(c *gin.Context, req *http.Header, info *relaycommon.RelayInfo) error {
|
||||||
@@ -81,16 +80,19 @@ func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycom
|
|||||||
switch info.RelayMode {
|
switch info.RelayMode {
|
||||||
case constant.RelayModeRerank:
|
case constant.RelayModeRerank:
|
||||||
usage, err = siliconflowRerankHandler(c, info, resp)
|
usage, err = siliconflowRerankHandler(c, info, resp)
|
||||||
|
case constant.RelayModeEmbeddings:
|
||||||
|
usage, err = openai.OpenaiHandler(c, info, resp)
|
||||||
case constant.RelayModeCompletions:
|
case constant.RelayModeCompletions:
|
||||||
fallthrough
|
fallthrough
|
||||||
case constant.RelayModeChatCompletions:
|
case constant.RelayModeChatCompletions:
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
if info.IsStream {
|
if info.IsStream {
|
||||||
usage, err = openai.OaiStreamHandler(c, info, resp)
|
usage, err = openai.OaiStreamHandler(c, info, resp)
|
||||||
} else {
|
} else {
|
||||||
usage, err = openai.OpenaiHandler(c, info, resp)
|
usage, err = openai.OpenaiHandler(c, info, resp)
|
||||||
}
|
}
|
||||||
case constant.RelayModeEmbeddings:
|
|
||||||
usage, err = openai.OpenaiHandler(c, info, resp)
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -234,6 +234,12 @@ func StreamScannerHandler(c *gin.Context, resp *http.Response, info *relaycommon
|
|||||||
case <-stopChan:
|
case <-stopChan:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// done, 处理完成标志,直接退出停止读取剩余数据防止出错
|
||||||
|
if common.DebugEnabled {
|
||||||
|
println("received [DONE], stopping scanner")
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -251,22 +251,54 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon
|
|||||||
resp.SetIndex(0)
|
resp.SetIndex(0)
|
||||||
claudeResponses = append(claudeResponses, resp)
|
claudeResponses = append(claudeResponses, resp)
|
||||||
} else {
|
} else {
|
||||||
//resp := &dto.ClaudeResponse{
|
|
||||||
// Type: "content_block_start",
|
}
|
||||||
// ContentBlock: &dto.ClaudeMediaMessage{
|
// 判断首个响应是否存在内容(非标准的 OpenAI 响应)
|
||||||
// Type: "text",
|
if len(openAIResponse.Choices) > 0 && len(openAIResponse.Choices[0].Delta.GetContentString()) > 0 {
|
||||||
// Text: common.GetPointer[string](""),
|
claudeResponses = append(claudeResponses, &dto.ClaudeResponse{
|
||||||
// },
|
Index: &info.ClaudeConvertInfo.Index,
|
||||||
//}
|
Type: "content_block_start",
|
||||||
//resp.SetIndex(0)
|
ContentBlock: &dto.ClaudeMediaMessage{
|
||||||
//claudeResponses = append(claudeResponses, resp)
|
Type: "text",
|
||||||
|
Text: common.GetPointer[string](""),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
claudeResponses = append(claudeResponses, &dto.ClaudeResponse{
|
||||||
|
Type: "content_block_delta",
|
||||||
|
Delta: &dto.ClaudeMediaMessage{
|
||||||
|
Type: "text",
|
||||||
|
Text: common.GetPointer[string](openAIResponse.Choices[0].Delta.GetContentString()),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
info.ClaudeConvertInfo.LastMessagesType = relaycommon.LastMessageTypeText
|
||||||
}
|
}
|
||||||
return claudeResponses
|
return claudeResponses
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(openAIResponse.Choices) == 0 {
|
if len(openAIResponse.Choices) == 0 {
|
||||||
// no choices
|
// no choices
|
||||||
// TODO: handle this case
|
// 可能为非标准的 OpenAI 响应,判断是否已经完成
|
||||||
|
if info.Done {
|
||||||
|
claudeResponses = append(claudeResponses, generateStopBlock(info.ClaudeConvertInfo.Index))
|
||||||
|
oaiUsage := info.ClaudeConvertInfo.Usage
|
||||||
|
if oaiUsage != nil {
|
||||||
|
claudeResponses = append(claudeResponses, &dto.ClaudeResponse{
|
||||||
|
Type: "message_delta",
|
||||||
|
Usage: &dto.ClaudeUsage{
|
||||||
|
InputTokens: oaiUsage.PromptTokens,
|
||||||
|
OutputTokens: oaiUsage.CompletionTokens,
|
||||||
|
CacheCreationInputTokens: oaiUsage.PromptTokensDetails.CachedCreationTokens,
|
||||||
|
CacheReadInputTokens: oaiUsage.PromptTokensDetails.CachedTokens,
|
||||||
|
},
|
||||||
|
Delta: &dto.ClaudeMediaMessage{
|
||||||
|
StopReason: common.GetPointer[string](stopReasonOpenAI2Claude(info.FinishReason)),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
claudeResponses = append(claudeResponses, &dto.ClaudeResponse{
|
||||||
|
Type: "message_stop",
|
||||||
|
})
|
||||||
|
}
|
||||||
return claudeResponses
|
return claudeResponses
|
||||||
} else {
|
} else {
|
||||||
chosenChoice := openAIResponse.Choices[0]
|
chosenChoice := openAIResponse.Choices[0]
|
||||||
|
|||||||
@@ -80,10 +80,7 @@ func ClaudeErrorWrapperLocal(err error, code string, statusCode int) *dto.Claude
|
|||||||
}
|
}
|
||||||
|
|
||||||
func RelayErrorHandler(resp *http.Response, showBodyWhenFail bool) (newApiErr *types.NewAPIError) {
|
func RelayErrorHandler(resp *http.Response, showBodyWhenFail bool) (newApiErr *types.NewAPIError) {
|
||||||
newApiErr = &types.NewAPIError{
|
newApiErr = types.InitOpenAIError(types.ErrorCodeBadResponseStatusCode, resp.StatusCode)
|
||||||
StatusCode: resp.StatusCode,
|
|
||||||
ErrorType: types.ErrorTypeOpenAIError,
|
|
||||||
}
|
|
||||||
|
|
||||||
responseBody, err := io.ReadAll(resp.Body)
|
responseBody, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -105,8 +102,7 @@ func RelayErrorHandler(resp *http.Response, showBodyWhenFail bool) (newApiErr *t
|
|||||||
// General format error (OpenAI, Anthropic, Gemini, etc.)
|
// General format error (OpenAI, Anthropic, Gemini, etc.)
|
||||||
newApiErr = types.WithOpenAIError(errResponse.Error, resp.StatusCode)
|
newApiErr = types.WithOpenAIError(errResponse.Error, resp.StatusCode)
|
||||||
} else {
|
} else {
|
||||||
newApiErr = types.NewErrorWithStatusCode(errors.New(errResponse.ToMessage()), types.ErrorCodeBadResponseStatusCode, resp.StatusCode)
|
newApiErr = types.NewOpenAIError(errors.New(errResponse.ToMessage()), types.ErrorCodeBadResponseStatusCode, resp.StatusCode)
|
||||||
newApiErr.ErrorType = types.ErrorTypeOpenAIError
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ const (
|
|||||||
type NewAPIError struct {
|
type NewAPIError struct {
|
||||||
Err error
|
Err error
|
||||||
RelayError any
|
RelayError any
|
||||||
ErrorType ErrorType
|
errorType ErrorType
|
||||||
errorCode ErrorCode
|
errorCode ErrorCode
|
||||||
StatusCode int
|
StatusCode int
|
||||||
}
|
}
|
||||||
@@ -87,6 +87,13 @@ func (e *NewAPIError) GetErrorCode() ErrorCode {
|
|||||||
return e.errorCode
|
return e.errorCode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *NewAPIError) GetErrorType() ErrorType {
|
||||||
|
if e == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return e.errorType
|
||||||
|
}
|
||||||
|
|
||||||
func (e *NewAPIError) Error() string {
|
func (e *NewAPIError) Error() string {
|
||||||
if e == nil {
|
if e == nil {
|
||||||
return ""
|
return ""
|
||||||
@@ -103,7 +110,7 @@ func (e *NewAPIError) SetMessage(message string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *NewAPIError) ToOpenAIError() OpenAIError {
|
func (e *NewAPIError) ToOpenAIError() OpenAIError {
|
||||||
switch e.ErrorType {
|
switch e.errorType {
|
||||||
case ErrorTypeOpenAIError:
|
case ErrorTypeOpenAIError:
|
||||||
if openAIError, ok := e.RelayError.(OpenAIError); ok {
|
if openAIError, ok := e.RelayError.(OpenAIError); ok {
|
||||||
return openAIError
|
return openAIError
|
||||||
@@ -120,14 +127,14 @@ func (e *NewAPIError) ToOpenAIError() OpenAIError {
|
|||||||
}
|
}
|
||||||
return OpenAIError{
|
return OpenAIError{
|
||||||
Message: e.Error(),
|
Message: e.Error(),
|
||||||
Type: string(e.ErrorType),
|
Type: string(e.errorType),
|
||||||
Param: "",
|
Param: "",
|
||||||
Code: e.errorCode,
|
Code: e.errorCode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *NewAPIError) ToClaudeError() ClaudeError {
|
func (e *NewAPIError) ToClaudeError() ClaudeError {
|
||||||
switch e.ErrorType {
|
switch e.errorType {
|
||||||
case ErrorTypeOpenAIError:
|
case ErrorTypeOpenAIError:
|
||||||
openAIError := e.RelayError.(OpenAIError)
|
openAIError := e.RelayError.(OpenAIError)
|
||||||
return ClaudeError{
|
return ClaudeError{
|
||||||
@@ -139,7 +146,7 @@ func (e *NewAPIError) ToClaudeError() ClaudeError {
|
|||||||
default:
|
default:
|
||||||
return ClaudeError{
|
return ClaudeError{
|
||||||
Message: e.Error(),
|
Message: e.Error(),
|
||||||
Type: string(e.ErrorType),
|
Type: string(e.errorType),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,7 +155,7 @@ func NewError(err error, errorCode ErrorCode) *NewAPIError {
|
|||||||
return &NewAPIError{
|
return &NewAPIError{
|
||||||
Err: err,
|
Err: err,
|
||||||
RelayError: nil,
|
RelayError: nil,
|
||||||
ErrorType: ErrorTypeNewAPIError,
|
errorType: ErrorTypeNewAPIError,
|
||||||
StatusCode: http.StatusInternalServerError,
|
StatusCode: http.StatusInternalServerError,
|
||||||
errorCode: errorCode,
|
errorCode: errorCode,
|
||||||
}
|
}
|
||||||
@@ -162,6 +169,13 @@ func NewOpenAIError(err error, errorCode ErrorCode, statusCode int) *NewAPIError
|
|||||||
return WithOpenAIError(openaiError, statusCode)
|
return WithOpenAIError(openaiError, statusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func InitOpenAIError(errorCode ErrorCode, statusCode int) *NewAPIError {
|
||||||
|
openaiError := OpenAIError{
|
||||||
|
Type: string(errorCode),
|
||||||
|
}
|
||||||
|
return WithOpenAIError(openaiError, statusCode)
|
||||||
|
}
|
||||||
|
|
||||||
func NewErrorWithStatusCode(err error, errorCode ErrorCode, statusCode int) *NewAPIError {
|
func NewErrorWithStatusCode(err error, errorCode ErrorCode, statusCode int) *NewAPIError {
|
||||||
return &NewAPIError{
|
return &NewAPIError{
|
||||||
Err: err,
|
Err: err,
|
||||||
@@ -169,7 +183,7 @@ func NewErrorWithStatusCode(err error, errorCode ErrorCode, statusCode int) *New
|
|||||||
Message: err.Error(),
|
Message: err.Error(),
|
||||||
Type: string(errorCode),
|
Type: string(errorCode),
|
||||||
},
|
},
|
||||||
ErrorType: ErrorTypeNewAPIError,
|
errorType: ErrorTypeNewAPIError,
|
||||||
StatusCode: statusCode,
|
StatusCode: statusCode,
|
||||||
errorCode: errorCode,
|
errorCode: errorCode,
|
||||||
}
|
}
|
||||||
@@ -182,7 +196,7 @@ func WithOpenAIError(openAIError OpenAIError, statusCode int) *NewAPIError {
|
|||||||
}
|
}
|
||||||
return &NewAPIError{
|
return &NewAPIError{
|
||||||
RelayError: openAIError,
|
RelayError: openAIError,
|
||||||
ErrorType: ErrorTypeOpenAIError,
|
errorType: ErrorTypeOpenAIError,
|
||||||
StatusCode: statusCode,
|
StatusCode: statusCode,
|
||||||
Err: errors.New(openAIError.Message),
|
Err: errors.New(openAIError.Message),
|
||||||
errorCode: ErrorCode(code),
|
errorCode: ErrorCode(code),
|
||||||
@@ -192,7 +206,7 @@ func WithOpenAIError(openAIError OpenAIError, statusCode int) *NewAPIError {
|
|||||||
func WithClaudeError(claudeError ClaudeError, statusCode int) *NewAPIError {
|
func WithClaudeError(claudeError ClaudeError, statusCode int) *NewAPIError {
|
||||||
return &NewAPIError{
|
return &NewAPIError{
|
||||||
RelayError: claudeError,
|
RelayError: claudeError,
|
||||||
ErrorType: ErrorTypeClaudeError,
|
errorType: ErrorTypeClaudeError,
|
||||||
StatusCode: statusCode,
|
StatusCode: statusCode,
|
||||||
Err: errors.New(claudeError.Message),
|
Err: errors.New(claudeError.Message),
|
||||||
errorCode: ErrorCode(claudeError.Type),
|
errorCode: ErrorCode(claudeError.Type),
|
||||||
@@ -211,5 +225,5 @@ func IsLocalError(err *NewAPIError) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return err.ErrorType == ErrorTypeNewAPIError
|
return err.errorType == ErrorTypeNewAPIError
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -704,20 +704,20 @@ const EditChannelModal = (props) => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>{t('批量创建')}</Checkbox>
|
>{t('批量创建')}</Checkbox>
|
||||||
{/*{batch && (*/}
|
{batch && (
|
||||||
{/* <Checkbox disabled={isEdit} checked={multiToSingle} onChange={() => {*/}
|
<Checkbox disabled={isEdit} checked={multiToSingle} onChange={() => {
|
||||||
{/* setMultiToSingle(prev => !prev);*/}
|
setMultiToSingle(prev => !prev);
|
||||||
{/* setInputs(prev => {*/}
|
setInputs(prev => {
|
||||||
{/* const newInputs = { ...prev };*/}
|
const newInputs = { ...prev };
|
||||||
{/* if (!multiToSingle) {*/}
|
if (!multiToSingle) {
|
||||||
{/* newInputs.multi_key_mode = multiKeyMode;*/}
|
newInputs.multi_key_mode = multiKeyMode;
|
||||||
{/* } else {*/}
|
} else {
|
||||||
{/* delete newInputs.multi_key_mode;*/}
|
delete newInputs.multi_key_mode;
|
||||||
{/* }*/}
|
}
|
||||||
{/* return newInputs;*/}
|
return newInputs;
|
||||||
{/* });*/}
|
});
|
||||||
{/* }}>{t('密钥聚合模式')}</Checkbox>*/}
|
}}>{t('密钥聚合模式')}</Checkbox>
|
||||||
{/*)}*/}
|
)}
|
||||||
</Space>
|
</Space>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user