From 984d54880cf92d5a66e62b2677b0db6dbb1ff34c Mon Sep 17 00:00:00 2001 From: highway Date: Tue, 25 Aug 2026 10:23:55 +0800 Subject: [PATCH] =?UTF-8?q?feat(model):=20=E6=97=A5=E5=B8=B8=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E9=BB=98=E8=AE=A4=E6=A8=A1=E5=9E=8B=E5=8F=AF=E7=8B=AC?= =?UTF-8?q?=E7=AB=8B=E9=85=8D=E7=BD=AE-=E9=80=9A=E7=94=A8=E9=9C=80?= =?UTF-8?q?=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/bisheng/common/errcode/llm.py | 5 + src/backend/bisheng/llm/domain/schemas.py | 6 ++ .../bisheng/llm/domain/services/llm.py | 18 ++++ .../test/llm/test_llm_share_fallback.py | 2 + .../test/llm/test_workbench_update_merge.py | 92 ++++++++++++++++++- src/frontend/client/eslint-suppressions.json | 2 +- src/frontend/client/src/api/linsight.ts | 2 + .../client/src/hooks/useChatModelMemo.ts | 29 ++++-- .../client/src/locales/en/api_errors.gen.json | 1 + .../client/src/locales/ja/api_errors.gen.json | 1 + .../src/locales/zh-Hans/api_errors.gen.json | 1 + .../packages/locales/src/api_errors/en.json | 1 + .../packages/locales/src/api_errors/ja.json | 1 + .../locales/src/api_errors/zh-Hans.json | 1 + .../public/locales/en-US/api_errors.json | 1 + .../platform/public/locales/en-US/model.json | 1 + .../public/locales/ja/api_errors.json | 1 + .../platform/public/locales/ja/model.json | 1 + .../public/locales/zh-Hans/api_errors.json | 1 + .../public/locales/zh-Hans/model.json | 1 + .../pages/BuildPage/bench/ModelManagement.tsx | 28 +++++- .../ModelPage/manage/tabs/WorkbenchModel.tsx | 19 +++- 22 files changed, 200 insertions(+), 15 deletions(-) diff --git a/src/backend/bisheng/common/errcode/llm.py b/src/backend/bisheng/common/errcode/llm.py index 2f99025dc..485858f89 100644 --- a/src/backend/bisheng/common/errcode/llm.py +++ b/src/backend/bisheng/common/errcode/llm.py @@ -25,3 +25,8 @@ class ServerAddError(BaseErrorCode): class WorkbenchEmbeddingError(BaseErrorCode): Code: int = 10810 Msg: str = 'Please configure the workbench embedding model.' + + +class WorkbenchChatDefaultModelError(BaseErrorCode): + Code: int = 10811 + Msg: str = 'The daily-chat default model must be one of the configured workbench chat models.' diff --git a/src/backend/bisheng/llm/domain/schemas.py b/src/backend/bisheng/llm/domain/schemas.py index 46b8c6041..cbf565b9f 100644 --- a/src/backend/bisheng/llm/domain/schemas.py +++ b/src/backend/bisheng/llm/domain/schemas.py @@ -86,6 +86,12 @@ class WorkbenchModelConfig(BaseModel): default=None, description="Linsight default execution model id (single-select from models)", ) + # Daily-chat (日常模式) default model — sibling of linsight_default_model_id, + # must be one of the configured ``models`` entries. + chat_default_model_id: str | None = Field( + default=None, + description="Daily-chat default model id (single-select from models)", + ) # RetrieveembeddingModels embedding_model: WSModel | None = Field(default=None, description="embeddingModels") # Speech-to-text model diff --git a/src/backend/bisheng/llm/domain/services/llm.py b/src/backend/bisheng/llm/domain/services/llm.py index c9c499175..3c4a6a0eb 100644 --- a/src/backend/bisheng/llm/domain/services/llm.py +++ b/src/backend/bisheng/llm/domain/services/llm.py @@ -19,6 +19,7 @@ from bisheng.common.errcode.llm import ( ServerAddAllError, ServerAddError, ServerExistError, + WorkbenchChatDefaultModelError, WorkbenchEmbeddingError, ) from bisheng.common.errcode.llm_tenant import ( @@ -115,6 +116,7 @@ def _workbench_model_ref_values(config: WorkbenchModelConfig) -> list[Any]: return [ *(one.id for one in (config.models or [])), config.linsight_default_model_id, + config.chat_default_model_id, getattr(config.embedding_model, "id", None), getattr(config.asr_model, "id", None), getattr(config.tts_model, "id", None), @@ -253,6 +255,7 @@ class LLMService: [ *(one.id for one in (workbench_cfg.models or [])), workbench_cfg.linsight_default_model_id, + workbench_cfg.chat_default_model_id, getattr(workbench_cfg.embedding_model, "id", None), getattr(workbench_cfg.asr_model, "id", None), getattr(workbench_cfg.tts_model, "id", None), @@ -373,6 +376,8 @@ class LLMService: config.models = [one for one in config.models if is_allowed(one.id)] if not is_allowed(config.linsight_default_model_id): config.linsight_default_model_id = None + if not is_allowed(config.chat_default_model_id): + config.chat_default_model_id = None for field_name in ("embedding_model", "asr_model", "tts_model", "chat_title_llm"): ws_model = getattr(config, field_name) if ws_model is not None and not is_allowed(ws_model.id): @@ -1361,6 +1366,7 @@ class LLMService: # linsight_default_model_id is already a model-id string (F035), # not a WSModel; pass it directly alongside the WSModel ids. config_obj.linsight_default_model_id, + config_obj.chat_default_model_id, *( (ws.id if ws else None) for ws in ( @@ -1391,6 +1397,18 @@ class LLMService: if config_obj.models is None: config_obj.models = config_old_obj.models + # chat_default_model_id must come from the configured (and, per the + # avalidate call above, accessible) daily-chat model list. + if config_obj.chat_default_model_id is not None: + chat_default_id = _coerce_model_id(config_obj.chat_default_model_id) + selectable_ids = { + model_id + for model_id in (_coerce_model_id(one.id) for one in (config_obj.models or [])) + if model_id is not None + } + if chat_default_id is None or chat_default_id not in selectable_ids: + raise WorkbenchChatDefaultModelError() + if config_obj.embedding_model: # Determine consistency if ( diff --git a/src/backend/test/llm/test_llm_share_fallback.py b/src/backend/test/llm/test_llm_share_fallback.py index b1b476485..f257922dd 100644 --- a/src/backend/test/llm/test_llm_share_fallback.py +++ b/src/backend/test/llm/test_llm_share_fallback.py @@ -370,6 +370,7 @@ async def test_workbench_getter_sanitizes_inherited_stale_model_refs(): WSModel(id="30", name="deleted-model").model_dump(), ], "linsight_default_model_id": "20", + "chat_default_model_id": "30", "embedding_model": WSModel(id="10", name="root-embedding").model_dump(), "asr_model": WSModel(id="30", name="deleted-asr").model_dump(), "tts_model": WSModel(id="20", name="child-tts").model_dump(), @@ -390,6 +391,7 @@ async def test_workbench_getter_sanitizes_inherited_stale_model_refs(): assert blocked is False assert [one.id for one in (config.models or [])] == ["10"] assert config.linsight_default_model_id is None + assert config.chat_default_model_id is None assert config.embedding_model and config.embedding_model.id == "10" assert config.asr_model is None assert config.tts_model is None diff --git a/src/backend/test/llm/test_workbench_update_merge.py b/src/backend/test/llm/test_workbench_update_merge.py index 3daf7b6c8..689b7733a 100644 --- a/src/backend/test/llm/test_workbench_update_merge.py +++ b/src/backend/test/llm/test_workbench_update_merge.py @@ -17,7 +17,7 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest -from bisheng.common.errcode.llm import WorkbenchEmbeddingError +from bisheng.common.errcode.llm import WorkbenchChatDefaultModelError, WorkbenchEmbeddingError from bisheng.llm.domain.schemas import WorkbenchModelConfig, WSModel from bisheng.llm.domain.services.llm import LLMService @@ -93,3 +93,93 @@ async def test_invalid_embedding_model_id_returns_business_error(invalid_model_i assert exc_info.value.code == 10810 mock_validate.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_chat_default_model_id_persists_alongside_linsight_default(): + """chat_default_model_id is a sibling of linsight_default_model_id — both + land in the same stored JSON without overwriting each other.""" + incoming = WorkbenchModelConfig( + models=[WSModel(id="840", name="qwen3.7-max"), WSModel(id="841", name="deepseek-v4")], + linsight_default_model_id="840", + chat_default_model_id="841", + embedding_model=None, + ) + persisted = await _run_and_capture(incoming) + assert persisted["linsight_default_model_id"] == "840" + assert persisted["chat_default_model_id"] == "841" + + +@pytest.mark.asyncio +async def test_chat_default_model_id_none_is_allowed(): + """No daily-chat default configured is a valid state.""" + incoming = WorkbenchModelConfig( + models=[WSModel(id="840", name="qwen3.7-max")], + chat_default_model_id=None, + embedding_model=None, + ) + persisted = await _run_and_capture(incoming) + assert persisted["chat_default_model_id"] is None + + +@pytest.mark.asyncio +async def test_chat_default_model_id_must_come_from_models_list(): + """A default outside the configured workbench chat model list is rejected.""" + incoming = WorkbenchModelConfig( + models=[WSModel(id="840", name="qwen3.7-max")], + chat_default_model_id="999", + embedding_model=None, + ) + with patch( + "bisheng.llm.domain.services.llm.avalidate_system_model_refs", + new=AsyncMock(), + ), patch( + "bisheng.llm.domain.services.llm.TenantSystemModelConfigDao.aresolve", + new=AsyncMock(return_value=(json.dumps(_OLD.model_dump()), False, False)), + ): + with pytest.raises(WorkbenchChatDefaultModelError) as exc_info: + await LLMService.update_workbench_llm(1, incoming, MagicMock(), tenant_id=1) + + assert exc_info.value.code == 10811 + + +@pytest.mark.parametrize("invalid_model_id", ["", "null", "undefined", "0", "-1", "abc"]) +@pytest.mark.asyncio +async def test_chat_default_model_id_rejects_non_model_values(invalid_model_id: str): + incoming = WorkbenchModelConfig( + models=[WSModel(id="840", name="qwen3.7-max")], + chat_default_model_id=invalid_model_id, + embedding_model=None, + ) + with patch( + "bisheng.llm.domain.services.llm.avalidate_system_model_refs", + new=AsyncMock(), + ), patch( + "bisheng.llm.domain.services.llm.TenantSystemModelConfigDao.aresolve", + new=AsyncMock(return_value=(json.dumps(_OLD.model_dump()), False, False)), + ): + with pytest.raises(WorkbenchChatDefaultModelError): + await LLMService.update_workbench_llm(1, incoming, MagicMock(), tenant_id=1) + + +@pytest.mark.asyncio +async def test_chat_default_model_id_validated_against_merged_old_models(): + """When the body omits ``models`` (merge-guard keeps the stored list), the + default is checked against that effective list — old id passes, new id not + in the stored list fails.""" + # "727" exists only in the stored (_OLD) list. + incoming = WorkbenchModelConfig(chat_default_model_id="727", embedding_model=None) + persisted = await _run_and_capture(incoming) + assert persisted["chat_default_model_id"] == "727" + assert [m["id"] for m in persisted["models"]] == ["727", "774"] + + incoming_bad = WorkbenchModelConfig(chat_default_model_id="840", embedding_model=None) + with patch( + "bisheng.llm.domain.services.llm.avalidate_system_model_refs", + new=AsyncMock(), + ), patch( + "bisheng.llm.domain.services.llm.TenantSystemModelConfigDao.aresolve", + new=AsyncMock(return_value=(json.dumps(_OLD.model_dump()), False, False)), + ): + with pytest.raises(WorkbenchChatDefaultModelError): + await LLMService.update_workbench_llm(1, incoming_bad, MagicMock(), tenant_id=1) diff --git a/src/frontend/client/eslint-suppressions.json b/src/frontend/client/eslint-suppressions.json index ab98daae7..328180eb5 100644 --- a/src/frontend/client/eslint-suppressions.json +++ b/src/frontend/client/eslint-suppressions.json @@ -3054,7 +3054,7 @@ }, "src/pages/appChat/useChatHelpers.ts": { "@typescript-eslint/no-explicit-any": { - "count": 12 + "count": 11 }, "@typescript-eslint/no-unused-vars": { "count": 2 diff --git a/src/frontend/client/src/api/linsight.ts b/src/frontend/client/src/api/linsight.ts index 46dec2a95..143fcb186 100644 --- a/src/frontend/client/src/api/linsight.ts +++ b/src/frontend/client/src/api/linsight.ts @@ -98,6 +98,8 @@ export function getSelectableSkills(): Promise { export interface LinsightModelConfig { models?: { id: string | number; name?: string; displayName?: string }[]; linsight_default_model_id?: string | null; + /** Admin-configured default model for new daily-mode conversations. */ + chat_default_model_id?: string | null; } export function getLinsightModelConfig(): Promise { diff --git a/src/frontend/client/src/hooks/useChatModelMemo.ts b/src/frontend/client/src/hooks/useChatModelMemo.ts index d9e18e663..2ef55031a 100644 --- a/src/frontend/client/src/hooks/useChatModelMemo.ts +++ b/src/frontend/client/src/hooks/useChatModelMemo.ts @@ -1,5 +1,6 @@ import { useEffect, useRef } from 'react'; import { useRecoilState } from 'recoil'; +import { useGetWorkbenchModelsQuery } from '~/hooks/queries/queries'; import store from '~/store'; interface UserLike { @@ -18,11 +19,13 @@ interface BsConfigLike { /** * Hydrate / persist the chatModel atom under the user-scoped - * `bs:{uid}:chatModel` localStorage key. Falls back to the latest configured - * model when nothing is saved or the saved id no longer exists. Used by every - * chat surface that lets the user pick a model so the selection survives - * page refresh and new tabs (and gets wiped on re-login alongside the rest - * of `bs:*`). + * `bs:{uid}:chatModel` localStorage key. Resolution order for a fresh + * session: the user's last valid manual selection → the admin-configured + * daily-mode default (`chat_default_model_id` from /api/v1/llm/workbench) → + * the last configured model (compat for backends that predate the field). + * Used by every chat surface that lets the user pick a model so the selection + * survives page refresh and new tabs (and gets wiped on re-login alongside + * the rest of `bs:*`). */ export default function useChatModelMemo( user: UserLike | null | undefined, @@ -30,6 +33,7 @@ export default function useChatModelMemo( ) { const [chatModel, setChatModel] = useRecoilState(store.chatModel); const hydratedRef = useRef(false); + const { data: workbenchConfig, isLoading: workbenchLoading } = useGetWorkbenchModelsQuery(); useEffect(() => { if (!bsConfig || !user?.id) return; @@ -40,7 +44,18 @@ export default function useChatModelMemo( let target: ModelLike | undefined | null = savedModelId ? models.find((m) => String(m.id) === savedModelId) : null; - if (!target && models.length) target = models[models.length - 1]; + if (!target) { + // No valid manual selection — the admin default decides. Wait for the + // workbench config before settling so the default isn't skipped. + if (workbenchLoading) return; + const rawDefault = (workbenchConfig as Record | undefined)?.chat_default_model_id; + const adminDefaultId = + typeof rawDefault === 'string' || typeof rawDefault === 'number' ? String(rawDefault) : null; + if (adminDefaultId) { + target = models.find((m) => String(m.id) === adminDefaultId) ?? null; + } + if (!target && models.length) target = models[models.length - 1]; + } if (target) { setChatModel({ id: Number(target.id), @@ -49,7 +64,7 @@ export default function useChatModelMemo( } } catch { /* ignore */ } hydratedRef.current = true; - }, [bsConfig, user?.id, setChatModel]); + }, [bsConfig, user?.id, setChatModel, workbenchConfig, workbenchLoading]); useEffect(() => { if (!hydratedRef.current || !user?.id || !chatModel.id) return; diff --git a/src/frontend/client/src/locales/en/api_errors.gen.json b/src/frontend/client/src/locales/en/api_errors.gen.json index 0eba8b546..0d566de78 100644 --- a/src/frontend/client/src/locales/en/api_errors.gen.json +++ b/src/frontend/client/src/locales/en/api_errors.gen.json @@ -103,6 +103,7 @@ "10802": "Failed to add provider. All models failed to initialize", "10803": "Failed to add provider. Some models failed to initialize", "10810": "Please configure the workbench embedding model.", + "10811": "The daily mode default model must be one of the configured workbench chat models. Please select again.", "10900": "Knowledge base name already exists", "10901": "Embedding model required", "10902": "Summary model invalid. Reconfigure in system model settings: {{exception}}", diff --git a/src/frontend/client/src/locales/ja/api_errors.gen.json b/src/frontend/client/src/locales/ja/api_errors.gen.json index f35d1d8cb..bdade6972 100644 --- a/src/frontend/client/src/locales/ja/api_errors.gen.json +++ b/src/frontend/client/src/locales/ja/api_errors.gen.json @@ -103,6 +103,7 @@ "10802": "提供元追加に失敗(全モデルの初期化失敗)", "10803": "提供元追加に失敗(一部モデルの初期化失敗)", "10810": "ワークベンチの embedding モデルを設定してください。", + "10811": "日常モードのデフォルトモデルは、設定済みのワークベンチチャットモデルから選択してください。", "10900": "ナレッジベース名が重複しています", "10901": "Embeddingモデルを選択してください", "10902": "要約モデルが無効です:{{exception}}", diff --git a/src/frontend/client/src/locales/zh-Hans/api_errors.gen.json b/src/frontend/client/src/locales/zh-Hans/api_errors.gen.json index 4395cf64e..34fbe9e92 100644 --- a/src/frontend/client/src/locales/zh-Hans/api_errors.gen.json +++ b/src/frontend/client/src/locales/zh-Hans/api_errors.gen.json @@ -103,6 +103,7 @@ "10802": "添加服务提供方失败,模型全部初始化失败", "10803": "添加服务提供方失败,部分模型初始化失败", "10810": "请配置工作台向量模型。", + "10811": "日常模式默认模型必须是已配置的工作台对话模型,请重新选择。", "10900": "知识库名称重复", "10901": "知识库必须选择一个embedding模型", "10902": "文档知识库总结模型已失效,请前往模型管理-系统模型设置中进行配置。{{exception}}", diff --git a/src/frontend/packages/locales/src/api_errors/en.json b/src/frontend/packages/locales/src/api_errors/en.json index 0eba8b546..0d566de78 100644 --- a/src/frontend/packages/locales/src/api_errors/en.json +++ b/src/frontend/packages/locales/src/api_errors/en.json @@ -103,6 +103,7 @@ "10802": "Failed to add provider. All models failed to initialize", "10803": "Failed to add provider. Some models failed to initialize", "10810": "Please configure the workbench embedding model.", + "10811": "The daily mode default model must be one of the configured workbench chat models. Please select again.", "10900": "Knowledge base name already exists", "10901": "Embedding model required", "10902": "Summary model invalid. Reconfigure in system model settings: {{exception}}", diff --git a/src/frontend/packages/locales/src/api_errors/ja.json b/src/frontend/packages/locales/src/api_errors/ja.json index f35d1d8cb..bdade6972 100644 --- a/src/frontend/packages/locales/src/api_errors/ja.json +++ b/src/frontend/packages/locales/src/api_errors/ja.json @@ -103,6 +103,7 @@ "10802": "提供元追加に失敗(全モデルの初期化失敗)", "10803": "提供元追加に失敗(一部モデルの初期化失敗)", "10810": "ワークベンチの embedding モデルを設定してください。", + "10811": "日常モードのデフォルトモデルは、設定済みのワークベンチチャットモデルから選択してください。", "10900": "ナレッジベース名が重複しています", "10901": "Embeddingモデルを選択してください", "10902": "要約モデルが無効です:{{exception}}", diff --git a/src/frontend/packages/locales/src/api_errors/zh-Hans.json b/src/frontend/packages/locales/src/api_errors/zh-Hans.json index 4395cf64e..34fbe9e92 100644 --- a/src/frontend/packages/locales/src/api_errors/zh-Hans.json +++ b/src/frontend/packages/locales/src/api_errors/zh-Hans.json @@ -103,6 +103,7 @@ "10802": "添加服务提供方失败,模型全部初始化失败", "10803": "添加服务提供方失败,部分模型初始化失败", "10810": "请配置工作台向量模型。", + "10811": "日常模式默认模型必须是已配置的工作台对话模型,请重新选择。", "10900": "知识库名称重复", "10901": "知识库必须选择一个embedding模型", "10902": "文档知识库总结模型已失效,请前往模型管理-系统模型设置中进行配置。{{exception}}", diff --git a/src/frontend/platform/public/locales/en-US/api_errors.json b/src/frontend/platform/public/locales/en-US/api_errors.json index 0eba8b546..0d566de78 100644 --- a/src/frontend/platform/public/locales/en-US/api_errors.json +++ b/src/frontend/platform/public/locales/en-US/api_errors.json @@ -103,6 +103,7 @@ "10802": "Failed to add provider. All models failed to initialize", "10803": "Failed to add provider. Some models failed to initialize", "10810": "Please configure the workbench embedding model.", + "10811": "The daily mode default model must be one of the configured workbench chat models. Please select again.", "10900": "Knowledge base name already exists", "10901": "Embedding model required", "10902": "Summary model invalid. Reconfigure in system model settings: {{exception}}", diff --git a/src/frontend/platform/public/locales/en-US/model.json b/src/frontend/platform/public/locales/en-US/model.json index e1d1edd6c..333b64258 100644 --- a/src/frontend/platform/public/locales/en-US/model.json +++ b/src/frontend/platform/public/locales/en-US/model.json @@ -10,6 +10,7 @@ "workVectorModel": "Workbench Vector Model", "workVectorModelTooltip": "Used for $t(bs:linsight) task execution scenarios", "linsightDefaultModel": "$t(bs:linsight) Default Model", + "dailyDefaultModel": "Daily Mode Default Model", "linsightDefaultModelTooltip": "This model is the default executor for $t(bs:linsight) tasks; users can switch to another model when starting a task", "workbenchVoiceModel": "Workbench Voice Model", "sessionTitleGenerationModel": "Session Title Generation Model", diff --git a/src/frontend/platform/public/locales/ja/api_errors.json b/src/frontend/platform/public/locales/ja/api_errors.json index f35d1d8cb..bdade6972 100644 --- a/src/frontend/platform/public/locales/ja/api_errors.json +++ b/src/frontend/platform/public/locales/ja/api_errors.json @@ -103,6 +103,7 @@ "10802": "提供元追加に失敗(全モデルの初期化失敗)", "10803": "提供元追加に失敗(一部モデルの初期化失敗)", "10810": "ワークベンチの embedding モデルを設定してください。", + "10811": "日常モードのデフォルトモデルは、設定済みのワークベンチチャットモデルから選択してください。", "10900": "ナレッジベース名が重複しています", "10901": "Embeddingモデルを選択してください", "10902": "要約モデルが無効です:{{exception}}", diff --git a/src/frontend/platform/public/locales/ja/model.json b/src/frontend/platform/public/locales/ja/model.json index 6844f1182..3ea9cd478 100644 --- a/src/frontend/platform/public/locales/ja/model.json +++ b/src/frontend/platform/public/locales/ja/model.json @@ -8,6 +8,7 @@ "confirmCancelEdit": "変更はまだ保存されていません。本当に終了しますか?", "promptSaved": "プロンプトが保存されました", "linsightDefaultModel": "$t(bs:linsight)デフォルトモデル", + "dailyDefaultModel": "日常モードのデフォルトモデル", "linsightDefaultModelTooltip": "このモデルは$t(bs:linsight)タスクのデフォルト実行モデルです。ユーザーはタスク開始時に切り替えできます", "workbenchVoiceModel": "ワークベンチ音声モデル", "sessionTitleGenerationModel": "セッションタイトル生成モデル", diff --git a/src/frontend/platform/public/locales/zh-Hans/api_errors.json b/src/frontend/platform/public/locales/zh-Hans/api_errors.json index 4395cf64e..34fbe9e92 100644 --- a/src/frontend/platform/public/locales/zh-Hans/api_errors.json +++ b/src/frontend/platform/public/locales/zh-Hans/api_errors.json @@ -103,6 +103,7 @@ "10802": "添加服务提供方失败,模型全部初始化失败", "10803": "添加服务提供方失败,部分模型初始化失败", "10810": "请配置工作台向量模型。", + "10811": "日常模式默认模型必须是已配置的工作台对话模型,请重新选择。", "10900": "知识库名称重复", "10901": "知识库必须选择一个embedding模型", "10902": "文档知识库总结模型已失效,请前往模型管理-系统模型设置中进行配置。{{exception}}", diff --git a/src/frontend/platform/public/locales/zh-Hans/model.json b/src/frontend/platform/public/locales/zh-Hans/model.json index 5feb58d9d..7afbcba75 100644 --- a/src/frontend/platform/public/locales/zh-Hans/model.json +++ b/src/frontend/platform/public/locales/zh-Hans/model.json @@ -8,6 +8,7 @@ "confirmCancelEdit": "您的修改尚未保存,确定要退出吗?", "promptSaved": "提示词已保存", "linsightDefaultModel": "$t(bs:linsight)默认模型", + "dailyDefaultModel": "日常模式默认模型", "linsightDefaultModelTooltip": "该模型为$t(bs:linsight)任务的默认执行模型,用户发起任务时可切换", "workbenchVoiceModel": "工作台及知识空间语音模型", "sessionTitleGenerationModel": "应用会话标题生成模型", diff --git a/src/frontend/platform/src/pages/BuildPage/bench/ModelManagement.tsx b/src/frontend/platform/src/pages/BuildPage/bench/ModelManagement.tsx index 180366423..cebd2b65d 100644 --- a/src/frontend/platform/src/pages/BuildPage/bench/ModelManagement.tsx +++ b/src/frontend/platform/src/pages/BuildPage/bench/ModelManagement.tsx @@ -36,12 +36,15 @@ interface ModelManagementProps { onNameChange: (index: number, name: string) => void; onDescriptionChange?: (index: number, description: string) => void; onVisualToggle?: (index: number, enabled: boolean) => void; + /** Daily-mode default model: used for new daily conversations when the user has no valid manual selection. */ + chatDefaultModelId?: string | null; + onChatDefaultChange?: (id: string) => void; /** Linsight default model: the model id used as the default executor for Linsight tasks. */ linsightDefaultModelId?: string | null; onLinsightDefaultChange?: (id: string) => void; } export const ModelManagement = forwardRef( - ({ models, errors, error, onAdd, onRemove, onModelChange, onNameChange, onDescriptionChange, onVisualToggle, linsightDefaultModelId, onLinsightDefaultChange }, ref) => { + ({ models, errors, error, onAdd, onRemove, onModelChange, onNameChange, onDescriptionChange, onVisualToggle, chatDefaultModelId, onChatDefaultChange, linsightDefaultModelId, onLinsightDefaultChange }, ref) => { // `assistant` mode hits /api/v1/llm/assistant/llm_list which is already // filtered to the admin-configured assistant allowlist (default model // and its server are placed first). Avoids the fetch-all + client-side @@ -90,8 +93,9 @@ export const ModelManagement = forwardRef -
+
+
+
@@ -105,6 +109,9 @@ export const ModelManagement = forwardRef{t('bench.vision')}
+
+ +
@@ -118,7 +125,7 @@ export const ModelManagement = forwardRef setItemRef(el, index)} className="grid items-center mb-4" - style={{ gridTemplateColumns: "1.2fr 0.85fr 1.3fr 72px 116px 36px" }} + style={{ gridTemplateColumns: "1.2fr 0.85fr 1.3fr 72px 116px 116px 36px" }} >
{assistantLlmOptions.length > 0 ? ( @@ -170,6 +177,18 @@ export const ModelManagement = forwardRef
+ {/* Daily-mode default model: single-select radio across the whole column */} +
+ model.id && onChatDefaultChange?.(model.id)} + /> +
+ {/* Linsight default model: single-select radio across the whole column */}
))} +