Merge branch 'feature/messages2responses' into upstream-main

# Conflicts:
#	service/openaicompat/chat_to_responses.go
This commit is contained in:
Seefs
2026-02-08 00:16:35 +08:00
3 changed files with 79 additions and 19 deletions

View File

@@ -171,7 +171,9 @@ func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) {
url = strings.Replace(url, "{model}", info.UpstreamModelName, -1) url = strings.Replace(url, "{model}", info.UpstreamModelName, -1)
return url, nil return url, nil
default: default:
if info.RelayFormat == types.RelayFormatClaude || info.RelayFormat == types.RelayFormatGemini { if (info.RelayFormat == types.RelayFormatClaude || info.RelayFormat == types.RelayFormatGemini) &&
info.RelayMode != relayconstant.RelayModeResponses &&
info.RelayMode != relayconstant.RelayModeResponsesCompact {
return fmt.Sprintf("%s/v1/chat/completions", info.ChannelBaseUrl), nil return fmt.Sprintf("%s/v1/chat/completions", info.ChannelBaseUrl), nil
} }
return relaycommon.GetFullRequestURL(info.ChannelBaseUrl, info.RequestURLPath, info.ChannelType), nil return relaycommon.GetFullRequestURL(info.ChannelBaseUrl, info.RequestURLPath, info.ChannelType), nil

View File

@@ -71,12 +71,22 @@ func OaiResponsesToChatHandler(c *gin.Context, info *relaycommon.RelayInfo, resp
chatResp.Usage = *usage chatResp.Usage = *usage
} }
chatBody, err := common.Marshal(chatResp) var responseBody []byte
switch info.RelayFormat {
case types.RelayFormatClaude:
claudeResp := service.ResponseOpenAI2Claude(chatResp, info)
responseBody, err = common.Marshal(claudeResp)
case types.RelayFormatGemini:
geminiResp := service.ResponseOpenAI2Gemini(chatResp, info)
responseBody, err = common.Marshal(geminiResp)
default:
responseBody, err = common.Marshal(chatResp)
}
if err != nil { if err != nil {
return nil, types.NewOpenAIError(err, types.ErrorCodeJsonMarshalFailed, http.StatusInternalServerError) return nil, types.NewOpenAIError(err, types.ErrorCodeJsonMarshalFailed, http.StatusInternalServerError)
} }
service.IOCopyBytesGracefully(c, resp, chatBody) service.IOCopyBytesGracefully(c, resp, responseBody)
return usage, nil return usage, nil
} }
@@ -110,12 +120,39 @@ func OaiResponsesToChatStreamHandler(c *gin.Context, info *relaycommon.RelayInfo
needsReasoningSummarySeparator := false needsReasoningSummarySeparator := false
//reasoningSummaryTextByKey := make(map[string]string) //reasoningSummaryTextByKey := make(map[string]string)
if info.RelayFormat == types.RelayFormatClaude && info.ClaudeConvertInfo == nil {
info.ClaudeConvertInfo = &relaycommon.ClaudeConvertInfo{LastMessagesType: relaycommon.LastMessageTypeNone}
}
sendChatChunk := func(chunk *dto.ChatCompletionsStreamResponse) bool {
if chunk == nil {
return true
}
if info.RelayFormat == types.RelayFormatOpenAI {
if err := helper.ObjectData(c, chunk); err != nil {
streamErr = types.NewOpenAIError(err, types.ErrorCodeBadResponse, http.StatusInternalServerError)
return false
}
return true
}
chunkData, err := common.Marshal(chunk)
if err != nil {
streamErr = types.NewOpenAIError(err, types.ErrorCodeJsonMarshalFailed, http.StatusInternalServerError)
return false
}
if err := HandleStreamFormat(c, info, string(chunkData), false, false); err != nil {
streamErr = types.NewOpenAIError(err, types.ErrorCodeBadResponse, http.StatusInternalServerError)
return false
}
return true
}
sendStartIfNeeded := func() bool { sendStartIfNeeded := func() bool {
if sentStart { if sentStart {
return true return true
} }
if err := helper.ObjectData(c, helper.GenerateStartEmptyResponse(responseId, createAt, model, nil)); err != nil { if !sendChatChunk(helper.GenerateStartEmptyResponse(responseId, createAt, model, nil)) {
streamErr = types.NewOpenAIError(err, types.ErrorCodeBadResponse, http.StatusInternalServerError)
return false return false
} }
sentStart = true sentStart = true
@@ -186,8 +223,7 @@ func OaiResponsesToChatStreamHandler(c *gin.Context, info *relaycommon.RelayInfo
}, },
}, },
} }
if err := helper.ObjectData(c, chunk); err != nil { if !sendChatChunk(chunk) {
streamErr = types.NewOpenAIError(err, types.ErrorCodeBadResponse, http.StatusInternalServerError)
return false return false
} }
hasSentReasoningSummary = true hasSentReasoningSummary = true
@@ -245,8 +281,7 @@ func OaiResponsesToChatStreamHandler(c *gin.Context, info *relaycommon.RelayInfo
}, },
}, },
} }
if err := helper.ObjectData(c, chunk); err != nil { if !sendChatChunk(chunk) {
streamErr = types.NewOpenAIError(err, types.ErrorCodeBadResponse, http.StatusInternalServerError)
return false return false
} }
sawToolCall = true sawToolCall = true
@@ -340,8 +375,7 @@ func OaiResponsesToChatStreamHandler(c *gin.Context, info *relaycommon.RelayInfo
}, },
}, },
} }
if err := helper.ObjectData(c, chunk); err != nil { if !sendChatChunk(chunk) {
streamErr = types.NewOpenAIError(err, types.ErrorCodeBadResponse, http.StatusInternalServerError)
return false return false
} }
} }
@@ -436,13 +470,15 @@ func OaiResponsesToChatStreamHandler(c *gin.Context, info *relaycommon.RelayInfo
return false return false
} }
if !sentStop { if !sentStop {
if info.RelayFormat == types.RelayFormatClaude && info.ClaudeConvertInfo != nil {
info.ClaudeConvertInfo.Usage = usage
}
finishReason := "stop" finishReason := "stop"
if sawToolCall && outputText.Len() == 0 { if sawToolCall && outputText.Len() == 0 {
finishReason = "tool_calls" finishReason = "tool_calls"
} }
stop := helper.GenerateStopResponse(responseId, createAt, model, finishReason) stop := helper.GenerateStopResponse(responseId, createAt, model, finishReason)
if err := helper.ObjectData(c, stop); err != nil { if !sendChatChunk(stop) {
streamErr = types.NewOpenAIError(err, types.ErrorCodeBadResponse, http.StatusInternalServerError)
return false return false
} }
sentStop = true sentStop = true
@@ -473,26 +509,31 @@ func OaiResponsesToChatStreamHandler(c *gin.Context, info *relaycommon.RelayInfo
} }
if !sentStart { if !sentStart {
if err := helper.ObjectData(c, helper.GenerateStartEmptyResponse(responseId, createAt, model, nil)); err != nil { if !sendChatChunk(helper.GenerateStartEmptyResponse(responseId, createAt, model, nil)) {
return nil, types.NewOpenAIError(err, types.ErrorCodeBadResponse, http.StatusInternalServerError) return nil, streamErr
} }
} }
if !sentStop { if !sentStop {
if info.RelayFormat == types.RelayFormatClaude && info.ClaudeConvertInfo != nil {
info.ClaudeConvertInfo.Usage = usage
}
finishReason := "stop" finishReason := "stop"
if sawToolCall && outputText.Len() == 0 { if sawToolCall && outputText.Len() == 0 {
finishReason = "tool_calls" finishReason = "tool_calls"
} }
stop := helper.GenerateStopResponse(responseId, createAt, model, finishReason) stop := helper.GenerateStopResponse(responseId, createAt, model, finishReason)
if err := helper.ObjectData(c, stop); err != nil { if !sendChatChunk(stop) {
return nil, types.NewOpenAIError(err, types.ErrorCodeBadResponse, http.StatusInternalServerError) return nil, streamErr
} }
} }
if info.ShouldIncludeUsage && usage != nil { if info.RelayFormat == types.RelayFormatOpenAI && info.ShouldIncludeUsage && usage != nil {
if err := helper.ObjectData(c, helper.GenerateFinalUsageResponse(responseId, createAt, model, *usage)); err != nil { if err := helper.ObjectData(c, helper.GenerateFinalUsageResponse(responseId, createAt, model, *usage)); err != nil {
return nil, types.NewOpenAIError(err, types.ErrorCodeBadResponse, http.StatusInternalServerError) return nil, types.NewOpenAIError(err, types.ErrorCodeBadResponse, http.StatusInternalServerError)
} }
} }
helper.Done(c) if info.RelayFormat == types.RelayFormatOpenAI {
helper.Done(c)
}
return usage, nil return usage, nil
} }

