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)}
+
+ );
},
},
{