From b5a15d0750930941d1cfaa18fa09f2bfc00ad1aa Mon Sep 17 00:00:00 2001 From: luke Date: Sun, 1 Jun 2025 16:33:32 +0800 Subject: [PATCH] =?UTF-8?q?llm=E8=BF=94=E5=9B=9E=E5=80=BC=E5=8C=85?= =?UTF-8?q?=E5=90=AB=E5=8F=82=E8=80=83=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- manifest.json | 2 +- scripts/llm.js | 109 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 109 insertions(+), 2 deletions(-) diff --git a/manifest.json b/manifest.json index 447377f..e05f46d 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "渠道应用开发部知识库", - "version": "1.5.3", + "version": "1.5.4", "description": "通过关键词快速搜索内部文档", "permissions": ["storage"], "host_permissions": [ diff --git a/scripts/llm.js b/scripts/llm.js index 513a2ce..9677f05 100644 --- a/scripts/llm.js +++ b/scripts/llm.js @@ -1,9 +1,92 @@ -// scripts/llm.js const LLM_TOKEN_API = "http://llm.wisdompulse.cn/api/request-token"; const LLM_WORKSPACES_API = "http://llm.wisdompulse.cn/api/workspaces"; const LLM_STREAM_API_BASE = "http://llm.wisdompulse.cn/api/workspace"; const LLM_TOKEN_EXPIRE_MS = 23 * 60 * 60 * 1000; + +async function resolveJumpLinks(titles) { + if (!titles || titles.length === 0) return {}; + + return new Promise((resolveOuter) => { + chrome.storage.local.get(["token"], async (res) => { + const token = res.token; + if (!token) { + console.warn("resolveJumpLinks 缺少 token,跳过"); + return resolveOuter({}); + } + + try { + const res = await fetch("http://app.wisdompulse.cn/search/file-path", { + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": token + }, + body: JSON.stringify({ fileNames: titles }) + }); + + const data = await res.json(); + const map = {}; + for (const title of Object.keys(data)) { + if (data[title]) { + const cleanPath = data[title].replace(/\.md$/, ".html"); + map[title] = `http://share.wisdompulse.cn/public/${cleanPath}`; + } + } + + resolveOuter(map); + } catch (e) { + console.warn("resolveJumpLinks 接口异常:", e); + resolveOuter({}); + } + }); + }); +} + + +async function renderSources(sourceList, sources) { + const titles = sources.map(s => s.title).filter(Boolean); + const jumpMap = await resolveJumpLinks(titles); + + sourceList.style.marginTop = "16px"; + sourceList.style.borderTop = "1px solid #dbe3f3"; + sourceList.style.paddingTop = "10px"; + + const titleBar = document.createElement("div"); + titleBar.textContent = "📎 参考资料"; + titleBar.style.fontWeight = "bold"; + titleBar.style.marginBottom = "6px"; + titleBar.style.fontSize = "13.5px"; + titleBar.style.color = "#0d2fec"; + + sourceList.appendChild(titleBar); + + for (const src of sources) { + const url = jumpMap[src.title]; + const line = document.createElement("div"); + line.style.marginTop = "4px"; + + if (url) { + const link = document.createElement("a"); + link.href = url; + link.textContent = src.title || "未命名文档"; + link.target = "_blank"; + link.style.color = "#2560de"; + link.style.textDecoration = "none"; + link.onmouseenter = () => (link.style.textDecoration = "underline"); + link.onmouseleave = () => (link.style.textDecoration = "none"); + line.appendChild(link); + } else { + line.textContent = src.title || "未命名文档"; + line.style.color = "#666"; + } + + sourceList.appendChild(line); + } +} + + + async function getLLMPassword() { return new Promise((resolve) => { chrome.storage.local.get(["llmPassword"], (res) => { @@ -83,6 +166,8 @@ async function fetchLLMAnswer(question, container, contentDiv) { contentDiv.appendChild(msgBox); contentDiv.scrollTop = contentDiv.scrollHeight; + let collectedSources = []; + try { const token = await getLLMToken(); const workspace = await getLLMWorkspaceSlug(token); @@ -118,6 +203,16 @@ async function fetchLLMAnswer(question, container, contentDiv) { if (line.startsWith("data: ")) { const data = line.slice(6); const chunk = JSON.parse(data); + + // 收集 sources(去重按 id) + if (chunk.sources && Array.isArray(chunk.sources)) { + for (const src of chunk.sources) { + if (!collectedSources.find(s => s.id === src.id)) { + collectedSources.push(src); + } + } + } + let content = chunk.textResponse || ""; if (content.includes("")) { inThink = true; @@ -132,6 +227,7 @@ async function fetchLLMAnswer(question, container, contentDiv) { } else { answerStr += content.replace(/\n/g, "
"); } + msgBox.innerHTML = (thinkStr ? `
${thinkStr}
` : "") + `智能助手:${answerStr}`; @@ -140,6 +236,17 @@ async function fetchLLMAnswer(question, container, contentDiv) { } } } + + if (collectedSources.length > 0) { + const sourceList = document.createElement("div"); + sourceList.className = "llm-sources"; + sourceList.style.marginTop = "10px"; + sourceList.style.fontSize = "13px"; + sourceList.style.color = "#555"; + msgBox.appendChild(sourceList); + await renderSources(sourceList, collectedSources); + } + } catch (e) { msgBox.innerHTML += `LLM异常:${e}`; }