From e05f85f3ce101087f0776e561a8cb4fe4e9035a3 Mon Sep 17 00:00:00 2001 From: zhayujie Date: Mon, 25 May 2026 15:09:53 +0800 Subject: [PATCH] feat: optimize model name display in English --- channel/web/static/js/console.js | 29 +++++++++++++++++++---------- channel/web/web_channel.py | 18 +++++++++--------- docs/en/models/index.mdx | 2 +- docs/en/releases/v2.0.9.mdx | 2 +- docs/ja/releases/v2.0.9.mdx | 2 +- docs/zh/README.md | 2 +- 6 files changed, 32 insertions(+), 23 deletions(-) diff --git a/channel/web/static/js/console.js b/channel/web/static/js/console.js index 541a63dc..7376e29c 100644 --- a/channel/web/static/js/console.js +++ b/channel/web/static/js/console.js @@ -367,6 +367,15 @@ function t(key) { return (I18N[currentLang] && I18N[currentLang][key]) || (I18N.en[key]) || key; } +// Resolve a localized label that may be either a plain string or +// a {zh, en} object returned by the backend. +function localizedLabel(label) { + if (label && typeof label === 'object') { + return label[currentLang] || label.en || label.zh || ''; + } + return label || ''; +} + function applyI18n() { document.querySelectorAll('[data-i18n]').forEach(el => { el.textContent = t(el.dataset.i18n); @@ -3164,7 +3173,7 @@ function initConfigView(data) { configCurrentModel = data.model || ''; const providerEl = document.getElementById('cfg-provider'); - const providerOpts = Object.entries(configProviders).map(([pid, p]) => ({ value: pid, label: p.label })); + const providerOpts = Object.entries(configProviders).map(([pid, p]) => ({ value: pid, label: localizedLabel(p.label) })); // if use_linkai is enabled, always select linkai as the provider // Otherwise prefer bot_type from config, fall back to model-based detection @@ -3914,7 +3923,7 @@ function renderVendorChip(p) { bg-slate-50 dark:bg-white/5 hover:border-primary-300 dark:hover:border-primary-500/50 cursor-pointer transition-colors duration-150 text-left"> ${renderProviderLogo(p, 28)} - ${escapeHtml(p.label)} + ${escapeHtml(localizedLabel(p.label))} `; } @@ -3922,7 +3931,7 @@ function renderVendorChip(p) { // Render a uniformly-styled logo for a provider. Tries an SVG asset first; if // it 404s the swaps itself for a monogram fallback via onerror. function renderProviderLogo(p, sizePx) { - const initial = (p.label || p.id || '?').slice(0, 1).toUpperCase(); + const initial = (localizedLabel(p.label) || p.id || '?').slice(0, 1).toUpperCase(); const sz = sizePx || 32; const url = `${MODELS_PROVIDER_LOGO_PATH}/${encodeURIComponent(p.id)}.svg`; const fallbackId = `pl-${p.id}-${Math.random().toString(36).slice(2, 8)}`; @@ -3977,7 +3986,7 @@ function renderCapabilityHeaderTag(def, cap) { function _searchProviderLabel(cap, providerId) { const list = (cap && cap.providers) || []; const hit = list.find(p => p.id === providerId); - return hit ? hit.label : providerId; + return hit ? localizedLabel(hit.label) : providerId; } // Search card body: strategy picker + (when fixed) provider picker + a @@ -4103,7 +4112,7 @@ function _renderSearchSummary(body, cap) { class="inline-flex items-center gap-1 px-2 py-0.5 text-[11px] rounded-md cursor-pointer bg-emerald-50 dark:bg-emerald-900/30 text-emerald-600 dark:text-emerald-400 hover:bg-emerald-100 dark:hover:bg-emerald-900/50 transition-colors"> - ${escapeHtml(p.label)} + ${escapeHtml(localizedLabel(p.label))} `).join(''); host.innerHTML = ` @@ -4150,7 +4159,7 @@ function openSearchAddProviderPicker(missingProviders) { class="w-full flex items-center justify-between px-3 py-2.5 rounded-lg cursor-pointer bg-slate-50 dark:bg-white/5 hover:bg-slate-100 dark:hover:bg-white/10 text-sm text-slate-700 dark:text-slate-200 transition-colors"> - ${escapeHtml(p.label)} + ${escapeHtml(localizedLabel(p.label))} `).join(''); @@ -4607,7 +4616,7 @@ function renderCapabilityHints(def, cap, body, currentProvider) { // id ("linkai") when we know it. Falls back to the id when the // provider isn't in our vendor table (rare). const provMeta = modelsState.providers.find(p => p.id === fbProv); - const fbProvLabel = (provMeta && provMeta.label) || fbProv; + const fbProvLabel = (provMeta && localizedLabel(provMeta.label)) || fbProv; const fbText = fbModel ? `${fbProvLabel} / ${fbModel}` : fbProvLabel; slot.innerHTML = `

