Skip to content
8 changes: 8 additions & 0 deletions controller/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,14 @@ func tasksToDto(tasks []*model.Task, fillUser bool) []*dto.TaskDto {
}
}
result[i] = relay.TaskModel2Dto(task)
if !fillUser {
// Strip upstream model name from user-facing responses.
if props, ok := result[i].Properties.(model.Properties); ok {
stripped := props
stripped.UpstreamModelName = ""
result[i].Properties = stripped
}
}
}
return result
}
3 changes: 3 additions & 0 deletions model/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ func formatUserLogs(logs []*Log, startIdx int) {
delete(otherMap, "admin_info")
// delete(otherMap, "reject_reason")
delete(otherMap, "stream_status")
// Remove model mapping fields visible only to admins.
delete(otherMap, "is_model_mapped")
delete(otherMap, "upstream_model_name")
}
logs[i].Other = common.MapToJsonStr(otherMap)
logs[i].Id = startIdx + i + 1
Expand Down
30 changes: 21 additions & 9 deletions relay/channel/claude/relay-claude.go
Original file line number Diff line number Diff line change
Expand Up @@ -434,10 +434,13 @@ func RequestOpenAI2ClaudeMessage(c *gin.Context, textRequest dto.GeneralOpenAIRe
return &claudeRequest, nil
}

