diff --git a/manifest.json b/manifest.json index 4448bd3..6c825af 100644 --- a/manifest.json +++ b/manifest.json @@ -6,7 +6,8 @@ "permissions": ["storage"], "host_permissions": [ "http://app.wisdompulse.cn/*", - "http://share.wisdompulse.cn/*" + "http://share.wisdompulse.cn/*", + "http://app-test.wisdompulse.cn/*" ], "action": { "default_popup": "pages/index.html", diff --git a/scripts/auth.js b/scripts/auth.js index 16cd7ce..ec97a56 100644 --- a/scripts/auth.js +++ b/scripts/auth.js @@ -19,7 +19,7 @@ async function handleLogout(auth) { } -async function loadToken() { +async function loadUserToken() { return new Promise((resolve) => { chrome.storage.local.get(["token", "username", "loginAt"], async (res) => { const mergedConfig = await loadMergedConfig(); @@ -34,4 +34,4 @@ async function loadToken() { } }); }); -} \ No newline at end of file +} diff --git a/scripts/config.js b/scripts/config.js index fee7e8f..991c1eb 100644 --- a/scripts/config.js +++ b/scripts/config.js @@ -53,7 +53,7 @@ const defaultConfig = { }, llmToken: { name: "获取 AnythingLLM Token 接口", - url: "http://llm.wisdompulse.cn/api/request-token", + url: "http://app-test.wisdompulse.cn/api/v1/llm/token", requestParams: [ { label: "密码字段", field: "password" } ], diff --git a/scripts/llm.js b/scripts/llm.js index 8eebfc1..f98b55d 100644 --- a/scripts/llm.js +++ b/scripts/llm.js @@ -272,7 +272,6 @@ function promptLLMPasswordModal() { } - async function getLLMToken(forceRefresh = false) { const config = await loadMergedConfig(); const tokenCfg = config.apiConfig.llmToken; @@ -303,7 +302,7 @@ async function getLLMToken(forceRefresh = false) { }); const data = await response.json(); - const tokenField = tokenCfg.responseParams.find(p => p.label.includes("Token")).field; + const tokenField = tokenCfg.responseParams.find(p => p.label.includes("token")).field; if (data[tokenField]) { chrome.storage.local.set({ llmToken: data[tokenField], llmTokenAt: now }); log("请求llmToken成功!"); @@ -320,55 +319,16 @@ async function getLLMToken(forceRefresh = false) { }); } -async function getLLMWorkspaceSlug(token, forceRefresh = false) { - const config = await loadMergedConfig(); - const wsCfg = config.apiConfig.workspaces; - const llmWorkspaceName = config.llmWorkspaceName; - const workspacesField = wsCfg.responseParams.find(p => p.field)?.field; - - return new Promise((resolve, reject) => { - chrome.storage.local.get(["llmWorkspaceSlug", "llmWorkspaceAt"], async (res) => { - const now = Date.now(); - const mergedConfig = await loadMergedConfig(); - const expireMs = mergedConfig.llmTokenExpireHours * 60 * 60 * 1000; - if (!forceRefresh && res.llmWorkspaceSlug && res.llmWorkspaceAt && now - res.llmWorkspaceAt < expireMs) { - log("获取llmWorkspaceSlug. expireMs: " + expireMs) - resolve(res.llmWorkspaceSlug); - } else { - try { - const response = await fetch(wsCfg.url, { - method: "GET", - headers: { Authorization: `Bearer ${token}` }, - }); - - const data = await response.json(); - const list = data[workspacesField] || []; - - let target = list.find(ws => ws.name === llmWorkspaceName); - // 若找不到,默认用第一个 - if (!target && list.length > 0) target = list[0]; - - if (target && target.slug) { - chrome.storage.local.set({ llmWorkspaceSlug: target.slug, llmWorkspaceAt: now }); - log("获取llmWorkspaceSlug: " + JSON.stringify(target)); - resolve(target.slug); - } else { - reject("未获取到LLM工作区slug"); - } - } catch (e) { - reject("LLM工作区接口异常"); - } - } - }); - }); -} - async function fetchLLMAnswer(question, container, contentDiv) { if (!question || !question.trim()) return; const config = await loadMergedConfig(); const llmToken = await getLLMToken(); - const userToken = await loadToken(); + const auth = await loadUserToken(); + if (!auth) { + return handleLogout(auth); + } + const userToken = auth.token; const url = "http://app-test.wisdompulse.cn/api/v1/llm/ask"; const userItem = document.createElement("div"); @@ -385,11 +345,7 @@ async function fetchLLMAnswer(question, container, contentDiv) { let collectedSources = []; let jumpMap = {}; try { - const body = { - llmToken, - question - // 可选扩展参数可追加到 body 中,如 slug/wsName/attachments - }; + const body = { llmToken, question }; const res = await fetch(url, { method: "POST", @@ -415,78 +371,83 @@ async function fetchLLMAnswer(question, container, contentDiv) { done = readerDone; if (value) { buffer += decoder.decode(value, { stream: true }); - const lines = buffer.split("\n"); - buffer = lines.pop(); // 剩下不完整的一行留给下一次 + const lines = buffer.split("\n\n"); + buffer = lines.pop(); // 留给下次读未完整数据 for (let line of lines) { - if (line.startsWith("data: ")) { - const chunkStr = line.slice(6).trim(); - if (!chunkStr) continue; + line = line.trim(); + if (!line.startsWith("data:")) continue; - let chunk; - try { - chunk = JSON.parse(chunkStr); - } catch (err) { - console.warn("非法 JSON:", chunkStr); - continue; - } + const chunkStr = line.slice(5).trim(); + if (!chunkStr) continue; - // 解析内容片段 - if (chunk.sources) { - for (const s of chunk.sources) { - if (!collectedSources.find(x => x.id === s.id)) { - collectedSources.push(s); - } - } - } + let chunk; + try { + chunk = JSON.parse(chunkStr); + } catch (err) { + console.warn("非法 JSON:", chunkStr); + continue; + } - let content = chunk.textResponse || ""; - if (content.includes("")) { - inThink = true; - content = content.replace("", ""); - } - if (content.includes("")) { - inThink = false; - content = content.replace("", ""); - } - if (inThink) { - thinkStr += content.replace(/\n/g, "
"); - } else { - answerStr += content; - } + // 收集 sources(只更新,不渲染) + if (chunk.sources) { + collectedSources = chunk.sources; + } - let html = ""; - try { - html = window.marked ? wrapHtmlWithCopyBtn(marked.parse(answerStr)) : answerStr.replace(/\n/g, "
"); - } catch (e) { - html = answerStr.replace(/\n/g, "
"); + // 累积内容 + let content = chunk.textResponse || ""; + if (content.includes("")) { + inThink = true; + content = content.replace("", ""); + } + if (content.includes("")) { + inThink = false; + content = content.replace("", ""); + } + + if (inThink) { + thinkStr += content.replace(/\n/g, "
"); + } else { + answerStr += content; + } + + // 渲染回答(markdown 支持) + let html = ""; + try { + html = window.marked ? wrapHtmlWithCopyBtn(marked.parse(answerStr)) : answerStr.replace(/\n/g, "
"); + } catch (e) { + html = answerStr.replace(/\n/g, "
"); + } + + msgBox.innerHTML = + (thinkStr ? `
${thinkStr}
` : "") + + `智能助手:
${html}
`; + + bindAllCopyButtons(msgBox); + insertCopyFullAnswerBtn(msgBox, answerStr); + contentDiv.scrollTop = contentDiv.scrollHeight; + + // 关闭信号触发最终引用渲染 + if (chunk.close === true) { + if (collectedSources.length > 0) { + const titles = collectedSources.map(s => s.title).filter(Boolean); + jumpMap = await resolveJumpLinks(titles); + + const sourceList = document.createElement("div"); + sourceList.className = "llm-sources"; + msgBox.appendChild(sourceList); + await renderSources(sourceList, collectedSources, jumpMap); + insertCopyFullAnswerBtn(msgBox, answerStr); } - - msgBox.innerHTML = - (thinkStr ? `
${thinkStr}
` : "") + - `智能助手:
${html}
`; - - bindAllCopyButtons(msgBox); - insertCopyFullAnswerBtn(msgBox, answerStr); - contentDiv.scrollTop = contentDiv.scrollHeight; + return; } } } } - - if (collectedSources.length > 0) { - const titles = collectedSources.map(s => s.title).filter(Boolean); - jumpMap = await resolveJumpLinks(titles); - - const sourceList = document.createElement("div"); - sourceList.className = "llm-sources"; - msgBox.appendChild(sourceList); - await renderSources(sourceList, collectedSources, jumpMap); - - insertCopyFullAnswerBtn(msgBox, answerStr); - } } catch (e) { msgBox.innerHTML += `LLM异常:${e}`; } } + + diff --git a/scripts/popup.js b/scripts/popup.js index 82a05a7..a4f4169 100644 --- a/scripts/popup.js +++ b/scripts/popup.js @@ -77,7 +77,7 @@ document.addEventListener("DOMContentLoaded", async () => { let lastKeywords = []; let config = await loadMergedConfig(); - const auth = await loadToken(); + const auth = await loadUserToken(); if (!auth) return handleLogout(auth); token = auth.token; userNameLabel.textContent = auth.username;