llm返回值包含参考文档
This commit is contained in:
parent
4954fee1e9
commit
b5a15d0750
@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "渠道应用开发部知识库",
|
||||
"version": "1.5.3",
|
||||
"version": "1.5.4",
|
||||
"description": "通过关键词快速搜索内部文档",
|
||||
"permissions": ["storage"],
|
||||
"host_permissions": [
|
||||
|
||||
109
scripts/llm.js
109
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("<think>")) {
|
||||
inThink = true;
|
||||
@ -132,6 +227,7 @@ async function fetchLLMAnswer(question, container, contentDiv) {
|
||||
} else {
|
||||
answerStr += content.replace(/\n/g, "<br>");
|
||||
}
|
||||
|
||||
msgBox.innerHTML =
|
||||
(thinkStr ? `<div class="llm-think">${thinkStr}</div>` : "") +
|
||||
`<span class="assistant-label">智能助手:</span>${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 += `<span style='color:red;'>LLM异常:${e}</span>`;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user