diff --git a/constant/context_key.go b/constant/context_key.go index f7640272c..87995fd6c 100644 --- a/constant/context_key.go +++ b/constant/context_key.go @@ -8,6 +8,7 @@ const ( ContextKeyOriginalModel ContextKey = "original_model" ContextKeyRequestStartTime ContextKey = "request_start_time" + ContextKeyRelayFormat ContextKey = "relay_format" /* token related keys */ ContextKeyTokenUnlimited ContextKey = "token_unlimited_quota" diff --git a/controller/relay.go b/controller/relay.go index e8ea1998e..a9bb16c8f 100644 --- a/controller/relay.go +++ b/controller/relay.go @@ -299,6 +299,10 @@ func processChannelError(c *gin.Context, channelError types.ChannelError, err *t userGroup := c.GetString("group") channelId := c.GetInt("channel_id") other := make(map[string]interface{}) + relayFormat := common.GetContextKeyString(c, constant.ContextKeyRelayFormat) + if relayFormat != "" { + other["relay_format"] = relayFormat + } other["error_type"] = err.GetErrorType() other["error_code"] = err.GetErrorCode() other["status_code"] = err.StatusCode diff --git a/relay/common/relay_info.go b/relay/common/relay_info.go index c7c73980f..5ae0f4481 100644 --- a/relay/common/relay_info.go +++ b/relay/common/relay_info.go @@ -426,38 +426,50 @@ func genBaseRelayInfo(c *gin.Context, request dto.Request) *RelayInfo { } func GenRelayInfo(c *gin.Context, relayFormat types.RelayFormat, request dto.Request, ws *websocket.Conn) (*RelayInfo, error) { + var info *RelayInfo + switch relayFormat { case types.RelayFormatOpenAI: - return GenRelayInfoOpenAI(c, request), nil + info = GenRelayInfoOpenAI(c, request) case types.RelayFormatOpenAIAudio: - return GenRelayInfoOpenAIAudio(c, request), nil + info = GenRelayInfoOpenAIAudio(c, request) case types.RelayFormatOpenAIImage: - return GenRelayInfoImage(c, request), nil + info = GenRelayInfoImage(c, request) case types.RelayFormatOpenAIRealtime: - return GenRelayInfoWs(c, ws), nil + info = GenRelayInfoWs(c, ws) case types.RelayFormatClaude: - return GenRelayInfoClaude(c, request), nil + info = GenRelayInfoClaude(c, request) case types.RelayFormatRerank: - if request, ok := request.(*dto.RerankRequest); ok { - return GenRelayInfoRerank(c, request), nil + rerankReq, ok := request.(*dto.RerankRequest) + if !ok { + return nil, errors.New("request is not a RerankRequest") } - return nil, errors.New("request is not a RerankRequest") + info = GenRelayInfoRerank(c, rerankReq) case types.RelayFormatGemini: - return GenRelayInfoGemini(c, request), nil + info = GenRelayInfoGemini(c, request) case types.RelayFormatEmbedding: - return GenRelayInfoEmbedding(c, request), nil + info = GenRelayInfoEmbedding(c, request) case types.RelayFormatOpenAIResponses: - if request, ok := request.(*dto.OpenAIResponsesRequest); ok { - return GenRelayInfoResponses(c, request), nil + responsesReq, ok := request.(*dto.OpenAIResponsesRequest) + if !ok { + return nil, errors.New("request is not a OpenAIResponsesRequest") } - return nil, errors.New("request is not a OpenAIResponsesRequest") + info = GenRelayInfoResponses(c, responsesReq) case types.RelayFormatTask: - return genBaseRelayInfo(c, nil), nil + info = genBaseRelayInfo(c, nil) + info.RelayFormat = types.RelayFormatTask case types.RelayFormatMjProxy: - return genBaseRelayInfo(c, nil), nil + info = genBaseRelayInfo(c, nil) + info.RelayFormat = types.RelayFormatMjProxy default: return nil, errors.New("invalid relay format") } + + if info != nil { + common.SetContextKey(c, constant.ContextKeyRelayFormat, string(info.RelayFormat)) + } + + return info, nil } func (info *RelayInfo) SetPromptTokens(promptTokens int) { diff --git a/relay/mjproxy_handler.go b/relay/mjproxy_handler.go index ad42dfecc..8916ab181 100644 --- a/relay/mjproxy_handler.go +++ b/relay/mjproxy_handler.go @@ -218,7 +218,7 @@ func RelaySwapFace(c *gin.Context, info *relaycommon.RelayInfo) *dto.MidjourneyR tokenName := c.GetString("token_name") logContent := fmt.Sprintf("模型固定价格 %.2f,分组倍率 %.2f,操作 %s", priceData.ModelPrice, priceData.GroupRatioInfo.GroupRatio, constant.MjActionSwapFace) - other := service.GenerateMjOtherInfo(priceData) + other := service.GenerateMjOtherInfo(info, priceData) model.RecordConsumeLog(c, info.UserId, model.RecordConsumeLogParams{ ChannelId: info.ChannelId, ModelName: modelName, @@ -518,7 +518,7 @@ func RelayMidjourneySubmit(c *gin.Context, relayInfo *relaycommon.RelayInfo) *dt } tokenName := c.GetString("token_name") logContent := fmt.Sprintf("模型固定价格 %.2f,分组倍率 %.2f,操作 %s,ID %s", priceData.ModelPrice, priceData.GroupRatioInfo.GroupRatio, midjRequest.Action, midjResponse.Result) - other := service.GenerateMjOtherInfo(priceData) + other := service.GenerateMjOtherInfo(relayInfo, priceData) model.RecordConsumeLog(c, relayInfo.UserId, model.RecordConsumeLogParams{ ChannelId: relayInfo.ChannelId, ModelName: modelName, diff --git a/relay/relay_task.go b/relay/relay_task.go index 4a30fb68b..83879c342 100644 --- a/relay/relay_task.go +++ b/relay/relay_task.go @@ -165,6 +165,9 @@ func RelayTaskSubmit(c *gin.Context, info *relaycommon.RelayInfo) (taskErr *dto. } } other := make(map[string]interface{}) + if info.RelayFormat != "" { + other["relay_format"] = string(info.RelayFormat) + } other["model_price"] = modelPrice other["group_ratio"] = groupRatio if hasUserGroupRatio { diff --git a/service/log_info_generate.go b/service/log_info_generate.go index 5b737692e..58b36ced0 100644 --- a/service/log_info_generate.go +++ b/service/log_info_generate.go @@ -13,6 +13,9 @@ import ( 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{}) + if relayInfo != nil && relayInfo.RelayFormat != "" { + other["relay_format"] = string(relayInfo.RelayFormat) + } other["model_ratio"] = modelRatio other["group_ratio"] = groupRatio other["completion_ratio"] = completionRatio @@ -78,8 +81,11 @@ func GenerateClaudeOtherInfo(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, return info } -func GenerateMjOtherInfo(priceData types.PerCallPriceData) map[string]interface{} { +func GenerateMjOtherInfo(relayInfo *relaycommon.RelayInfo, priceData types.PerCallPriceData) map[string]interface{} { other := make(map[string]interface{}) + if relayInfo != nil && relayInfo.RelayFormat != "" { + other["relay_format"] = string(relayInfo.RelayFormat) + } other["model_price"] = priceData.ModelPrice other["group_ratio"] = priceData.GroupRatioInfo.GroupRatio if priceData.GroupRatioInfo.HasSpecialRatio { diff --git a/web/src/components/table/usage-logs/UsageLogsColumnDefs.jsx b/web/src/components/table/usage-logs/UsageLogsColumnDefs.jsx index 9af30226a..d41c35ccf 100644 --- a/web/src/components/table/usage-logs/UsageLogsColumnDefs.jsx +++ b/web/src/components/table/usage-logs/UsageLogsColumnDefs.jsx @@ -103,6 +103,38 @@ function renderType(type, t) { } } +const relayFormatMeta = { + openai: { color: 'blue', label: 'OpenAI' }, + claude: { color: 'purple', label: 'Claude' }, + gemini: { color: 'orange', label: 'Gemini' }, + openai_responses: { color: 'violet', label: 'Responses' }, + openai_audio: { color: 'teal', label: 'Audio' }, + openai_image: { color: 'pink', label: 'Image' }, + openai_realtime: { color: 'indigo', label: 'Realtime' }, + rerank: { color: 'cyan', label: 'Rerank' }, + embedding: { color: 'green', label: 'Embedding' }, + task: { color: 'amber', label: 'Task' }, + mj_proxy: { color: 'red', label: 'Midjourney' }, +}; + +function renderRelayFormat(relayFormat) { + if (!relayFormat) { + return null; + } + const meta = relayFormatMeta[relayFormat] || { + color: 'grey', + label: relayFormat + .replace(/_/g, ' ') + .replace(/\b\w/g, (c) => c.toUpperCase()), + }; + + return ( + + {meta.label} + + ); +} + function renderIsStream(bool, t) { if (bool) { return ( @@ -371,7 +403,13 @@ export const getLogsColumns = ({ title: t('类型'), dataIndex: 'type', render: (text, record, index) => { - return <>{renderType(text, t)}; + const relayFormat = getLogOther(record.other)?.relay_format; + return ( + + {renderType(text, t)} + {renderRelayFormat(relayFormat)} + + ); }, }, {