View File

@@ -110,6 +110,23 @@ func ClaudeHelper(c *gin.Context, info *relaycommon.RelayInfo) (newAPIError *typ
} }
} }
if !model_setting.GetGlobalSettings().PassThroughRequestEnabled &&
!info.ChannelSetting.PassThroughBodyEnabled &&
service.ShouldChatCompletionsUseResponsesGlobal(info.ChannelId, info.ChannelType, info.OriginModelName) {
openAIRequest, convErr := service.ClaudeToOpenAIRequest(*request, info)
if convErr != nil {
return types.NewError(convErr, types.ErrorCodeConvertRequestFailed, types.ErrOptionWithSkipRetry())
}
usage, newApiErr := chatCompletionsViaResponses(c, info, adaptor, openAIRequest)
if newApiErr != nil {
return newApiErr
}
service.PostClaudeConsumeQuota(c, info, usage)
return nil
}
var requestBody io.Reader var requestBody io.Reader
if model_setting.GetGlobalSettings().PassThroughRequestEnabled || info.ChannelSetting.PassThroughBodyEnabled { if model_setting.GetGlobalSettings().PassThroughRequestEnabled || info.ChannelSetting.PassThroughBodyEnabled {
body, err := common.GetRequestBody(c) body, err := common.GetRequestBody(c)