@@ -4639,7 +4648,7 @@ function buildCapabilityProviderOptions(def, cap) { const configured = !tracked || !!meta.configured; return { value: pid, - label: (meta && meta.label) || pid, + label: (meta && localizedLabel(meta.label)) || pid, _tracked: tracked, _configured: configured, }; @@ -5069,7 +5078,7 @@ function openVendorModal(providerId, onSaved) { const pickerEl = document.getElementById('vendor-modal-picker'); const pickerOpts = modelsState.providers.map(p => ({ value: p.id, - label: p.label, + label: localizedLabel(p.label), _configured: !!p.configured, })); initDropdown(pickerEl, pickerOpts, defaultId, (val) => fillVendorModalForProvider(val)); @@ -5108,7 +5117,7 @@ function openVendorModal(providerId, onSaved) { function fillVendorModalForProvider(providerId) { const meta = modelsState.providers.find(p => p.id === providerId); if (!meta) return; - document.getElementById('vendor-modal-title').textContent = meta.label; + document.getElementById('vendor-modal-title').textContent = localizedLabel(meta.label); document.getElementById('vendor-modal-subtitle').textContent = meta.id; // ----- API Base ----- diff --git a/channel/web/web_channel.py b/channel/web/web_channel.py index ab6599be..371f0271 100644 --- a/channel/web/web_channel.py +++ b/channel/web/web_channel.py @@ -1337,7 +1337,7 @@ class ConfigHandler: "models": [const.GPT_55, const.GPT_54, const.GPT_54_MINI, const.GPT_54_NANO, const.GPT_5, const.GPT_41, const.GPT_4o], }), ("zhipu", { - "label": "智谱AI", + "label": {"zh": "智谱AI", "en": "GLM"}, "api_key_field": "zhipu_ai_api_key", "api_base_key": "zhipu_ai_api_base", "api_base_default": "https://open.bigmodel.cn/api/paas/v4", @@ -1345,7 +1345,7 @@ class ConfigHandler: "models": [const.GLM_5_1, const.GLM_5_TURBO, const.GLM_5, const.GLM_4_7], }), ("dashscope", { - "label": "通义千问", + "label": {"zh": "通义千问", "en": "Qwen"}, "api_key_field": "dashscope_api_key", "api_base_key": None, "api_base_default": None, @@ -1353,7 +1353,7 @@ class ConfigHandler: "models": [const.QWEN36_PLUS, const.QWEN37_MAX, const.QWEN35_PLUS, const.QWEN3_MAX], }), ("doubao", { - "label": "豆包", + "label": {"zh": "豆包", "en": "Doubao"}, "api_key_field": "ark_api_key", "api_base_key": "ark_base_url", "api_base_default": "https://ark.cn-beijing.volces.com/api/v3", @@ -1369,7 +1369,7 @@ class ConfigHandler: "models": [const.KIMI_K2_6, const.KIMI_K2_5, const.KIMI_K2], }), ("qianfan", { - "label": "百度千帆", + "label": {"zh": "百度千帆", "en": "ERNIE"}, "api_key_field": "qianfan_api_key", "api_base_key": "qianfan_api_base", "api_base_default": "https://qianfan.baidubce.com/v2", @@ -1385,7 +1385,7 @@ class ConfigHandler: "models": _RECOMMENDED_MODELS, }), ("custom", { - "label": "自定义", + "label": {"zh": "自定义", "en": "Custom"}, "api_key_field": "custom_api_key", "api_base_key": "custom_api_base", "api_base_default": "", @@ -2241,10 +2241,10 @@ class ModelsHandler: _SEARCH_PROVIDERS = ("bocha", "qianfan", "zhipu", "linkai") _SEARCH_PROVIDER_LABELS = { - "bocha": "博查", - "zhipu": "智谱", - "qianfan": "百度千帆", - "linkai": "LinkAI", + "bocha": {"zh": "博查", "en": "Bocha"}, + "zhipu": {"zh": "智谱", "en": "GLM"}, + "qianfan": {"zh": "百度千帆", "en": "ERNIE"}, + "linkai": {"zh": "LinkAI", "en": "LinkAI"}, } @classmethod diff --git a/docs/en/models/index.mdx b/docs/en/models/index.mdx index fdd59154..9c7afe44 100644 --- a/docs/en/models/index.mdx +++ b/docs/en/models/index.mdx @@ -32,6 +32,6 @@ A snapshot of each vendor's capabilities. "Text" refers to the main chat model; **Option 1 (recommended):** Manage models and capabilities online via the [Web console](/en/channels/web), with no need to edit the configuration file: - + **Option 2:** Edit `config.json` manually and fill in the model name and API key for the selected vendor. Every model also supports OpenAI-compatible access — just set `bot_type` to `openai` and configure `open_ai_api_base` and `open_ai_api_key`. diff --git a/docs/en/releases/v2.0.9.mdx b/docs/en/releases/v2.0.9.mdx index 57d7bfec..ccae36fc 100644 --- a/docs/en/releases/v2.0.9.mdx +++ b/docs/en/releases/v2.0.9.mdx @@ -15,7 +15,7 @@ The Web Console adds a new **Models** page that organizes everything by **provid Documentation: [Models Overview](https://docs.cowagent.ai/en/models) -20260522113305 +20260522113305 ## 🧩 MCP Protocol Support diff --git a/docs/ja/releases/v2.0.9.mdx b/docs/ja/releases/v2.0.9.mdx index d02f0840..003ae6e3 100644 --- a/docs/ja/releases/v2.0.9.mdx +++ b/docs/ja/releases/v2.0.9.mdx @@ -15,7 +15,7 @@ Web コンソールに「モデル」ページを新設。**モデルプロバ ドキュメント:[モデル概要](https://docs.cowagent.ai/ja/models) -20260522113305 +20260522113305 ## 🧩 MCP プロトコル対応 diff --git a/docs/zh/README.md b/docs/zh/README.md index 0d8234e2..63daf22e 100644 --- a/docs/zh/README.md +++ b/docs/zh/README.md @@ -227,7 +227,7 @@ CowAgent 支持国内外主流厂商的大语言模型。**文本对话、图像 ## 🏢 企业服务 - + > [LinkAI](https://link-ai.tech/) 是面向企业和个人的一站式 AI 智能体平台,为 CowAgent 提供云端托管和企业级支持: >