func StreamResponseClaude2OpenAI(claudeResponse *dto.ClaudeResponse) *dto.ChatCompletionsStreamResponse {
func StreamResponseClaude2OpenAI(claudeResponse *dto.ClaudeResponse, callerModel string) *dto.ChatCompletionsStreamResponse {
var response dto.ChatCompletionsStreamResponse
response.Object = "chat.completion.chunk"
response.Model = claudeResponse.Model
response.Model = callerModel
if response.Model == "" {
response.Model = claudeResponse.Model
}
response.Choices = make([]dto.ChatCompletionsStreamResponseChoice, 0)
tools := make([]dto.ToolCallResponse, 0)
fcIdx := 0
Expand All @@ -451,7 +454,6 @@ func StreamResponseClaude2OpenAI(claudeResponse *dto.ClaudeResponse) *dto.ChatCo
if claudeResponse.Type == "message_start" {
if claudeResponse.Message != nil {
response.Id = claudeResponse.Message.Id
response.Model = claudeResponse.Message.Model
}
//claudeUsage = &claudeResponse.Message.Usage
choice.Delta.SetContentString("")
Expand Down Expand Up @@ -518,7 +520,7 @@ func StreamResponseClaude2OpenAI(claudeResponse *dto.ClaudeResponse) *dto.ChatCo
return &response
}

func ResponseClaude2OpenAI(claudeResponse *dto.ClaudeResponse) *dto.OpenAITextResponse {
func ResponseClaude2OpenAI(claudeResponse *dto.ClaudeResponse, callerModel string) *dto.OpenAITextResponse {
choices := make([]dto.OpenAITextResponseChoice, 0)
fullTextResponse := dto.OpenAITextResponse{
Id: fmt.Sprintf("chatcmpl-%s", common.GetUUID()),
Expand Down Expand Up @@ -575,7 +577,11 @@ func ResponseClaude2OpenAI(claudeResponse *dto.ClaudeResponse) *dto.OpenAITextRe
if thinkingContent != "" {
choice.Message.ReasoningContent = &thinkingContent
}
fullTextResponse.Model = claudeResponse.Model
model := callerModel
if model == "" {
model = claudeResponse.Model
}
fullTextResponse.Model = model
choices = append(choices, choice)
fullTextResponse.Choices = choices
return &fullTextResponse
Expand Down Expand Up @@ -778,7 +784,10 @@ func FormatClaudeResponseInfo(claudeResponse *dto.ClaudeResponse, oaiResponse *d
if oaiResponse != nil {
oaiResponse.Id = claudeInfo.ResponseId
oaiResponse.Created = claudeInfo.Created
oaiResponse.Model = claudeInfo.Model
// Preserve caller model: only set model from claudeInfo if oaiResponse doesn't have one
if oaiResponse.Model == "" {
oaiResponse.Model = claudeInfo.Model
}
}
return true
}
Expand Down Expand Up @@ -816,7 +825,8 @@ func HandleStreamResponseData(c *gin.Context, info *relaycommon.RelayInfo, claud
}
helper.ClaudeChunkData(c, claudeResponse, data)
} else if info.RelayFormat == types.RelayFormatOpenAI {
response := StreamResponseClaude2OpenAI(&claudeResponse)
callerModel := relaycommon.GetCallerModelName(c, info)
response := StreamResponseClaude2OpenAI(&claudeResponse, callerModel)

if !FormatClaudeResponseInfo(&claudeResponse, response, claudeInfo) {
return nil
Expand Down Expand Up @@ -858,7 +868,8 @@ func HandleStreamFinalResponse(c *gin.Context, info *relaycommon.RelayInfo, clau
} else if info.RelayFormat == types.RelayFormatOpenAI {
if info.ShouldIncludeUsage {
openAIUsage := buildOpenAIStyleUsageFromClaudeUsage(claudeInfo.Usage)
response := helper.GenerateFinalUsageResponse(claudeInfo.ResponseId, claudeInfo.Created, info.UpstreamModelName, openAIUsage)
callerModel := relaycommon.GetCallerModelName(c, info)
response := helper.GenerateFinalUsageResponse(claudeInfo.ResponseId, claudeInfo.Created, callerModel, openAIUsage)
err := helper.ObjectData(c, response)
if err != nil {
common.SysLog("send final response failed: " + err.Error())
Expand Down Expand Up @@ -917,7 +928,8 @@ func HandleClaudeResponseData(c *gin.Context, info *relaycommon.RelayInfo, claud
var responseData []byte
switch info.RelayFormat {
case types.RelayFormatOpenAI:
openaiResponse := ResponseClaude2OpenAI(&claudeResponse)
callerModel := relaycommon.GetCallerModelName(c, info)
openaiResponse := ResponseClaude2OpenAI(&claudeResponse, callerModel)
openaiResponse.Usage = buildOpenAIStyleUsageFromClaudeUsage(claudeInfo.Usage)
responseData, err = json.Marshal(openaiResponse)
if err != nil {
Expand Down
6 changes: 3 additions & 3 deletions relay/channel/cloudflare/relay_cloudflare.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func cfStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Res
responseText += choice.Delta.GetContentString()
}
response.Id = id
response.Model = info.UpstreamModelName
response.Model = relaycommon.GetCallerModelName(c, info)
err = helper.ObjectData(c, response)
if isFirst {
isFirst = false
Expand All @@ -77,7 +77,7 @@ func cfStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Res
}
usage := service.ResponseText2Usage(c, responseText, info.UpstreamModelName, info.GetEstimatePromptTokens())
if info.ShouldIncludeUsage {
response := helper.GenerateFinalUsageResponse(id, info.StartTime.Unix(), info.UpstreamModelName, *usage)
response := helper.GenerateFinalUsageResponse(id, info.StartTime.Unix(), relaycommon.GetCallerModelName(c, info), *usage)
err := helper.ObjectData(c, response)
if err != nil {
logger.LogError(c, "error_rendering_final_usage_response: "+err.Error())
Expand All @@ -101,7 +101,7 @@ func cfHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Response)
if err != nil {
return types.NewError(err, types.ErrorCodeBadResponseBody), nil
}
response.Model = info.UpstreamModelName
response.Model = relaycommon.GetCallerModelName(c, info)
var responseText string
for _, choice := range response.Choices {
responseText += choice.Message.StringContent()
Expand Down
4 changes: 2 additions & 2 deletions relay/channel/cohere/relay-cohere.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func cohereStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http
openaiResp.Id = responseId
openaiResp.Created = createdTime
openaiResp.Object = "chat.completion.chunk"
openaiResp.Model = info.UpstreamModelName
openaiResp.Model = relaycommon.GetCallerModelName(c, info)
if cohereResp.IsFinished {
finishReason := stopReasonCohere2OpenAI(cohereResp.FinishReason)
openaiResp.Choices = []dto.ChatCompletionsStreamResponseChoice{
Expand Down Expand Up @@ -193,7 +193,7 @@ func cohereHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Respo
openaiResp.Id = cohereResp.ResponseId
openaiResp.Created = createdTime
openaiResp.Object = "chat.completion"
openaiResp.Model = info.UpstreamModelName
openaiResp.Model = relaycommon.GetCallerModelName(c, info)
openaiResp.Usage = usage

openaiResp.Choices = []dto.OpenAITextResponseChoice{
Expand Down
6 changes: 3 additions & 3 deletions relay/channel/coze/relay-coze.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func cozeChatHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Res
// convert coze response to openai response
var response dto.TextResponse
var cozeResponse CozeChatDetailResponse
response.Model = info.UpstreamModelName
response.Model = relaycommon.GetCallerModelName(c, info)
err = json.Unmarshal(responseBody, &cozeResponse)
if err != nil {
return nil, types.NewError(err, types.ErrorCodeBadResponseBody)
Expand Down Expand Up @@ -165,7 +165,7 @@ func handleCozeEvent(c *gin.Context, event string, data string, responseText *st
usage.TotalTokens = chatData.Usage.TokenCount

finishReason := "stop"
stopResponse := helper.GenerateStopResponse(id, common.GetTimestamp(), info.UpstreamModelName, finishReason)
stopResponse := helper.GenerateStopResponse(id, common.GetTimestamp(), relaycommon.GetCallerModelName(c, info), finishReason)
helper.ObjectData(c, stopResponse)

case "conversation.message.delta":
Expand All @@ -190,7 +190,7 @@ func handleCozeEvent(c *gin.Context, event string, data string, responseText *st
Id: id,
Object: "chat.completion.chunk",
Created: common.GetTimestamp(),
Model: info.UpstreamModelName,
Model: relaycommon.GetCallerModelName(c, info),
}

choice := dto.ChatCompletionsStreamResponseChoice{
Expand Down
12 changes: 6 additions & 6 deletions relay/channel/gemini/relay-gemini.go
Original file line number Diff line number Diff line change
Expand Up @@ -1338,7 +1338,7 @@ func GeminiChatStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *

response.Id = id
response.Created = createAt
response.Model = info.UpstreamModelName
response.Model = relaycommon.GetCallerModelName(c, info)
for choiceIdx := range response.Choices {
choiceKey := response.Choices[choiceIdx].Index
for toolIdx := range response.Choices[choiceIdx].Delta.ToolCalls {
Expand All @@ -1365,7 +1365,7 @@ func GeminiChatStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *
logger.LogDebug(c, fmt.Sprintf("info.SendResponseCount = %d", info.SendResponseCount))
if info.SendResponseCount == 0 {
// send first response
emptyResponse := helper.GenerateStartEmptyResponse(id, createAt, info.UpstreamModelName, nil)
emptyResponse := helper.GenerateStartEmptyResponse(id, createAt, relaycommon.GetCallerModelName(c, info), nil)
if response.IsToolCall() {
if len(emptyResponse.Choices) > 0 && len(response.Choices) > 0 {
toolCalls := response.Choices[0].Delta.ToolCalls
Expand Down Expand Up @@ -1399,7 +1399,7 @@ func GeminiChatStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *
logger.LogError(c, err.Error())
}
if isStop {
_ = handleStream(c, info, helper.GenerateStopResponse(id, createAt, info.UpstreamModelName, finishReason))
_ = handleStream(c, info, helper.GenerateStopResponse(id, createAt, relaycommon.GetCallerModelName(c, info), finishReason))
}
return true
})
Expand All @@ -1408,7 +1408,7 @@ func GeminiChatStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *
return usage, err
}

response := helper.GenerateFinalUsageResponse(id, createAt, info.UpstreamModelName, *usage)
response := helper.GenerateFinalUsageResponse(id, createAt, relaycommon.GetCallerModelName(c, info), *usage)
handleErr := handleFinalStream(c, info, response)
if handleErr != nil {
common.SysLog("send final response failed: " + handleErr.Error())
Expand Down Expand Up @@ -1466,7 +1466,7 @@ func GeminiChatHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.R
return &usage, nil
}
fullTextResponse := responseGeminiChat2OpenAI(c, &geminiResponse)
fullTextResponse.Model = info.UpstreamModelName
fullTextResponse.Model = relaycommon.GetCallerModelName(c, info)
usage := buildUsageFromGeminiMetadata(geminiResponse.UsageMetadata, info.GetEstimatePromptTokens())

fullTextResponse.Usage = usage
Expand Down Expand Up @@ -1510,7 +1510,7 @@ func GeminiEmbeddingHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *h
openAIResponse := dto.OpenAIEmbeddingResponse{
Object: "list",
Data: make([]dto.OpenAIEmbeddingResponseItem, 0, len(geminiResponse.Embeddings)),
Model: info.UpstreamModelName,
Model: relaycommon.GetCallerModelName(c, info),
}

for i, embedding := range geminiResponse.Embeddings {
Expand Down
2 changes: 1 addition & 1 deletion relay/channel/ollama/relay-ollama.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ func ollamaEmbeddingHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *h
data = append(data, dto.OpenAIEmbeddingResponseItem{Index: i, Object: "embedding", Embedding: emb})
}
usage := &dto.Usage{PromptTokens: oResp.PromptEvalCount, CompletionTokens: 0, TotalTokens: oResp.PromptEvalCount}
embResp := &dto.OpenAIEmbeddingResponse{Object: "list", Data: data, Model: info.UpstreamModelName, Usage: *usage}
embResp := &dto.OpenAIEmbeddingResponse{Object: "list", Data: data, Model: relaycommon.GetCallerModelName(c, info), Usage: *usage}
out, _ := common.Marshal(embResp)
service.IOCopyBytesGracefully(c, resp, out)
return usage, nil
Expand Down
8 changes: 4 additions & 4 deletions relay/channel/ollama/stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func ollamaStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http
helper.SetEventStreamHeaders(c)
scanner := bufio.NewScanner(resp.Body)
usage := &dto.Usage{}
var model = info.UpstreamModelName
var model = relaycommon.GetCallerModelName(c, info)
var responseId = common.GetUUID()
var created = time.Now().Unix()
var toolCallIndex int
Expand All @@ -93,7 +93,7 @@ func ollamaStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http
return usage, types.NewOpenAIError(err, types.ErrorCodeBadResponseBody, http.StatusInternalServerError)
}
if chunk.Model != "" {
model = chunk.Model
// Don't override caller model with upstream model
}
created = toUnix(chunk.CreatedAt)

Expand Down Expand Up @@ -259,9 +259,9 @@ func ollamaChatHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.R
}
}

model := lastChunk.Model
model := relaycommon.GetCallerModelName(c, info)
if model == "" {
model = info.UpstreamModelName
model = lastChunk.Model
}
created := toUnix(lastChunk.CreatedAt)
usage := &dto.Usage{PromptTokens: lastChunk.PromptEvalCount, CompletionTokens: lastChunk.EvalCount, TotalTokens: lastChunk.PromptEvalCount + lastChunk.EvalCount}
Expand Down
17 changes: 10 additions & 7 deletions relay/channel/openai/chat_via_responses.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@ func OaiResponsesToChatHandler(c *gin.Context, info *relaycommon.RelayInfo, resp
return nil, types.NewOpenAIError(err, types.ErrorCodeBadResponseBody, http.StatusInternalServerError)
}

// Override model to caller model
callerModel := relaycommon.GetCallerModelName(c, info)
if callerModel != "" {
chatResp.Model = callerModel
}
if err != nil {
return nil, types.NewOpenAIError(err, types.ErrorCodeBadResponseBody, http.StatusInternalServerError)
}

if usage == nil || usage.TotalTokens == 0 {
text := service.ExtractOutputTextFromResponses(&responsesResp)
usage = service.ResponseText2Usage(c, text, info.UpstreamModelName, info.GetEstimatePromptTokens())
Expand Down Expand Up @@ -99,7 +108,7 @@ func OaiResponsesToChatStreamHandler(c *gin.Context, info *relaycommon.RelayInfo

responseId := helper.GetResponseID(c)
createAt := time.Now().Unix()
model := info.UpstreamModelName
model := relaycommon.GetCallerModelName(c, info)

var (
usage = &dto.Usage{}
Expand Down Expand Up @@ -312,9 +321,6 @@ func OaiResponsesToChatStreamHandler(c *gin.Context, info *relaycommon.RelayInfo
switch streamResp.Type {
case "response.created":
if streamResp.Response != nil {
if streamResp.Response.Model != "" {
model = streamResp.Response.Model
}
if streamResp.Response.CreatedAt != 0 {
createAt = int64(streamResp.Response.CreatedAt)
}
Expand Down Expand Up @@ -444,9 +450,6 @@ func OaiResponsesToChatStreamHandler(c *gin.Context, info *relaycommon.RelayInfo

case "response.completed":
if streamResp.Response != nil {
if streamResp.Response.Model != "" {
model = streamResp.Response.Model
}
if streamResp.Response.CreatedAt != 0 {
createAt = int64(streamResp.Response.CreatedAt)
}
Expand Down
2 changes: 1 addition & 1 deletion relay/channel/openai/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ func handleLastResponse(lastStreamData string, responseId *string, createAt *int
*responseId = lastStreamResponse.Id
*createAt = lastStreamResponse.Created
*systemFingerprint = lastStreamResponse.GetSystemFingerprint()
*model = lastStreamResponse.Model
// Don't overwrite model with upstream model; caller model is set upstream

if service.ValidUsage(lastStreamResponse.Usage) {
*containStreamUsage = true
Expand Down
24 changes: 21 additions & 3 deletions relay/channel/openai/relay-openai.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,14 @@ func sendStreamData(c *gin.Context, info *relaycommon.RelayInfo, data string, fo
return nil
}

// Patch top-level model to caller model for raw passthrough path
if !forceFormat && !thinkToContent {
callerModel := relaycommon.GetCallerModelName(c, info)
patched, changed, err := relaycommon.PatchTopLevelModelRaw(common.StringToByteSlice(data), callerModel)
if err == nil && changed {
relaycommon.MarkResponseBodyRewritten(c)
return helper.StringData(c, string(patched))
}
return helper.StringData(c, data)
}

Expand Down Expand Up @@ -111,7 +118,7 @@ func OaiStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Re

defer service.CloseResponseBodyGracefully(resp)

model := info.UpstreamModelName
model := relaycommon.GetCallerModelName(c, info)
var responseId string
var createAt int64 = 0
var systemFingerprint string
Expand All @@ -123,8 +130,10 @@ func OaiStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Re
var lastStreamData string
var secondLastStreamData string // 存储倒数第二个stream data,用于音频模型

// 检查是否为音频模型
isAudioModel := strings.Contains(strings.ToLower(model), "audio")
// Audio model detection uses upstream model name, not caller model.
// When model mapping is active the caller name may not contain "audio"
// even though the upstream model is an audio model.
isAudioModel := strings.Contains(strings.ToLower(info.UpstreamModelName), "audio")

helper.StreamScannerHandler(c, resp, info, func(data string, sr *helper.StreamResult) {
if lastStreamData != "" {
Expand Down Expand Up @@ -294,6 +303,15 @@ func OpenaiHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Respo
responseBody = geminiRespStr
}

callerModel := relaycommon.GetCallerModelName(c, info)
if callerModel != "" {
patched, changed, err := relaycommon.PatchTopLevelModelRaw(responseBody, callerModel)
if err == nil && changed {
relaycommon.MarkResponseBodyRewritten(c)
responseBody = patched
}
}

service.IOCopyBytesGracefully(c, resp, responseBody)

return &simpleResponse.Usage, nil
Expand Down
Loading
Loading