diff --git a/frontend/src/pages/admin/Models.tsx b/frontend/src/pages/admin/Models.tsx index 4692033..1d54870 100644 --- a/frontend/src/pages/admin/Models.tsx +++ b/frontend/src/pages/admin/Models.tsx @@ -26,9 +26,9 @@ const capabilityLabels: Record = { embedding: '嵌入向量', rerank: '重排序', video: '视频生成', }; -// 锁定项:文本对话(text) 与 推理思考(thinking) 强制使用平台默认对话模型, -// 防止被误配到失效模型导致 chat 不可用(见全栈体检修复记录)。 -const LOCKED_CAP_DEFAULTS: Record = { text: 'aaae3233-4fd9-4b3e-ab51-cd8a87c82e0b', thinking: 'aaae3233-4fd9-4b3e-ab51-cd8a87c82e0b' }; +// 锁定项:text/thinking 能力动态绑定 is_default=true 模型,不再硬编码 ID。 +// 后端 defensive guard(chat_service.py)确保普通文字对话不会误走 image/video 模型。 +// #964: 修复 React error #321 — useState 必须放在组件函数体内,不能在模块顶层 // Test result type interface TestResult { @@ -53,6 +53,8 @@ interface EditingModel { } const Models: React.FC = () => { + const [lockedCapDefaults, setLockedCapDefaults] = useState>({}); + const [providers, setProviders] = useState([]); const [models, setModels] = useState([]); const [protocols, setProtocols] = useState([]); @@ -120,6 +122,11 @@ const Models: React.FC = () => { setCapabilities(cd.capabilities || []); setCapabilityDefaults(cd.defaults || {}); setCapabilityModels(cd.models || []); + // 动态锁定:is_default=true 模型绑定 text + thinking + const defaultM = mappedModels.find((m: any) => m.isDefault); + if (defaultM) { + setLockedCapDefaults({ text: defaultM.id, thinking: defaultM.id }); + } } catch (err: any) { setError('加载数据失败:' + (err.message || err.detail || JSON.stringify(err))); } finally { @@ -531,7 +538,7 @@ const Models: React.FC = () => { ); const cur = capabilityDefaults[cap.id] || ''; const saving = savingCaps.has(cap.id); - const lockedModelId = LOCKED_CAP_DEFAULTS[cap.id]; + const lockedModelId = lockedCapDefaults[cap.id]; const isLocked = Boolean(lockedModelId); const dotColor = cap.color || capabilityColors[cap.id] || '#6B7280'; return (