代码优化,以适配后端llm新接口

This commit is contained in:
luke 2025-06-23 19:18:01 +08:00
parent 254b2591d2
commit debf2fbbb2
5 changed files with 77 additions and 115 deletions

View File

@ -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",

View File

@ -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() {
}
});
});
}
}

View File

@ -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" }
],

View File

@ -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("<think>")) {
inThink = true;
content = content.replace("<think>", "");
}
if (content.includes("</think>")) {
inThink = false;
content = content.replace("</think>", "");
}
if (inThink) {
thinkStr += content.replace(/\n/g, "<br>");
} else {
answerStr += content;
}
// 收集 sources只更新不渲染
if (chunk.sources) {
collectedSources = chunk.sources;
}
let html = "";
try {
html = window.marked ? wrapHtmlWithCopyBtn(marked.parse(answerStr)) : answerStr.replace(/\n/g, "<br>");
} catch (e) {
html = answerStr.replace(/\n/g, "<br>");
// 累积内容
let content = chunk.textResponse || "";
if (content.includes("<think>")) {
inThink = true;
content = content.replace("<think>", "");
}
if (content.includes("</think>")) {
inThink = false;
content = content.replace("</think>", "");
}
if (inThink) {
thinkStr += content.replace(/\n/g, "<br>");
} else {
answerStr += content;
}
// 渲染回答markdown 支持)
let html = "";
try {
html = window.marked ? wrapHtmlWithCopyBtn(marked.parse(answerStr)) : answerStr.replace(/\n/g, "<br>");
} catch (e) {
html = answerStr.replace(/\n/g, "<br>");
}
msgBox.innerHTML =
(thinkStr ? `<div class="llm-think">${thinkStr}</div>` : "") +
`<span class="assistant-label">智能助手:</span><div class="llm-md">${html}</div>`;
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 ? `<div class="llm-think">${thinkStr}</div>` : "") +
`<span class="assistant-label">智能助手:</span><div class="llm-md">${html}</div>`;
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 += `<span style='color:red;'>LLM异常${e}</span>`;
}
}

View File

@ -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;