feat: endpoint type log

This commit is contained in:
Seefs 2025-10-13 22:44:40 +08:00
parent 9718568179
commit b3fc7255ee
6 changed files with 113 additions and 39 deletions

View File

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

View File

@ -299,9 +299,8 @@ func processChannelError(c *gin.Context, channelError types.ChannelError, err *t
userGroup := c.GetString("group") userGroup := c.GetString("group")
channelId := c.GetInt("channel_id") channelId := c.GetInt("channel_id")
other := make(map[string]interface{}) other := make(map[string]interface{})
relayFormat := common.GetContextKeyString(c, constant.ContextKeyRelayFormat) if c.Request != nil && c.Request.URL != nil {
if relayFormat != "" { other["request_path"] = c.Request.URL.Path
other["relay_format"] = relayFormat
} }
other["error_type"] = err.GetErrorType() other["error_type"] = err.GetErrorType()
other["error_code"] = err.GetErrorCode() other["error_code"] = err.GetErrorCode()

View File

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

View File

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

View File

@ -1,6 +1,8 @@
package service package service
import ( import (
"strings"
"github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/constant" "github.com/QuantumNous/new-api/constant"
"github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/dto"
@ -10,12 +12,28 @@ import (
"github.com/gin-gonic/gin" "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, func GenerateTextOtherInfo(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, modelRatio, groupRatio, completionRatio float64,
cacheTokens int, cacheRatio float64, modelPrice float64, userGroupRatio float64) map[string]interface{} { cacheTokens int, cacheRatio float64, modelPrice float64, userGroupRatio float64) map[string]interface{} {
other := make(map[string]interface{}) other := make(map[string]interface{})
if relayInfo != nil && relayInfo.RelayFormat != "" {
other["relay_format"] = string(relayInfo.RelayFormat)
}
other["model_ratio"] = modelRatio other["model_ratio"] = modelRatio
other["group_ratio"] = groupRatio other["group_ratio"] = groupRatio
other["completion_ratio"] = completionRatio 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) adminInfo["multi_key_index"] = common.GetContextKeyInt(ctx, constant.ContextKeyChannelMultiKeyIndex)
} }
other["admin_info"] = adminInfo other["admin_info"] = adminInfo
appendRequestPath(ctx, relayInfo, other)
return 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{} { func GenerateMjOtherInfo(relayInfo *relaycommon.RelayInfo, priceData types.PerCallPriceData) map[string]interface{} {
other := make(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["model_price"] = priceData.ModelPrice
other["group_ratio"] = priceData.GroupRatioInfo.GroupRatio other["group_ratio"] = priceData.GroupRatioInfo.GroupRatio
if priceData.GroupRatioInfo.HasSpecialRatio { if priceData.GroupRatioInfo.HasSpecialRatio {
other["user_group_ratio"] = priceData.GroupRatioInfo.GroupSpecialRatio other["user_group_ratio"] = priceData.GroupRatioInfo.GroupSpecialRatio
} }
appendRequestPath(nil, relayInfo, other)
return other return other
} }

View File

@ -103,36 +103,98 @@ function renderType(type, t) {
} }
} }
const relayFormatMeta = { const endpointColorMap = {
openai: { color: 'blue', label: 'OpenAI' }, chat: 'blue',
claude: { color: 'purple', label: 'Claude' }, completions: 'blue',
gemini: { color: 'orange', label: 'Gemini' }, messages: 'purple',
openai_responses: { color: 'violet', label: 'Responses' }, responses: 'violet',
openai_audio: { color: 'teal', label: 'Audio' }, images: 'pink',
openai_image: { color: 'pink', label: 'Image' }, image: 'pink',
openai_realtime: { color: 'indigo', label: 'Realtime' }, embeddings: 'green',
rerank: { color: 'cyan', label: 'Rerank' }, embedding: 'green',
embedding: { color: 'green', label: 'Embedding' }, audio: 'teal',
task: { color: 'amber', label: 'Task' }, speech: 'teal',
mj_proxy: { color: 'red', label: 'Midjourney' }, 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) { function formatPathSegment(segment) {
if (!relayFormat) { 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; return null;
} }
const meta = relayFormatMeta[relayFormat] || { const cleanPath = path.split('?')[0];
color: 'grey', const segments = cleanPath.split('/').filter(Boolean);
label: relayFormat if (segments.length === 0) {
.replace(/_/g, ' ') return null;
.replace(/\b\w/g, (c) => c.toUpperCase()), }
}; let startIndex = 0;
if (/^v\d/i.test(segments[0])) {
startIndex = 1;
}
return ( 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,
};
}
function renderEndpointTag(requestPath) {
const meta = deriveEndpointMeta(requestPath);
if (!meta) {
return null;
}
const tag = (
<Tag color={meta.color} type='light' shape='circle' size='small'> <Tag color={meta.color} type='light' shape='circle' size='small'>
{meta.label} {meta.label}
</Tag> </Tag>
); );
if (requestPath) {
return <Tooltip content={requestPath}>{tag}</Tooltip>;
}
return tag;
} }
function renderIsStream(bool, t) { function renderIsStream(bool, t) {
@ -403,11 +465,12 @@ export const getLogsColumns = ({
title: t('类型'), title: t('类型'),
dataIndex: 'type', dataIndex: 'type',
render: (text, record, index) => { render: (text, record, index) => {
const relayFormat = getLogOther(record.other)?.relay_format; const other = getLogOther(record.other) || {};
const requestPath = other.request_path;
return ( return (
<Space size='small' wrap> <Space size='small' wrap>
{renderType(text, t)} {renderType(text, t)}
{renderRelayFormat(relayFormat)} {renderEndpointTag(requestPath)}
</Space> </Space>
); );
}, },