Commit f2bca12d by Apple\Apple

feat(ui): Enhance model tag rendering in logs table with icons

Improve the logs table by implementing brand-specific model icons and better
redirection visualization:

- Replace standard tags with `renderModelTag` to display appropriate brand
  icons for each model (OpenAI, Claude, Gemini, etc.)
- Fix background colors by explicitly passing color parameters
- Restore descriptive text for model redirection in popover
- Replace refresh icon with forward icon for better representation of model
  redirection
- Clean up unused imports

This change provides a more intuitive visual representation of models and
their relationships, making the logs table easier to understand at a glance.
parent 56491f4d
import React, { useContext, useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import {
API, API,
...@@ -19,7 +19,8 @@ import { ...@@ -19,7 +19,8 @@ import {
renderNumber, renderNumber,
renderQuota, renderQuota,
stringToColor, stringToColor,
getLogOther getLogOther,
renderModelTag
} from '../../helpers'; } from '../../helpers';
import { import {
...@@ -39,18 +40,14 @@ import { ...@@ -39,18 +40,14 @@ import {
Typography, Typography,
Divider, Divider,
Input, Input,
DatePicker, DatePicker
} from '@douyinfe/semi-ui'; } from '@douyinfe/semi-ui';
import { ITEMS_PER_PAGE } from '../../constants'; import { ITEMS_PER_PAGE } from '../../constants';
import Paragraph from '@douyinfe/semi-ui/lib/es/typography/paragraph'; import Paragraph from '@douyinfe/semi-ui/lib/es/typography/paragraph';
import { import {
IconRefresh,
IconSetting, IconSetting,
IconEyeOpened,
IconSearch, IconSearch,
IconCoinMoneyStroked, IconForward
IconPulse,
IconTypograph,
} from '@douyinfe/semi-icons'; } from '@douyinfe/semi-icons';
const { Text } = Typography; const { Text } = Typography;
...@@ -202,19 +199,12 @@ const LogsTable = () => { ...@@ -202,19 +199,12 @@ const LogsTable = () => {
other?.upstream_model_name && other?.upstream_model_name &&
other?.upstream_model_name !== ''; other?.upstream_model_name !== '';
if (!modelMapped) { if (!modelMapped) {
return ( return renderModelTag(record.model_name, {
<Tag color: stringToColor(record.model_name),
color={stringToColor(record.model_name)} onClick: (event) => {
size='large'
shape='circle'
onClick={(event) => {
copyText(event, record.model_name).then((r) => { }); copyText(event, record.model_name).then((r) => { });
}} }
> });
{' '}
{record.model_name}{' '}
</Tag>
);
} else { } else {
return ( return (
<> <>
...@@ -223,48 +213,35 @@ const LogsTable = () => { ...@@ -223,48 +213,35 @@ const LogsTable = () => {
content={ content={
<div style={{ padding: 10 }}> <div style={{ padding: 10 }}>
<Space vertical align={'start'}> <Space vertical align={'start'}>
<Tag <div className="flex items-center">
color={stringToColor(record.model_name)} <Text strong style={{ marginRight: 8 }}>{t('请求并计费模型')}:</Text>
size='large' {renderModelTag(record.model_name, {
shape='circle' color: stringToColor(record.model_name),
onClick={(event) => { onClick: (event) => {
copyText(event, record.model_name).then((r) => { }); copyText(event, record.model_name).then((r) => { });
}} }
> })}
{t('请求并计费模型')} {record.model_name}{' '} </div>
</Tag> <div className="flex items-center">
<Tag <Text strong style={{ marginRight: 8 }}>{t('实际模型')}:</Text>
color={stringToColor(other.upstream_model_name)} {renderModelTag(other.upstream_model_name, {
size='large' color: stringToColor(other.upstream_model_name),
shape='circle' onClick: (event) => {
onClick={(event) => { copyText(event, other.upstream_model_name).then((r) => { });
copyText(event, other.upstream_model_name).then( }
(r) => { }, })}
); </div>
}}
>
{t('实际模型')} {other.upstream_model_name}{' '}
</Tag>
</Space> </Space>
</div> </div>
} }
> >
<Tag {renderModelTag(record.model_name, {
color={stringToColor(record.model_name)} color: stringToColor(record.model_name),
size='large' onClick: (event) => {
shape='circle'
onClick={(event) => {
copyText(event, record.model_name).then((r) => { }); copyText(event, record.model_name).then((r) => { });
}} },
suffixIcon={ suffixIcon: <IconForward style={{ width: '0.9em', height: '0.9em', opacity: 0.75 }} />
<IconRefresh })}
style={{ width: '0.8em', height: '0.8em', opacity: 0.6 }}
/>
}
>
{' '}
{record.model_name}{' '}
</Tag>
</Popover> </Popover>
</Space> </Space>
</> </>
......
import React, { useContext, useEffect, useRef, useMemo, useState } from 'react'; import React, { useContext, useEffect, useRef, useMemo, useState } from 'react';
import { API, copy, showError, showInfo, showSuccess } from '../../helpers/index.js'; import { API, copy, showError, showInfo, showSuccess, getModelCategories } from '../../helpers/index.js';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import {
...@@ -28,7 +28,6 @@ import { ...@@ -28,7 +28,6 @@ import {
} from '@douyinfe/semi-icons'; } from '@douyinfe/semi-icons';
import { UserContext } from '../../context/User/index.js'; import { UserContext } from '../../context/User/index.js';
import { AlertCircle } from 'lucide-react'; import { AlertCircle } from 'lucide-react';
import { MODEL_CATEGORIES } from '../../constants/index.js';
const ModelPricing = () => { const ModelPricing = () => {
const { t } = useTranslation(); const { t } = useTranslation();
...@@ -321,7 +320,7 @@ const ModelPricing = () => { ...@@ -321,7 +320,7 @@ const ModelPricing = () => {
refresh().then(); refresh().then();
}, []); }, []);
const modelCategories = MODEL_CATEGORIES(t); const modelCategories = getModelCategories(t);
const renderArrow = (items, pos, handleArrowClick) => { const renderArrow = (items, pos, handleArrowClick) => {
const style = { const style = {
......
...@@ -2,5 +2,4 @@ export * from './channel.constants'; ...@@ -2,5 +2,4 @@ export * from './channel.constants';
export * from './user.constants'; export * from './user.constants';
export * from './toast.constants'; export * from './toast.constants';
export * from './common.constant'; export * from './common.constant';
export * from './model.constants';
export * from './playground.constants'; export * from './playground.constants';
import {
OpenAI,
Claude,
Gemini,
Moonshot,
Zhipu,
Qwen,
DeepSeek,
Minimax,
Wenxin,
Spark,
Midjourney,
Hunyuan,
Cohere,
Cloudflare,
Ai360,
Yi,
Jina,
Mistral,
XAI,
Ollama,
Doubao,
} from '@lobehub/icons';
export const MODEL_CATEGORIES = (t) => ({
all: {
label: t('全部模型'),
icon: null,
filter: () => true
},
openai: {
label: 'OpenAI',
icon: <OpenAI />,
filter: (model) => model.model_name.toLowerCase().includes('gpt') ||
model.model_name.toLowerCase().includes('dall-e') ||
model.model_name.toLowerCase().includes('whisper') ||
model.model_name.toLowerCase().includes('tts') ||
model.model_name.toLowerCase().includes('text-') ||
model.model_name.toLowerCase().includes('babbage') ||
model.model_name.toLowerCase().includes('davinci') ||
model.model_name.toLowerCase().includes('curie') ||
model.model_name.toLowerCase().includes('ada')
},
anthropic: {
label: 'Anthropic',
icon: <Claude.Color />,
filter: (model) => model.model_name.toLowerCase().includes('claude')
},
gemini: {
label: 'Gemini',
icon: <Gemini.Color />,
filter: (model) => model.model_name.toLowerCase().includes('gemini')
},
moonshot: {
label: 'Moonshot',
icon: <Moonshot />,
filter: (model) => model.model_name.toLowerCase().includes('moonshot')
},
zhipu: {
label: t('智谱'),
icon: <Zhipu.Color />,
filter: (model) => model.model_name.toLowerCase().includes('chatglm') ||
model.model_name.toLowerCase().includes('glm-')
},
qwen: {
label: t('通义千问'),
icon: <Qwen.Color />,
filter: (model) => model.model_name.toLowerCase().includes('qwen')
},
deepseek: {
label: 'DeepSeek',
icon: <DeepSeek.Color />,
filter: (model) => model.model_name.toLowerCase().includes('deepseek')
},
minimax: {
label: 'MiniMax',
icon: <Minimax.Color />,
filter: (model) => model.model_name.toLowerCase().includes('abab')
},
baidu: {
label: t('文心一言'),
icon: <Wenxin.Color />,
filter: (model) => model.model_name.toLowerCase().includes('ernie')
},
xunfei: {
label: t('讯飞星火'),
icon: <Spark.Color />,
filter: (model) => model.model_name.toLowerCase().includes('spark')
},
midjourney: {
label: 'Midjourney',
icon: <Midjourney />,
filter: (model) => model.model_name.toLowerCase().includes('mj_')
},
tencent: {
label: t('腾讯混元'),
icon: <Hunyuan.Color />,
filter: (model) => model.model_name.toLowerCase().includes('hunyuan')
},
cohere: {
label: 'Cohere',
icon: <Cohere.Color />,
filter: (model) => model.model_name.toLowerCase().includes('command')
},
cloudflare: {
label: 'Cloudflare',
icon: <Cloudflare.Color />,
filter: (model) => model.model_name.toLowerCase().includes('@cf/')
},
ai360: {
label: t('360智脑'),
icon: <Ai360.Color />,
filter: (model) => model.model_name.toLowerCase().includes('360')
},
yi: {
label: t('零一万物'),
icon: <Yi.Color />,
filter: (model) => model.model_name.toLowerCase().includes('yi')
},
jina: {
label: 'Jina',
icon: <Jina />,
filter: (model) => model.model_name.toLowerCase().includes('jina')
},
mistral: {
label: 'Mistral AI',
icon: <Mistral.Color />,
filter: (model) => model.model_name.toLowerCase().includes('mistral')
},
xai: {
label: 'xAI',
icon: <XAI />,
filter: (model) => model.model_name.toLowerCase().includes('grok')
},
llama: {
label: 'Llama',
icon: <Ollama />,
filter: (model) => model.model_name.toLowerCase().includes('llama')
},
doubao: {
label: t('豆包'),
icon: <Doubao.Color />,
filter: (model) => model.model_name.toLowerCase().includes('doubao')
}
});
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment