代码优化,以适配后端llm新接口
This commit is contained in:
parent
254b2591d2
commit
debf2fbbb2
@ -6,7 +6,8 @@
|
|||||||
"permissions": ["storage"],
|
"permissions": ["storage"],
|
||||||
"host_permissions": [
|
"host_permissions": [
|
||||||
"http://app.wisdompulse.cn/*",
|
"http://app.wisdompulse.cn/*",
|
||||||
"http://share.wisdompulse.cn/*"
|
"http://share.wisdompulse.cn/*",
|
||||||
|
"http://app-test.wisdompulse.cn/*"
|
||||||
],
|
],
|
||||||
"action": {
|
"action": {
|
||||||
"default_popup": "pages/index.html",
|
"default_popup": "pages/index.html",
|
||||||
|
|||||||
@ -19,7 +19,7 @@ async function handleLogout(auth) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function loadToken() {
|
async function loadUserToken() {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
chrome.storage.local.get(["token", "username", "loginAt"], async (res) => {
|
chrome.storage.local.get(["token", "username", "loginAt"], async (res) => {
|
||||||
const mergedConfig = await loadMergedConfig();
|
const mergedConfig = await loadMergedConfig();
|
||||||
|
|||||||
@ -53,7 +53,7 @@ const defaultConfig = {
|
|||||||
},
|
},
|
||||||
llmToken: {
|
llmToken: {
|
||||||
name: "获取 AnythingLLM Token 接口",
|
name: "获取 AnythingLLM Token 接口",
|
||||||
url: "http://llm.wisdompulse.cn/api/request-token",
|
url: "http://app-test.wisdompulse.cn/api/v1/llm/token",
|
||||||
requestParams: [
|
requestParams: [
|
||||||
{ label: "密码字段", field: "password" }
|
{ label: "密码字段", field: "password" }
|
||||||
],
|
],
|
||||||
|
|||||||
@ -272,7 +272,6 @@ function promptLLMPasswordModal() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async function getLLMToken(forceRefresh = false) {
|
async function getLLMToken(forceRefresh = false) {
|
||||||
const config = await loadMergedConfig();
|
const config = await loadMergedConfig();
|
||||||
const tokenCfg = config.apiConfig.llmToken;
|
const tokenCfg = config.apiConfig.llmToken;
|
||||||
@ -303,7 +302,7 @@ async function getLLMToken(forceRefresh = false) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
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]) {
|
if (data[tokenField]) {
|
||||||
chrome.storage.local.set({ llmToken: data[tokenField], llmTokenAt: now });
|
chrome.storage.local.set({ llmToken: data[tokenField], llmTokenAt: now });
|
||||||
log("请求llmToken成功!");
|
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) {
|
async function fetchLLMAnswer(question, container, contentDiv) {
|
||||||
if (!question || !question.trim()) return;
|
if (!question || !question.trim()) return;
|
||||||
|
|
||||||
const config = await loadMergedConfig();
|
const config = await loadMergedConfig();
|
||||||
const llmToken = await getLLMToken();
|
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 url = "http://app-test.wisdompulse.cn/api/v1/llm/ask";
|
||||||
|
|
||||||
const userItem = document.createElement("div");
|
const userItem = document.createElement("div");
|
||||||
@ -385,11 +345,7 @@ async function fetchLLMAnswer(question, container, contentDiv) {
|
|||||||
let collectedSources = [];
|
let collectedSources = [];
|
||||||
let jumpMap = {};
|
let jumpMap = {};
|
||||||
try {
|
try {
|
||||||
const body = {
|
const body = { llmToken, question };
|
||||||
llmToken,
|
|
||||||
question
|
|
||||||
// 可选扩展参数可追加到 body 中,如 slug/wsName/attachments
|
|
||||||
};
|
|
||||||
|
|
||||||
const res = await fetch(url, {
|
const res = await fetch(url, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@ -415,12 +371,14 @@ async function fetchLLMAnswer(question, container, contentDiv) {
|
|||||||
done = readerDone;
|
done = readerDone;
|
||||||
if (value) {
|
if (value) {
|
||||||
buffer += decoder.decode(value, { stream: true });
|
buffer += decoder.decode(value, { stream: true });
|
||||||
const lines = buffer.split("\n");
|
const lines = buffer.split("\n\n");
|
||||||
buffer = lines.pop(); // 剩下不完整的一行留给下一次
|
buffer = lines.pop(); // 留给下次读未完整数据
|
||||||
|
|
||||||
for (let line of lines) {
|
for (let line of lines) {
|
||||||
if (line.startsWith("data: ")) {
|
line = line.trim();
|
||||||
const chunkStr = line.slice(6).trim();
|
if (!line.startsWith("data:")) continue;
|
||||||
|
|
||||||
|
const chunkStr = line.slice(5).trim();
|
||||||
if (!chunkStr) continue;
|
if (!chunkStr) continue;
|
||||||
|
|
||||||
let chunk;
|
let chunk;
|
||||||
@ -431,15 +389,12 @@ async function fetchLLMAnswer(question, container, contentDiv) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析内容片段
|
// 收集 sources(只更新,不渲染)
|
||||||
if (chunk.sources) {
|
if (chunk.sources) {
|
||||||
for (const s of chunk.sources) {
|
collectedSources = chunk.sources;
|
||||||
if (!collectedSources.find(x => x.id === s.id)) {
|
|
||||||
collectedSources.push(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 累积内容
|
||||||
let content = chunk.textResponse || "";
|
let content = chunk.textResponse || "";
|
||||||
if (content.includes("<think>")) {
|
if (content.includes("<think>")) {
|
||||||
inThink = true;
|
inThink = true;
|
||||||
@ -449,12 +404,14 @@ async function fetchLLMAnswer(question, container, contentDiv) {
|
|||||||
inThink = false;
|
inThink = false;
|
||||||
content = content.replace("</think>", "");
|
content = content.replace("</think>", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inThink) {
|
if (inThink) {
|
||||||
thinkStr += content.replace(/\n/g, "<br>");
|
thinkStr += content.replace(/\n/g, "<br>");
|
||||||
} else {
|
} else {
|
||||||
answerStr += content;
|
answerStr += content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 渲染回答(markdown 支持)
|
||||||
let html = "";
|
let html = "";
|
||||||
try {
|
try {
|
||||||
html = window.marked ? wrapHtmlWithCopyBtn(marked.parse(answerStr)) : answerStr.replace(/\n/g, "<br>");
|
html = window.marked ? wrapHtmlWithCopyBtn(marked.parse(answerStr)) : answerStr.replace(/\n/g, "<br>");
|
||||||
@ -469,11 +426,9 @@ async function fetchLLMAnswer(question, container, contentDiv) {
|
|||||||
bindAllCopyButtons(msgBox);
|
bindAllCopyButtons(msgBox);
|
||||||
insertCopyFullAnswerBtn(msgBox, answerStr);
|
insertCopyFullAnswerBtn(msgBox, answerStr);
|
||||||
contentDiv.scrollTop = contentDiv.scrollHeight;
|
contentDiv.scrollTop = contentDiv.scrollHeight;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 关闭信号触发最终引用渲染
|
||||||
|
if (chunk.close === true) {
|
||||||
if (collectedSources.length > 0) {
|
if (collectedSources.length > 0) {
|
||||||
const titles = collectedSources.map(s => s.title).filter(Boolean);
|
const titles = collectedSources.map(s => s.title).filter(Boolean);
|
||||||
jumpMap = await resolveJumpLinks(titles);
|
jumpMap = await resolveJumpLinks(titles);
|
||||||
@ -482,11 +437,17 @@ async function fetchLLMAnswer(question, container, contentDiv) {
|
|||||||
sourceList.className = "llm-sources";
|
sourceList.className = "llm-sources";
|
||||||
msgBox.appendChild(sourceList);
|
msgBox.appendChild(sourceList);
|
||||||
await renderSources(sourceList, collectedSources, jumpMap);
|
await renderSources(sourceList, collectedSources, jumpMap);
|
||||||
|
|
||||||
insertCopyFullAnswerBtn(msgBox, answerStr);
|
insertCopyFullAnswerBtn(msgBox, answerStr);
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
msgBox.innerHTML += `<span style='color:red;'>LLM异常:${e}</span>`;
|
msgBox.innerHTML += `<span style='color:red;'>LLM异常:${e}</span>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -77,7 +77,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
let lastKeywords = [];
|
let lastKeywords = [];
|
||||||
let config = await loadMergedConfig();
|
let config = await loadMergedConfig();
|
||||||
|
|
||||||
const auth = await loadToken();
|
const auth = await loadUserToken();
|
||||||
if (!auth) return handleLogout(auth);
|
if (!auth) return handleLogout(auth);
|
||||||
token = auth.token;
|
token = auth.token;
|
||||||
userNameLabel.textContent = auth.username;
|
userNameLabel.textContent = auth.username;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user