| package service |
|
|
| import ( |
| "strings" |
|
|
| "github.com/QuantumNous/new-api/common" |
| "github.com/QuantumNous/new-api/constant" |
| "github.com/QuantumNous/new-api/dto" |
| relaycommon "github.com/QuantumNous/new-api/relay/common" |
| "github.com/QuantumNous/new-api/types" |
|
|
| "github.com/gin-gonic/gin" |
| ) |
|
|
| func appendRequestPath(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, other map[string]interface{}) { |
| if other == nil { |
| return |
| } |
| if ctx != nil && ctx.Request != nil && ctx.Request.URL != nil { |
| if path := ctx.Request.URL.Path; path != "" { |
| other["request_path"] = path |
| return |
| } |
| } |
| if relayInfo != nil && relayInfo.RequestURLPath != "" { |
| path := relayInfo.RequestURLPath |
| if idx := strings.Index(path, "?"); idx != -1 { |
| path = path[:idx] |
| } |
| other["request_path"] = path |
| } |
| } |
|
|
| func GenerateTextOtherInfo(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, modelRatio, groupRatio, completionRatio float64, |
| cacheTokens int, cacheRatio float64, modelPrice float64, userGroupRatio float64) map[string]interface{} { |
| other := make(map[string]interface{}) |
| other["model_ratio"] = modelRatio |
| other["group_ratio"] = groupRatio |
| other["completion_ratio"] = completionRatio |
| other["cache_tokens"] = cacheTokens |
| other["cache_ratio"] = cacheRatio |
| other["model_price"] = modelPrice |
| other["user_group_ratio"] = userGroupRatio |
| other["frt"] = float64(relayInfo.FirstResponseTime.UnixMilli() - relayInfo.StartTime.UnixMilli()) |
| if relayInfo.ReasoningEffort != "" { |
| other["reasoning_effort"] = relayInfo.ReasoningEffort |
| } |
| if relayInfo.IsModelMapped { |
| other["is_model_mapped"] = true |
| other["upstream_model_name"] = relayInfo.UpstreamModelName |
| } |
|
|
| isSystemPromptOverwritten := common.GetContextKeyBool(ctx, constant.ContextKeySystemPromptOverride) |
| if isSystemPromptOverwritten { |
| other["is_system_prompt_overwritten"] = true |
| } |
|
|
| adminInfo := make(map[string]interface{}) |
| adminInfo["use_channel"] = ctx.GetStringSlice("use_channel") |
| isMultiKey := common.GetContextKeyBool(ctx, constant.ContextKeyChannelIsMultiKey) |
| if isMultiKey { |
| adminInfo["is_multi_key"] = true |
| adminInfo["multi_key_index"] = common.GetContextKeyInt(ctx, constant.ContextKeyChannelMultiKeyIndex) |
| } |
|
|
| isLocalCountTokens := common.GetContextKeyBool(ctx, constant.ContextKeyLocalCountTokens) |
| if isLocalCountTokens { |
| adminInfo["local_count_tokens"] = isLocalCountTokens |
| } |
|
|
| AppendChannelAffinityAdminInfo(ctx, adminInfo) |
|
|
| other["admin_info"] = adminInfo |
| appendRequestPath(ctx, relayInfo, other) |
| appendRequestConversionChain(relayInfo, other) |
| appendBillingInfo(relayInfo, other) |
| return other |
| } |
|
|
| func appendBillingInfo(relayInfo *relaycommon.RelayInfo, other map[string]interface{}) { |
| if relayInfo == nil || other == nil { |
| return |
| } |
| |
| if relayInfo.BillingSource != "" { |
| other["billing_source"] = relayInfo.BillingSource |
| } |
| if relayInfo.UserSetting.BillingPreference != "" { |
| other["billing_preference"] = relayInfo.UserSetting.BillingPreference |
| } |
| if relayInfo.BillingSource == "subscription" { |
| if relayInfo.SubscriptionId != 0 { |
| other["subscription_id"] = relayInfo.SubscriptionId |
| } |
| if relayInfo.SubscriptionPreConsumed > 0 { |
| other["subscription_pre_consumed"] = relayInfo.SubscriptionPreConsumed |
| } |
| |
| if relayInfo.SubscriptionPostDelta != 0 { |
| other["subscription_post_delta"] = relayInfo.SubscriptionPostDelta |
| } |
| if relayInfo.SubscriptionPlanId != 0 { |
| other["subscription_plan_id"] = relayInfo.SubscriptionPlanId |
| } |
| if relayInfo.SubscriptionPlanTitle != "" { |
| other["subscription_plan_title"] = relayInfo.SubscriptionPlanTitle |
| } |
| |
| consumed := relayInfo.SubscriptionPreConsumed + relayInfo.SubscriptionPostDelta |
| usedFinal := relayInfo.SubscriptionAmountUsedAfterPreConsume + relayInfo.SubscriptionPostDelta |
| if consumed < 0 { |
| consumed = 0 |
| } |
| if usedFinal < 0 { |
| usedFinal = 0 |
| } |
| if relayInfo.SubscriptionAmountTotal > 0 { |
| remain := relayInfo.SubscriptionAmountTotal - usedFinal |
| if remain < 0 { |
| remain = 0 |
| } |
| other["subscription_total"] = relayInfo.SubscriptionAmountTotal |
| other["subscription_used"] = usedFinal |
| other["subscription_remain"] = remain |
| } |
| if consumed > 0 { |
| other["subscription_consumed"] = consumed |
| } |
| |
| other["wallet_quota_deducted"] = 0 |
| } |
| } |
|
|
| func appendRequestConversionChain(relayInfo *relaycommon.RelayInfo, other map[string]interface{}) { |
| if relayInfo == nil || other == nil { |
| return |
| } |
| if len(relayInfo.RequestConversionChain) == 0 { |
| return |
| } |
| chain := make([]string, 0, len(relayInfo.RequestConversionChain)) |
| for _, f := range relayInfo.RequestConversionChain { |
| switch f { |
| case types.RelayFormatOpenAI: |
| chain = append(chain, "OpenAI Compatible") |
| case types.RelayFormatClaude: |
| chain = append(chain, "Claude Messages") |
| case types.RelayFormatGemini: |
| chain = append(chain, "Google Gemini") |
| case types.RelayFormatOpenAIResponses: |
| chain = append(chain, "OpenAI Responses") |
| default: |
| chain = append(chain, string(f)) |
| } |
| } |
| if len(chain) == 0 { |
| return |
| } |
| other["request_conversion"] = chain |
| } |
|
|
| func GenerateWssOtherInfo(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, usage *dto.RealtimeUsage, modelRatio, groupRatio, completionRatio, audioRatio, audioCompletionRatio, modelPrice, userGroupRatio float64) map[string]interface{} { |
| info := GenerateTextOtherInfo(ctx, relayInfo, modelRatio, groupRatio, completionRatio, 0, 0.0, modelPrice, userGroupRatio) |
| info["ws"] = true |
| info["audio_input"] = usage.InputTokenDetails.AudioTokens |
| info["audio_output"] = usage.OutputTokenDetails.AudioTokens |
| info["text_input"] = usage.InputTokenDetails.TextTokens |
| info["text_output"] = usage.OutputTokenDetails.TextTokens |
| info["audio_ratio"] = audioRatio |
| info["audio_completion_ratio"] = audioCompletionRatio |
| return info |
| } |
|
|
| func GenerateAudioOtherInfo(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, usage *dto.Usage, modelRatio, groupRatio, completionRatio, audioRatio, audioCompletionRatio, modelPrice, userGroupRatio float64) map[string]interface{} { |
| info := GenerateTextOtherInfo(ctx, relayInfo, modelRatio, groupRatio, completionRatio, 0, 0.0, modelPrice, userGroupRatio) |
| info["audio"] = true |
| info["audio_input"] = usage.PromptTokensDetails.AudioTokens |
| info["audio_output"] = usage.CompletionTokenDetails.AudioTokens |
| info["text_input"] = usage.PromptTokensDetails.TextTokens |
| info["text_output"] = usage.CompletionTokenDetails.TextTokens |
| info["audio_ratio"] = audioRatio |
| info["audio_completion_ratio"] = audioCompletionRatio |
| return info |
| } |
|
|
| func GenerateClaudeOtherInfo(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, modelRatio, groupRatio, completionRatio float64, |
| cacheTokens int, cacheRatio float64, |
| cacheCreationTokens int, cacheCreationRatio float64, |
| cacheCreationTokens5m int, cacheCreationRatio5m float64, |
| cacheCreationTokens1h int, cacheCreationRatio1h float64, |
| modelPrice float64, userGroupRatio float64) map[string]interface{} { |
| info := GenerateTextOtherInfo(ctx, relayInfo, modelRatio, groupRatio, completionRatio, cacheTokens, cacheRatio, modelPrice, userGroupRatio) |
| info["claude"] = true |
| info["cache_creation_tokens"] = cacheCreationTokens |
| info["cache_creation_ratio"] = cacheCreationRatio |
| if cacheCreationTokens5m != 0 { |
| info["cache_creation_tokens_5m"] = cacheCreationTokens5m |
| info["cache_creation_ratio_5m"] = cacheCreationRatio5m |
| } |
| if cacheCreationTokens1h != 0 { |
| info["cache_creation_tokens_1h"] = cacheCreationTokens1h |
| info["cache_creation_ratio_1h"] = cacheCreationRatio1h |
| } |
| return info |
| } |
|
|
| func GenerateMjOtherInfo(relayInfo *relaycommon.RelayInfo, priceData types.PriceData) map[string]interface{} { |
| other := make(map[string]interface{}) |
| other["model_price"] = priceData.ModelPrice |
| other["group_ratio"] = priceData.GroupRatioInfo.GroupRatio |
| if priceData.GroupRatioInfo.HasSpecialRatio { |
| other["user_group_ratio"] = priceData.GroupRatioInfo.GroupSpecialRatio |
| } |
| appendRequestPath(nil, relayInfo, other) |
| return other |
| } |
|
|