feat: add icon for ai provider (#12722)
@@ -200,8 +200,13 @@ export const updateAgentAccount = (req: AI.AgentAccountUpdateReq) => {
|
||||
return http.post(`/ai/agents/accounts/update`, req);
|
||||
};
|
||||
|
||||
export const pageAgentAccounts = (req: AI.AgentAccountSearch) => {
|
||||
return http.post<ResPage<AI.AgentAccountItem>>(`/ai/agents/accounts/search`, req);
|
||||
export const pageAgentAccounts = (req: AI.AgentAccountSearch, currentNode?: string) => {
|
||||
return http.post<ResPage<AI.AgentAccountItem>>(
|
||||
`/ai/agents/accounts/search`,
|
||||
req,
|
||||
undefined,
|
||||
currentNode ? { CurrentNode: currentNode } : undefined,
|
||||
);
|
||||
};
|
||||
|
||||
export const getAgentAccountModels = (req: AI.AgentAccountModelReq) => {
|
||||
|
||||
BIN
frontend/src/assets/images/ai-providers/aliyun.webp
Normal file
|
After Width: | Height: | Size: 281 B |
BIN
frontend/src/assets/images/ai-providers/anthropic.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
frontend/src/assets/images/ai-providers/deepseek.png
Normal file
|
After Width: | Height: | Size: 908 B |
BIN
frontend/src/assets/images/ai-providers/gemini.webp
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
4
frontend/src/assets/images/ai-providers/kimi.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M21.7202 0.939941C22.9502 0.939941 23.9502 1.93994 23.9502 3.16994C23.9502 4.39994 22.9502 5.39994 21.7202 5.39994H19.7502C19.6002 5.39994 19.4902 5.27994 19.4902 5.13994V3.16994C19.4902 1.93994 20.4902 0.939941 21.7202 0.939941Z" fill="#1783FF"/>
|
||||
<path d="M9.39 13.9501L17.82 5.59012C17.98 5.43012 17.89 5.12012 17.68 5.12012H13.14C13.14 5.12012 13.04 5.14012 13 5.18012L3.92 14.1901C3.78 14.3301 3.57 14.2101 3.57 13.9801V5.39012C3.57 5.24012 3.47 5.12012 3.35 5.12012H0.219999C0.0999993 5.12012 0 5.24012 0 5.39012V23.9201C0 24.0701 0.0999993 24.1901 0.219999 24.1901H3.35C3.47 24.1901 3.57 24.0701 3.57 23.9201V20.1401C3.57 20.0601 3.6 19.9801 3.65 19.9301L6.47 17.1401C6.54 17.0701 6.63 17.0601 6.71 17.1101L14.24 22.6501C15.47 23.4801 16.85 23.9901 18.25 24.1401C18.37 24.1501 18.48 24.0301 18.48 23.8701V20.3101C18.48 20.1701 18.4 20.0601 18.29 20.0501C17.47 19.9201 16.66 19.6001 15.94 19.1101L9.42 14.3901C9.28 14.3001 9.27 14.0701 9.39 13.9501Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
BIN
frontend/src/assets/images/ai-providers/minimax.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
frontend/src/assets/images/ai-providers/ollama.webp
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
1
frontend/src/assets/images/ai-providers/openai.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M11.248 18.25q-.825 0-1.568-.314a4.3 4.3 0 0 1-1.32-.874 4 4 0 0 1-1.304.214 4 4 0 0 1-2.046-.544 4.27 4.27 0 0 1-1.518-1.485 4 4 0 0 1-.56-2.095q0-.48.131-1.04A4.4 4.4 0 0 1 2.04 10.71a4.07 4.07 0 0 1 .017-3.4 4.2 4.2 0 0 1 1.056-1.418 3.8 3.8 0 0 1 1.6-.842 3.9 3.9 0 0 1 .76-1.683q.593-.759 1.451-1.188a4.04 4.04 0 0 1 1.832-.429q.825 0 1.567.313.742.314 1.32.875a4 4 0 0 1 1.304-.215q1.106 0 2.046.545a4.14 4.14 0 0 1 1.501 1.485q.578.941.578 2.095 0 .48-.132 1.04.66.61 1.023 1.419.363.792.363 1.666 0 .892-.38 1.717a4.3 4.3 0 0 1-1.072 1.435 3.8 3.8 0 0 1-1.584.825 3.8 3.8 0 0 1-.775 1.683 4.06 4.06 0 0 1-1.436 1.188 4.04 4.04 0 0 1-1.832.429m-4.076-2.062q.825 0 1.435-.347l3.103-1.782a.36.36 0 0 0 .164-.313v-1.42L7.881 14.62a.67.67 0 0 1-.726 0l-3.118-1.798a.5.5 0 0 1-.017.115v.198q0 .841.396 1.551.413.693 1.139 1.089a3.2 3.2 0 0 0 1.617.412m.165-2.69a.4.4 0 0 0 .181.05q.083 0 .165-.05l1.238-.71-3.977-2.31a.7.7 0 0 1-.363-.643v-3.58q-.825.362-1.32 1.122a2.9 2.9 0 0 0-.495 1.65q0 .809.413 1.55.412.743 1.072 1.123zm3.91 3.663q.875 0 1.585-.396a2.96 2.96 0 0 0 1.534-2.64v-3.564a.32.32 0 0 0-.165-.297l-1.254-.726v4.604a.7.7 0 0 1-.363.643l-3.119 1.799a3 3 0 0 0 1.783.577m.627-6.039V8.878L10.01 7.822 8.129 8.878v2.244l1.881 1.056zM7.057 5.859a.7.7 0 0 1 .363-.644l3.119-1.798a3 3 0 0 0-1.782-.578q-.874 0-1.584.396A2.96 2.96 0 0 0 6.05 4.324a3.07 3.07 0 0 0-.396 1.551v3.547q0 .199.165.314l1.237.726zm8.383 7.887q.825-.364 1.303-1.123.495-.758.495-1.65a3.15 3.15 0 0 0-.412-1.55q-.413-.743-1.073-1.123l-3.086-1.782q-.099-.065-.181-.049a.3.3 0 0 0-.165.05l-1.238.692 3.993 2.327a.6.6 0 0 1 .264.264.64.64 0 0 1 .1.363zm-3.317-8.382a.63.63 0 0 1 .726 0l3.135 1.831v-.297q0-.792-.396-1.501a2.86 2.86 0 0 0-1.105-1.155q-.71-.43-1.65-.43-.825 0-1.436.347L8.294 5.941a.36.36 0 0 0-.165.314v1.418z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
BIN
frontend/src/assets/images/ai-providers/openrouter.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
1
frontend/src/assets/images/ai-providers/vllm.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg version="1.1" viewBox="0.0 0.0 96.0 96.0" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><clipPath id="g31e21232314_0_33.0"><path d="m0 0l96.0 0l0 96.0l-96.0 0l0 -96.0z" clip-rule="nonzero"/></clipPath><g clip-path="url(#g31e21232314_0_33.0)"><path fill="#d9d9d9" d="m41.04961 80.271324l1.8897629 0l0 2.3307114l-1.8897629 0z" fill-rule="evenodd"/><path fill="#d9d9d9" d="m42.221855 81.45145l1.8897629 0l0 2.3307037l-1.8897629 0z" fill-rule="evenodd"/><g filter="url(#shadowFilter-g31e21232314_0_33.1)"><use xlink:href="#g31e21232314_0_33.1" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-g31e21232314_0_33.1" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.0" intercept="0"/></feComponentTransfer></filter></defs><g id="g31e21232314_0_33.1"><path fill="#d9d9d9" d="m42.22417 28.470434l0 55.307083l-27.653543 -55.307083z" fill-rule="evenodd"/></g><g filter="url(#shadowFilter-g31e21232314_0_33.2)"><use xlink:href="#g31e21232314_0_33.2" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-g31e21232314_0_33.2" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.0" intercept="0"/></feComponentTransfer></filter></defs><g id="g31e21232314_0_33.2"><path fill="#d9d9d9" d="m42.223038 83.77752l21.729656 0l18.653545 -70.385826l-25.574802 13.461943z" fill-rule="evenodd"/></g><g filter="url(#shadowFilter-g31e21232314_0_33.3)"><use xlink:href="#g31e21232314_0_33.3" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-g31e21232314_0_33.3" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.0" intercept="0"/></feComponentTransfer></filter></defs><g id="g31e21232314_0_33.3"><path fill="#fdb515" d="m41.0477 27.293962l0 55.30709l-27.653542 -55.30709z" fill-rule="evenodd"/></g><g filter="url(#shadowFilter-g31e21232314_0_33.4)"><use xlink:href="#g31e21232314_0_33.4" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-g31e21232314_0_33.4" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.0" intercept="0"/></feComponentTransfer></filter></defs><g id="g31e21232314_0_33.4"><path fill="#30a2ff" d="m41.046566 82.60105l21.72966 0l18.653545 -70.385826l-25.574806 13.461943z" fill-rule="evenodd"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
BIN
frontend/src/assets/images/ai-providers/volcengine.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
frontend/src/assets/images/ai-providers/xiaomi.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
frontend/src/assets/images/ai-providers/zai.webp
Normal file
|
After Width: | Height: | Size: 992 B |
56
frontend/src/components/agent-provider-logo/index.vue
Normal file
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<span class="agent-provider-logo" :style="logoStyle" :aria-label="`${displayName || provider} logo`" role="img">
|
||||
<img v-if="logo.src" class="agent-provider-logo__image" :src="logo.src" :alt="displayName || provider" />
|
||||
<span v-else>{{ logo.mark }}</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { getAgentProviderLogo } from '@/utils/agent-provider-logo';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
provider: string;
|
||||
displayName?: string;
|
||||
size?: number;
|
||||
}>(),
|
||||
{
|
||||
displayName: '',
|
||||
size: 16,
|
||||
},
|
||||
);
|
||||
|
||||
const logo = computed(() => getAgentProviderLogo(props.provider, props.displayName));
|
||||
const logoStyle = computed(() => ({
|
||||
width: `${props.size}px`,
|
||||
height: `${props.size}px`,
|
||||
minWidth: `${props.size}px`,
|
||||
background: logo.value.background,
|
||||
color: logo.value.color,
|
||||
fontSize: `${Math.max(10, Math.floor(props.size * 0.48))}px`,
|
||||
}));
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.agent-provider-logo {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
letter-spacing: 0;
|
||||
user-select: none;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
vertical-align: middle;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.agent-provider-logo__image {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
</style>
|
||||
@@ -1,14 +1,9 @@
|
||||
<template>
|
||||
<MdEditor
|
||||
previewOnly
|
||||
:model-value="props.content"
|
||||
:sanitize="sanitizeHtml"
|
||||
:theme="isDarkTheme ? 'dark' : 'light'"
|
||||
/>
|
||||
<MdPreview :model-value="props.content" :sanitize="sanitizeHtml" :theme="isDarkTheme ? 'dark' : 'light'" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { MdEditor } from 'md-editor-v3';
|
||||
import { MdPreview } from 'md-editor-v3';
|
||||
import 'md-editor-v3/lib/style.css';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
|
||||
@@ -974,6 +974,7 @@ const message = {
|
||||
failedRequests: 'Failed Requests',
|
||||
averageTokenPerRequest: 'Avg Tokens/Request',
|
||||
callLogs: 'Call Logs',
|
||||
usageKeywordPlaceholder: 'Search Request ID / model / upstream model / error message',
|
||||
attemptChain: 'Attempt Chain',
|
||||
attemptIndex: 'Call Order',
|
||||
noAttemptDetails: 'This request hit once; no fallback details',
|
||||
|
||||
@@ -988,6 +988,7 @@ const message = {
|
||||
failedRequests: 'Solicitudes fallidas',
|
||||
averageTokenPerRequest: 'Tokens promedio/solicitud',
|
||||
callLogs: 'Registros de llamadas',
|
||||
usageKeywordPlaceholder: 'Buscar Request ID / modelo / modelo upstream / mensaje de error',
|
||||
attemptChain: 'Cadena de intentos',
|
||||
attemptIndex: 'Orden de llamada',
|
||||
noAttemptDetails: 'Esta solicitud acertó en el primer intento; no hay detalles de fallback',
|
||||
|
||||
@@ -978,6 +978,7 @@ const message = {
|
||||
failedRequests: '失敗リクエスト',
|
||||
averageTokenPerRequest: '平均 Token/リクエスト',
|
||||
callLogs: '呼び出しログ',
|
||||
usageKeywordPlaceholder: 'Request ID / モデル / 上流モデル / エラーメッセージを検索',
|
||||
attemptChain: '試行チェーン',
|
||||
attemptIndex: '呼び出し順',
|
||||
noAttemptDetails: 'このリクエストは 1 回で命中しました。fallback 明細はありません',
|
||||
|
||||
@@ -962,6 +962,7 @@ const message = {
|
||||
failedRequests: '실패한 요청',
|
||||
averageTokenPerRequest: '평균 Token/요청',
|
||||
callLogs: '호출 로그',
|
||||
usageKeywordPlaceholder: 'Request ID / 모델 / 업스트림 모델 / 오류 메시지 검색',
|
||||
attemptChain: '시도 체인',
|
||||
attemptIndex: '호출 순서',
|
||||
noAttemptDetails: '이 요청은 한 번에 적중했으며 fallback 상세가 없습니다',
|
||||
|
||||
@@ -987,6 +987,7 @@ const message = {
|
||||
failedRequests: 'Permintaan gagal',
|
||||
averageTokenPerRequest: 'Purata Token/permintaan',
|
||||
callLogs: 'Log panggilan',
|
||||
usageKeywordPlaceholder: 'Cari Request ID / model / model upstream / mesej ralat',
|
||||
attemptChain: 'Rantaian percubaan',
|
||||
attemptIndex: 'Urutan panggilan',
|
||||
noAttemptDetails: 'Permintaan ini berjaya sekali percubaan; tiada butiran fallback',
|
||||
|
||||
@@ -983,6 +983,7 @@ const message = {
|
||||
failedRequests: 'Requisições com falha',
|
||||
averageTokenPerRequest: 'Média de Token/requisição',
|
||||
callLogs: 'Logs de chamadas',
|
||||
usageKeywordPlaceholder: 'Pesquisar Request ID / modelo / modelo upstream / mensagem de erro',
|
||||
attemptChain: 'Cadeia de tentativas',
|
||||
attemptIndex: 'Ordem de chamada',
|
||||
noAttemptDetails: 'Esta requisição acertou na primeira tentativa; sem detalhes de fallback',
|
||||
|
||||
@@ -977,6 +977,7 @@ const message = {
|
||||
failedRequests: 'Неуспешные запросы',
|
||||
averageTokenPerRequest: 'Среднее Token/запрос',
|
||||
callLogs: 'Журнал вызовов',
|
||||
usageKeywordPlaceholder: 'Поиск по Request ID / модели / upstream-модели / сообщению об ошибке',
|
||||
attemptChain: 'Цепочка попыток',
|
||||
attemptIndex: 'Порядок вызова',
|
||||
noAttemptDetails: 'Запрос выполнен с первой попытки, деталей fallback нет',
|
||||
|
||||
@@ -985,6 +985,7 @@ const message = {
|
||||
failedRequests: 'Başarısız istekler',
|
||||
averageTokenPerRequest: 'Ortalama Token/istek',
|
||||
callLogs: 'Çağrı günlükleri',
|
||||
usageKeywordPlaceholder: 'Request ID / model / upstream model / hata mesajı ara',
|
||||
attemptChain: 'Deneme zinciri',
|
||||
attemptIndex: 'Çağrı sırası',
|
||||
noAttemptDetails: 'Bu istek tek denemede isabet etti; fallback ayrıntısı yok',
|
||||
|
||||
@@ -922,6 +922,7 @@ const message = {
|
||||
failedRequests: '失敗請求',
|
||||
averageTokenPerRequest: '平均 Token/請求',
|
||||
callLogs: '呼叫日誌',
|
||||
usageKeywordPlaceholder: '搜尋 Request ID / 模型 / 上游模型 / 錯誤資訊',
|
||||
attemptChain: '呼叫鏈路',
|
||||
attemptIndex: '調用順序',
|
||||
noAttemptDetails: '本次請求一次命中,無 fallback 明細',
|
||||
|
||||
@@ -916,6 +916,7 @@ const message = {
|
||||
failedRequests: '失败请求',
|
||||
averageTokenPerRequest: '平均 Token/请求',
|
||||
callLogs: '调用日志',
|
||||
usageKeywordPlaceholder: '搜索 Request ID / 模型 / 上游模型 / 错误信息',
|
||||
attemptChain: '调用链路',
|
||||
attemptIndex: '调用顺序',
|
||||
noAttemptDetails: '本次请求一次命中,无 fallback 明细',
|
||||
|
||||
168
frontend/src/utils/agent-provider-logo.ts
Normal file
@@ -0,0 +1,168 @@
|
||||
export interface AgentProviderLogo {
|
||||
src?: string;
|
||||
mark: string;
|
||||
background: string;
|
||||
color: string;
|
||||
borderColor?: string;
|
||||
source?: string;
|
||||
}
|
||||
|
||||
const asset = (name: string) => new URL(`../assets/images/ai-providers/${name}`, import.meta.url).href;
|
||||
|
||||
const providerLogos: Record<string, AgentProviderLogo> = {
|
||||
ollama: {
|
||||
src: asset('ollama.webp'),
|
||||
mark: 'OL',
|
||||
background: '#ffffff',
|
||||
color: '#111827',
|
||||
borderColor: '#dcdfe6',
|
||||
source: 'https://github.com/ollama/ollama/blob/main/docs/ollama-logo.svg',
|
||||
},
|
||||
vllm: {
|
||||
src: asset('vllm.svg'),
|
||||
mark: 'v',
|
||||
background: '#ffffff',
|
||||
color: '#4338ca',
|
||||
borderColor: '#dcdfe6',
|
||||
source: 'https://vllm.ai/vLLM-Logo.svg',
|
||||
},
|
||||
deepseek: {
|
||||
src: asset('deepseek.png'),
|
||||
mark: 'DS',
|
||||
background: '#ffffff',
|
||||
color: '#4d6bfe',
|
||||
borderColor: '#dcdfe6',
|
||||
source: 'https://cdn.deepseek.com/logo.png',
|
||||
},
|
||||
'bailian-coding-plan': {
|
||||
src: asset('aliyun.webp'),
|
||||
mark: 'QW',
|
||||
background: '#ffffff',
|
||||
color: '#ff6a00',
|
||||
borderColor: '#dcdfe6',
|
||||
source: 'https://www.alibabacloud.com/',
|
||||
},
|
||||
'ark-coding-plan': {
|
||||
src: asset('volcengine.png'),
|
||||
mark: 'AR',
|
||||
background: '#ffffff',
|
||||
color: '#1664ff',
|
||||
borderColor: '#dcdfe6',
|
||||
source: 'https://www.volcengine.com/',
|
||||
},
|
||||
zai: {
|
||||
src: asset('zai.webp'),
|
||||
mark: 'Z',
|
||||
background: '#ffffff',
|
||||
color: '#111827',
|
||||
borderColor: '#dcdfe6',
|
||||
source: 'https://z.ai/',
|
||||
},
|
||||
minimax: {
|
||||
src: asset('minimax.ico'),
|
||||
mark: 'MM',
|
||||
background: '#ffffff',
|
||||
color: '#2563eb',
|
||||
borderColor: '#dcdfe6',
|
||||
source: 'https://www.minimax.io/',
|
||||
},
|
||||
xiaomi: {
|
||||
src: asset('xiaomi.ico'),
|
||||
mark: 'MI',
|
||||
background: '#ffffff',
|
||||
color: '#ff6900',
|
||||
borderColor: '#dcdfe6',
|
||||
source: 'https://www.mi.com/favicon.ico',
|
||||
},
|
||||
kimi: {
|
||||
src: asset('kimi.svg'),
|
||||
mark: 'KM',
|
||||
background: '#ffffff',
|
||||
color: '#111827',
|
||||
borderColor: '#dcdfe6',
|
||||
source: 'https://moonshotai.github.io/Branding-Guide/',
|
||||
},
|
||||
'kimi-coding': {
|
||||
src: asset('kimi.svg'),
|
||||
mark: 'KC',
|
||||
background: '#ffffff',
|
||||
color: '#111827',
|
||||
borderColor: '#dcdfe6',
|
||||
source: 'https://moonshotai.github.io/Branding-Guide/',
|
||||
},
|
||||
openai: {
|
||||
src: asset('openai.svg'),
|
||||
mark: 'AI',
|
||||
background: '#ffffff',
|
||||
color: '#111827',
|
||||
borderColor: '#dcdfe6',
|
||||
source: 'https://openai.com/brand/',
|
||||
},
|
||||
openrouter: {
|
||||
src: asset('openrouter.png'),
|
||||
mark: 'OR',
|
||||
background: '#ffffff',
|
||||
color: '#101828',
|
||||
borderColor: '#dcdfe6',
|
||||
source: 'https://openrouter.ai/',
|
||||
},
|
||||
anthropic: {
|
||||
src: asset('anthropic.png'),
|
||||
mark: 'A',
|
||||
background: '#ffffff',
|
||||
color: '#141413',
|
||||
borderColor: '#dcdfe6',
|
||||
source: 'https://www.anthropic.com/',
|
||||
},
|
||||
gemini: {
|
||||
src: asset('gemini.webp'),
|
||||
mark: 'G',
|
||||
background: '#ffffff',
|
||||
color: '#1a73e8',
|
||||
borderColor: '#dcdfe6',
|
||||
source: 'https://gemini.google.com/',
|
||||
},
|
||||
moonshot: {
|
||||
src: asset('kimi.svg'),
|
||||
mark: 'MS',
|
||||
background: '#ffffff',
|
||||
color: '#111827',
|
||||
borderColor: '#dcdfe6',
|
||||
source: 'https://moonshotai.github.io/Branding-Guide/',
|
||||
},
|
||||
};
|
||||
|
||||
const fallbackLogo: AgentProviderLogo = {
|
||||
mark: 'AI',
|
||||
background: '#f5f7fa',
|
||||
color: '#606266',
|
||||
borderColor: '#dcdfe6',
|
||||
};
|
||||
|
||||
const normalizeProvider = (provider?: string) =>
|
||||
String(provider || '')
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
|
||||
const buildFallbackMark = (provider?: string, displayName?: string) => {
|
||||
const source = String(displayName || provider || '').trim();
|
||||
const asciiWords = source.match(/[a-zA-Z0-9]+/g) || [];
|
||||
if (asciiWords.length >= 2) {
|
||||
return `${asciiWords[0][0]}${asciiWords[1][0]}`.toUpperCase();
|
||||
}
|
||||
if (asciiWords.length === 1) {
|
||||
return asciiWords[0].slice(0, 2).toUpperCase();
|
||||
}
|
||||
return fallbackLogo.mark;
|
||||
};
|
||||
|
||||
export const getAgentProviderLogo = (provider?: string, displayName?: string): AgentProviderLogo => {
|
||||
const logo = providerLogos[normalizeProvider(provider)];
|
||||
if (logo) {
|
||||
return logo;
|
||||
}
|
||||
return {
|
||||
...fallbackLogo,
|
||||
mark: buildFallbackMark(provider, displayName),
|
||||
};
|
||||
};
|
||||
@@ -11,7 +11,12 @@
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
>
|
||||
<div class="provider-option">
|
||||
<ProviderLogo :provider="item.value" :display-name="item.label" />
|
||||
<span>{{ item.label }}</span>
|
||||
</div>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="API Key" prop="apiKey">
|
||||
@@ -88,6 +93,7 @@ import i18n from '@/lang';
|
||||
import { getAgentProviderDisplayName } from '@/utils/agent';
|
||||
import { MsgError } from '@/utils/message';
|
||||
import { useGlobalStore } from '@/composables/useGlobalStore';
|
||||
import ProviderLogo from '@/components/agent-provider-logo/index.vue';
|
||||
|
||||
const emit = defineEmits(['search']);
|
||||
|
||||
@@ -355,3 +361,19 @@ defineExpose({
|
||||
open: openDrawer,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.provider-option {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
white-space: nowrap;
|
||||
|
||||
span {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -15,7 +15,13 @@
|
||||
<el-table-column :label="$t('commons.table.name')" prop="name" min-width="200" />
|
||||
<el-table-column :label="$t('aiTools.agents.provider')" prop="provider" width="200">
|
||||
<template #default="{ row }">
|
||||
{{ getAgentProviderDisplayName(row.provider, row.providerName) }}
|
||||
<div class="provider-cell">
|
||||
<ProviderLogo
|
||||
:provider="row.provider"
|
||||
:display-name="getAgentProviderDisplayName(row.provider, row.providerName)"
|
||||
/>
|
||||
<span>{{ getAgentProviderDisplayName(row.provider, row.providerName) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Base URL" prop="baseUrl" min-width="200" />
|
||||
@@ -57,6 +63,7 @@ import { deleteAgentAccount, pageAgentAccounts } from '@/api/modules/ai';
|
||||
import { AI } from '@/api/interface/ai';
|
||||
import AddDialog from '@/views/ai/agents/model/add/index.vue';
|
||||
import ModelPoolDialog from '@/views/ai/agents/model/pool/index.vue';
|
||||
import ProviderLogo from '@/components/agent-provider-logo/index.vue';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import i18n from '@/lang';
|
||||
import { dateFormat } from '@/utils/date';
|
||||
@@ -177,3 +184,19 @@ onMounted(async () => {
|
||||
await search();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.provider-cell {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
white-space: nowrap;
|
||||
|
||||
span {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||