Seefs пре 4 месеци
родитељ
комит
86c63ea4a7

+ 0 - 1
constant/context_key.go

@@ -8,7 +8,6 @@ const (
 
 	ContextKeyOriginalModel    ContextKey = "original_model"
 	ContextKeyRequestStartTime ContextKey = "request_start_time"
-	ContextKeyRelayFormat      ContextKey = "relay_format"
 
 	/* token related keys */
 	ContextKeyTokenUnlimited         ContextKey = "token_unlimited_quota"

+ 2 - 3
controller/relay.go

@@ -299,9 +299,8 @@ 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
+		if c.Request != nil && c.Request.URL != nil {
+			other["request_path"] = c.Request.URL.Path
 		}
 		other["error_type"] = err.GetErrorType()
 		other["error_code"] = err.GetErrorCode()

+ 0 - 4
relay/common/relay_info.go

@@ -465,10 +465,6 @@ func GenRelayInfo(c *gin.Context, relayFormat types.RelayFormat, request dto.Req
 		return nil, errors.New("invalid relay format")
 	}
 
-	if info != nil {
-		common.SetContextKey(c, constant.ContextKeyRelayFormat, string(info.RelayFormat))
-	}
-
 	return info, nil
 }
 

+ 2 - 2
relay/relay_task.go

@@ -165,8 +165,8 @@ func RelayTaskSubmit(c *gin.Context, info *relaycommon.RelayInfo) (taskErr *dto.
 					}
 				}
 				other := make(map[string]interface{})
-				if info.RelayFormat != "" {
-					other["relay_format"] = string(info.RelayFormat)
+				if c != nil && c.Request != nil && c.Request.URL != nil {
+					other["request_path"] = c.Request.URL.Path
 				}
 				other["model_price"] = modelPrice
 				other["group_ratio"] = groupRatio

+ 23 - 6
service/log_info_generate.go

@@ -1,6 +1,8 @@
 package service
 
 import (
+	"strings"
+
 	"github.com/QuantumNous/new-api/common"
 	"github.com/QuantumNous/new-api/constant"
 	"github.com/QuantumNous/new-api/dto"
@@ -10,12 +12,28 @@ import (
 	"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{})
-	if relayInfo != nil && relayInfo.RelayFormat != "" {
-		other["relay_format"] = string(relayInfo.RelayFormat)
-	}
 	other["model_ratio"] = modelRatio
 	other["group_ratio"] = groupRatio
 	other["completion_ratio"] = completionRatio
@@ -45,6 +63,7 @@ func GenerateTextOtherInfo(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, m
 		adminInfo["multi_key_index"] = common.GetContextKeyInt(ctx, constant.ContextKeyChannelMultiKeyIndex)
 	}
 	other["admin_info"] = adminInfo
+	appendRequestPath(ctx, relayInfo, other)
 	return other
 }
 
@@ -83,13 +102,11 @@ func GenerateClaudeOtherInfo(ctx *gin.Context, relayInfo *relaycommon.RelayInfo,
 
 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 {
 		other["user_group_ratio"] = priceData.GroupRatioInfo.GroupSpecialRatio
 	}
+	appendRequestPath(nil, relayInfo, other)
 	return other
 }

+ 85 - 22
web/src/components/table/usage-logs/UsageLogsColumnDefs.jsx

@@ -103,36 +103,98 @@ 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' },
+const endpointColorMap = {
+  chat: 'blue',
+  completions: 'blue',
+  messages: 'purple',
+  responses: 'violet',
+  images: 'pink',
+  image: 'pink',
+  embeddings: 'green',
+  embedding: 'green',
+  audio: 'teal',
+  speech: 'teal',
+  translations: 'teal',
+  transcriptions: 'teal',
+  rerank: 'cyan',
+  moderations: 'red',
+  models: 'orange',
+  engines: 'orange',
+  mj: 'red',
+  submit: 'red',
+  suno: 'amber',
+  realtime: 'indigo',
+  notifications: 'violet',
 };
 
-function renderRelayFormat(relayFormat) {
-  if (!relayFormat) {
+function formatPathSegment(segment) {
+  if (!segment) {
+    return '';
+  }
+  const normalized = segment.replace(/^:/, '').replace(/[_-]/g, ' ');
+  return normalized
+    .split(' ')
+    .filter(Boolean)
+    .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
+    .join(' ');
+}
+
+function deriveEndpointMeta(path) {
+  if (!path) {
+    return null;
+  }
+  const cleanPath = path.split('?')[0];
+  const segments = cleanPath.split('/').filter(Boolean);
+  if (segments.length === 0) {
     return null;
   }
-  const meta = relayFormatMeta[relayFormat] || {
-    color: 'grey',
-    label: relayFormat
-      .replace(/_/g, ' ')
-      .replace(/\b\w/g, (c) => c.toUpperCase()),
+  let startIndex = 0;
+  if (/^v\d/i.test(segments[0])) {
+    startIndex = 1;
+  }
+
+  const primary = segments[startIndex] || segments[segments.length - 1];
+  const tailSegments = segments
+    .slice(startIndex + 1)
+    .filter((segment) => segment && !segment.startsWith(':'));
+  const secondary = tailSegments[tailSegments.length - 1];
+
+  const labelParts = [];
+  const formattedPrimary = formatPathSegment(primary);
+  if (formattedPrimary) {
+    labelParts.push(formattedPrimary);
+  }
+  const formattedSecondary = formatPathSegment(secondary);
+  if (formattedSecondary && formattedSecondary !== formattedPrimary) {
+    labelParts.push(formattedSecondary);
+  }
+  const label = labelParts.join(' · ');
+
+  const color =
+    endpointColorMap[primary] ||
+    (secondary ? endpointColorMap[secondary] : undefined) ||
+    'grey';
+
+  return {
+    label: label || formatPathSegment(primary),
+    color,
   };
+}
 
-  return (
+function renderEndpointTag(requestPath) {
+  const meta = deriveEndpointMeta(requestPath);
+  if (!meta) {
+    return null;
+  }
+  const tag = (
     <Tag color={meta.color} type='light' shape='circle' size='small'>
       {meta.label}
     </Tag>
   );
+  if (requestPath) {
+    return <Tooltip content={requestPath}>{tag}</Tooltip>;
+  }
+  return tag;
 }
 
 function renderIsStream(bool, t) {
@@ -403,11 +465,12 @@ export const getLogsColumns = ({
       title: t('类型'),
       dataIndex: 'type',
       render: (text, record, index) => {
-        const relayFormat = getLogOther(record.other)?.relay_format;
+        const other = getLogOther(record.other) || {};
+        const requestPath = other.request_path;
         return (
           <Space size='small' wrap>
             {renderType(text, t)}
-            {renderRelayFormat(relayFormat)}
+            {renderEndpointTag(requestPath)}
           </Space>
         );
       },