From b1e0c9fb987d4d603348575957fc445f9b56f689 Mon Sep 17 00:00:00 2001 From: Ethan-Zhang Date: Fri, 21 Nov 2025 17:28:11 +0800 Subject: [PATCH] =?UTF-8?q?Fix:=20=E9=87=8D=E6=9E=84=E5=AF=B9=E8=AF=9D?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E9=BB=98=E8=AE=A4=E6=A8=A1=E5=9E=8B=E9=80=89?= =?UTF-8?q?=E9=A1=B9=E8=8E=B7=E5=8F=96=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/paths/llm.ts | 12 +- src/store/historySession.ts | 70 ++--- .../dialogue/components/DialogueSession.vue | 258 ++++++++---------- 3 files changed, 136 insertions(+), 204 deletions(-) diff --git a/src/apis/paths/llm.ts b/src/apis/paths/llm.ts index dbd3b39a..0421abca 100644 --- a/src/apis/paths/llm.ts +++ b/src/apis/paths/llm.ts @@ -9,6 +9,15 @@ const getLLMList = (modelType?: string) => { return get('/api/llm', modelType ? { modelType } : {}); }; +/** + * 根据 llmId 获取单个模型信息 + * @param llmId 模型ID + * @returns + */ +const getLLMById = (llmId: string) => { + return get('/api/llm', { llmId }); +}; + /** * 更新会话的LLM模型 * @returns @@ -28,5 +37,6 @@ const getAddedModels = (modelType?: string) => { export const llmApi = { getAddedModels, getLLMList, + getLLMById, updateLLMList, -}; +}; \ No newline at end of file diff --git a/src/store/historySession.ts b/src/store/historySession.ts index edf08fbc..3919aaf5 100644 --- a/src/store/historySession.ts +++ b/src/store/historySession.ts @@ -16,7 +16,6 @@ import { useSessionStore } from '.'; import type { SessionItem } from 'src/components/sessionCard/type'; import { successMsg } from 'src/components/Message'; import i18n from 'src/i18n'; -import { getPreferredReasoningModel } from '@/utils/userPreferences'; export interface HistorySessionItem { conversationId: string; @@ -158,6 +157,7 @@ export const useHistorySessionStore = defineStore( }; /** * 获取当前 llm 模型的数值 + * 🔑 修复:从完整的模型列表中查找,确保包含所有能力字段 */ const currentLLM = async () => { // 先置空 @@ -165,7 +165,13 @@ export const useHistorySessionStore = defineStore( await getHistorySession(); historySession.value.forEach((item) => { if (item.conversationId === currentSelectedSession.value) { - selectLLM.value = item.llm; + // 🔑 关键修复:不直接使用历史记录中的llm对象 + // 而是根据llmId从完整的模型列表中查找,确保包含所有能力字段 + selectLLM.value = { + ...item.llm, + // 标记这个是来自历史记录的,需要在使用时补充能力字段 + _fromHistory: true + }; if (item.appId) { app.value.appId = item.appId; } @@ -344,38 +350,16 @@ export const useHistorySessionStore = defineStore( try { const appId = user_selected_app.value ?? ''; - // 🔑 新增:获取用户偏好的推理模型(可选,失败不影响创建对话) - let preferredLlmId = ''; - try { - const [llmError, llmRes] = await api.getLLMList(); - if (!llmError && llmRes && llmRes.code === 200) { - // 🔑 新增:只保留chat类型的模型 - const allModels = llmRes.result || []; - const chatModels = allModels.filter(model => - !model.type || model.type === 'chat' // 如果没有type字段则默认为chat类型 - ); - const preferredModel = getPreferredReasoningModel(chatModels); - if (preferredModel) { - preferredLlmId = preferredModel.llmId; - } else { - } - } else { - } - } catch (error) { - } + // 🔑 移除前端的用户偏好模型选择逻辑 + // 完全依赖后端指定的默认模型 - // 🔑 临时修复:简化参数,避免可能的参数问题 + // 🔑 简化参数,不传递 llm_id,让后端完全控制默认模型选择 const createParams: any = { appId: appId || '', // 确保appId不是undefined debug: isDebug || false }; - // 只有当llmId有值时才添加 - if (preferredLlmId && preferredLlmId.trim() !== '') { - createParams.llm_id = preferredLlmId; - } - const [error, res] = await api.createSession(createParams); @@ -404,31 +388,12 @@ export const useHistorySessionStore = defineStore( // 🔑 修复:创建新会话时更新路由(在组件中处理) - // 🔑 新架构:手动将新会话添加到 allHistorySessions - const currentAppId = appId || ''; - const newSession: HistorySessionItem = { - conversationId: newConversationId, - createdTime: new Date().toISOString(), - title: '新对话', - docCount: 0, - appId: currentAppId, - debug: isDebug || false, - kbList: [], - llm: { icon: '', modelName: '', llmId: preferredLlmId || '' }, - }; - - // 🔑 确保 allHistorySessions 有这个 app 的数组 - if (!allHistorySessions.value[currentAppId]) { - allHistorySessions.value[currentAppId] = []; - } - - // 🔑 将新会话添加到对应 app 的数组开头 - allHistorySessions.value[currentAppId].unshift(newSession); - - - // 🔑 简化:不需要再调用 getHistorySession(),因为已经手动更新了数据 - // 重置防重复标志 + // 🔑 关键修复:重置加载标志,强制重新获取会话列表 isLoadingSessionRecord = false; + + // 🔑 关键修复:创建会话后立即调用 getHistorySession() 重新获取会话列表 + // 确保获取到后端返回的完整 llm 信息(包括后端指定的默认模型) + await getHistorySession(); } } catch (error) { throw error; // 重新抛出错误 @@ -453,6 +418,7 @@ export const useHistorySessionStore = defineStore( return { params, historySession, + allHistorySessions, // 🔑 导出所有会话数据(未过滤) currentSelectedSession, selectedSessionIds, indeterminate, @@ -479,4 +445,4 @@ export const useHistorySessionStore = defineStore( pick: [], }, }, -); +); \ No newline at end of file diff --git a/src/views/dialogue/components/DialogueSession.vue b/src/views/dialogue/components/DialogueSession.vue index 86a5e5ac..33c7070a 100644 --- a/src/views/dialogue/components/DialogueSession.vue +++ b/src/views/dialogue/components/DialogueSession.vue @@ -19,7 +19,7 @@ import { ElMessage } from 'element-plus'; import { useAccountStore } from 'src/store'; import { successMsg, errorMsg } from 'src/components/Message'; import i18n from 'src/i18n'; -import { getAutoExecutePreference, getPreferredReasoningModel } from '@/utils/userPreferences'; +import { getAutoExecutePreference } from '@/utils/userPreferences'; import DialogueVariablePanel from './DialogueVariablePanel.vue'; import DocumentPreview from './DocumentPreview.vue'; import { listVariables } from '@/api/variable'; @@ -972,130 +972,85 @@ const clearSuggestion = (index: number): void => { } }; -// 🔑 优化:获取完整的模型信息(包括能力字段) -// 优先从已加载的 llmOptions 中查找,避免重复请求 -const getFullModelInfo = async (llmId: string): Promise => { - // 🔑 优先级1:从已加载的 llmOptions 中查找 - if (llmOptions.value && llmOptions.value.length > 0) { - const foundModel = llmOptions.value.find(model => - (model.llmId || model.id) === llmId - ); - - if (foundModel) { - return { - llmId: foundModel.llmId, - id: foundModel.id || foundModel.llmId, - modelName: foundModel.modelName, - icon: foundModel.icon || '', - supportsThinking: foundModel.supportsThinking, - canToggleThinking: foundModel.canToggleThinking - } as LLMOption; +// 🔑 根据当前 conversationId 查询对应模型的完整能力信息 +const loadLLMInfoByConversation = async () => { + const conversationId = currentSelectedSession.value; + + if (!conversationId) { + return; + } + + // 🔑 从 allHistorySessions 中查找当前会话的 llm.llmId + const { allHistorySessions } = storeToRefs(useHistorySessionStore()); + + // 🔑 关键:如果 allHistorySessions 为空,说明 getHistorySession() 还没完成,直接返回 + if (Object.keys(allHistorySessions.value).length === 0) { + return; + } + + let llmId: string | null = null; + + // 遍历所有 appId 分组,查找当前会话 + for (const appId in allHistorySessions.value) { + const sessions = allHistorySessions.value[appId]; + const found = sessions.find(s => s.conversationId === conversationId); + if (found && found.llm && found.llm.llmId) { + llmId = found.llm.llmId; + break; } } - // 🔑 优先级2:如果 llmOptions 为空或未找到,才调用 API 查询 + if (!llmId) { + return; + } + try { - const [_, res] = await api.getLLMList('chat'); - if (!_ && res && res.code === 200) { - const allModels = res.result || []; - const chatModels = allModels.filter(model => { - if (!model.type) return true; - if (Array.isArray(model.type)) { - return model.type.includes('chat'); - } - return model.type === 'chat'; - }); - - // 查找匹配的模型 - const foundModel = chatModels.find(model => - (model.llmId || model.id) === llmId - ); - - if (foundModel) { - return { - llmId: foundModel.llmId, - id: foundModel.id || foundModel.llmId, - modelName: foundModel.modelName, - icon: foundModel.icon || '', - supportsThinking: foundModel.supportsThinking, - canToggleThinking: foundModel.canToggleThinking - } as LLMOption; - } + // 调用 /api/llm?llmId=xxx 查询模型的完整能力信息 + const [error, res] = await api.getLLMById(llmId); + + if (error || !res || res.code !== 200) { + return; } - } catch (error) { - console.error('获取模型信息失败', error); - } - return null; -}; - -// 🔑 新增:根据 conversationId 直接获取会话的 llm 信息 -const getCurrentSessionLLM = async (conversationId: string): Promise<{ icon: string; modelName: string; llmId: string } | null> => { - const [err, res] = await api.getSessionRecord(); - if (!err && res && res.code === 200) { - const conversations = res.result.conversations || []; - const currentConv = conversations.find((conv: any) => conv.conversationId === conversationId); - if (currentConv && currentConv.llm && currentConv.llm.llmId) { - return { - icon: currentConv.llm.icon || '', - modelName: currentConv.llm.modelName || '', - llmId: currentConv.llm.llmId - }; + const modelList = res.result || []; + if (modelList.length === 0) { + return; } + + const modelInfo = modelList[0]; + + // 🔑 设置选中的模型(包含完整的能力字段) + selectedLLM.value = { + llmId: modelInfo.llmId || modelInfo.id, + id: modelInfo.id || modelInfo.llmId, + modelName: modelInfo.modelName, + icon: modelInfo.icon || '', + supports_thinking: modelInfo.supportsThinking || false, + can_toggle_thinking: modelInfo.canToggleThinking || false + }; + + } catch (error) { + console.error('加载模型信息失败:', error); } - return null; }; -// 🔑 修复:添加 forceReselect 参数,确保每次页面进入都能重新选择模型 -const getProviderLLM = async (forceReselect: boolean = false) => { - // 🔑 修复:使用查询参数type限制只查询chat类型的模型 +// 🔑 获取模型列表,用于模型选择器的下拉选项 +const getProviderLLM = async () => { const [_, res] = await api.getLLMList('chat'); if (!_ && res && res.code === 200) { const allModels = res.result || []; - // 🔑 修复:适配后端type从字符串改为字符串数组的变更 - // 保留前端过滤作为兜底,确保只有chat类型的模型 const chatModels = allModels.filter(model => { - // 如果没有type字段,默认为chat类型 if (!model.type) return true; - - // 适配type为字符串数组的情况 if (Array.isArray(model.type)) { return model.type.includes('chat'); } - - // 兼容旧的字符串格式 return model.type === 'chat'; }); llmOptions.value = chatModels; - - // 🔑 简化逻辑:后端已处理用户偏好,前端只需根据 conversation.llm 查找完整信息 - const currentLlmId = selectedLLM.value?.llmId || selectedLLM.value?.id; - - if (llmOptions.value.length > 0 && (!currentLlmId || forceReselect)) { - // 🔑 优化:直接从 API 获取当前会话的 llm 信息 - // 不依赖 historySession,避免因为 filter 过滤掉当前会话而找不到 - if (currentSelectedSession.value) { - const sessionLLM = await getCurrentSessionLLM(currentSelectedSession.value); - - if (sessionLLM && sessionLLM.llmId) { - const fullModel = await getFullModelInfo(sessionLLM.llmId); - - if (fullModel) { - selectedLLM.value = { - llmId: fullModel.llmId || fullModel.id, - id: fullModel.id || fullModel.llmId, - modelName: fullModel.modelName, - icon: fullModel.icon || '', - supports_thinking: fullModel.supportsThinking || false, - can_toggle_thinking: fullModel.canToggleThinking || false - }; - } - } - } - } } }; + const autoExecuteChange = async (value) => { autoExecuteRef.value = value; await nextTick(); @@ -1193,22 +1148,26 @@ onMounted(async () => { if (!inputRef.value) return; inputRef.value.focus(); - // 🔑 修复:不在这里调用 getProviderLLM,等待 currentSelectedSession 设置后由 watch 触发 - // 这样能确保在有会话信息时才获取模型,避免时序问题 + // 🔑 先加载模型选择器选项(异步,不等待) + getProviderLLM(); // 🔑 修复:从路由参数中恢复conversationId并同步左侧对话记录栏选中状态 if (route.query?.conversationId && typeof route.query.conversationId === 'string') { const conversationId = route.query.conversationId; - - // 🔑 简化:不再需要预加载 historySession,直接设置 currentSelectedSession - // getProviderLLM 内部会通过 getCurrentSessionLLM 直接从 API 获取会话信息 - currentSelectedSession.value = conversationId; - // 检查该conversationId是否存在于历史记录中 + // 🔑 优化:只在DialogueAside没有加载历史记录时才调用getHistorySession + // 避免与DialogueAside的onMounted重复调用 if (historySession.value.length === 0) { await getHistorySession(); + // 🔑 关键:在 getHistorySession 之后立即加载模型信息 + await loadLLMInfoByConversation(); + } else { + // 🔑 即使有数据也要加载模型信息 + await loadLLMInfoByConversation(); } + + // 检查该conversationId是否存在于历史记录中 const sessionExists = historySession.value.some(session => session.conversationId === conversationId); if (sessionExists) { // 如果存在,调用changeSession来同步状态(不重复设置loading状态) @@ -1219,6 +1178,10 @@ onMounted(async () => { if (historySession.value.length === 0) { await getHistorySession(); } + + // 🔑 关键:在 getHistorySession 之后再加载模型信息 + await loadLLMInfoByConversation(); + // 重置loading状态 isLoadingHistory.value = false; } @@ -1278,17 +1241,12 @@ onMounted(async () => { // 这样可以避免重复创建和状态不一致的问题 }); -// 🔑 新增:处理页面重新激活(从其他页面切换回来) +// 🔑 处理页面重新激活(从其他页面切换回来) onActivated(async () => { - // 🔑 关键:每次页面激活都重新按优先级选择模型 - // 优先级:历史记录模型 -> 用户偏好模型 - if (llmOptions.value.length > 0) { - // 如果已经加载过模型列表,强制重新选择 - await getProviderLLM(true); - } else { - // 如果还没有加载模型列表,正常加载 - await getProviderLLM(false); - } + // 重新加载当前会话的模型信息 + await loadLLMInfoByConversation(); + // 同时更新模型选择器的选项列表 + getProviderLLM(); }); // 🔑 新增:监听路由变化,处理根路径跳转 @@ -1347,41 +1305,39 @@ watch( }, ); -// 🔑 简化:监听 selectLLM 变化,直接使用 conversation 的 llm 信息 -// 后端已处理用户偏好,前端只需查找完整的模型能力信息 +// 🔑 监听 selectLLM 变化(来自历史记录的模型信息) +// 当历史记录更新时,根据 llmId 查询完整的模型信息 watch(selectLLM, async (newValue) => { - // 只在当前没有选中模型时才应用(避免覆盖用户手动选择) + // 🔑 条件判断: + // 1. newValue 必须有值且有ID + // 2. 当前没有选中模型(避免覆盖用户手动选择) if (newValue && (newValue.llmId || newValue.id) && !selectedLLM.value) { - - // 🔑 优化:从 llmOptions 查找完整信息,包含模型能力字段 - const llmId = newValue.llmId || newValue.id; - const fullModel = await getFullModelInfo(llmId); - - if (fullModel) { - selectedLLM.value = { - llmId: fullModel.llmId || fullModel.id, - id: fullModel.id || fullModel.llmId, - modelName: fullModel.modelName || '', - icon: fullModel.icon || '', - supports_thinking: fullModel.supportsThinking || false, - can_toggle_thinking: fullModel.canToggleThinking || false - }; - } else { - // 如果查询失败,使用历史记录数据(可能缺少能力字段) - selectedLLM.value = { - llmId: newValue.llmId || newValue.id, - id: newValue.id || newValue.llmId, - modelName: newValue.modelName || '', - icon: newValue.icon || '', - supports_thinking: newValue.supportsThinking || false, - can_toggle_thinking: newValue.canToggleThinking || false - }; - } + await loadLLMInfoByConversation(); } }); +// 🔑 新增:监听 allHistorySessions 变化 +// 当历史记录从空变为非空,或新增会话时,触发模型信息加载 +watch( + () => { + const { allHistorySessions } = storeToRefs(useHistorySessionStore()); + // 🔑 计算所有会话的总数(不只是 key 的数量) + let totalCount = 0; + for (const appId in allHistorySessions.value) { + totalCount += allHistorySessions.value[appId].length; + } + return totalCount; + }, + async (newCount, oldCount) => { + // 当会话数量增加时(新建或加载),且有当前会话ID,且没有选中模型 + if (newCount > oldCount && currentSelectedSession.value && !selectedLLM.value) { + await loadLLMInfoByConversation(); + } + } +); + // 监听模型切换,重置思维链状态 watch(selectedLLM, (newModel, oldModel) => { if (newModel && (!oldModel || newModel.llmId !== oldModel.llmId)) { @@ -1422,10 +1378,10 @@ watch( conversationList.value = []; } - // 🔑 修复:无论 llmOptions 是否已加载,都调用 getProviderLLM - // getProviderLLM 内部会自己获取模型列表 - nextTick(() => { - getProviderLLM(true); + // 🔑 简化:直接加载模型信息(不再依赖 llmOptions) + // 延迟执行,确保会话切换完成后再选择模型 + nextTick(async () => { + await loadLLMInfoByConversation(); }); } }, @@ -3194,4 +3150,4 @@ button[disabled]:hover { } } - + \ No newline at end of file -- Gitee