代码优化,以适配后端llm新接口
This commit is contained in:
parent
254b2591d2
commit
debf2fbbb2
@ -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",
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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" }
|
||||
],
|
||||
|
||||
@ -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,12 +371,14 @@ 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();
|
||||
line = line.trim();
|
||||
if (!line.startsWith("data:")) continue;
|
||||
|
||||
const chunkStr = line.slice(5).trim();
|
||||
if (!chunkStr) continue;
|
||||
|
||||
let chunk;
|
||||
@ -431,15 +389,12 @@ async function fetchLLMAnswer(question, container, contentDiv) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 解析内容片段
|
||||
// 收集 sources(只更新,不渲染)
|
||||
if (chunk.sources) {
|
||||
for (const s of chunk.sources) {
|
||||
if (!collectedSources.find(x => x.id === s.id)) {
|
||||
collectedSources.push(s);
|
||||
}
|
||||
}
|
||||
collectedSources = chunk.sources;
|
||||
}
|
||||
|
||||
// 累积内容
|
||||
let content = chunk.textResponse || "";
|
||||
if (content.includes("<think>")) {
|
||||
inThink = true;
|
||||
@ -449,12 +404,14 @@ async function fetchLLMAnswer(question, container, contentDiv) {
|
||||
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>");
|
||||
@ -469,11 +426,9 @@ async function fetchLLMAnswer(question, container, contentDiv) {
|
||||
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);
|
||||
@ -482,11 +437,17 @@ async function fetchLLMAnswer(question, container, contentDiv) {
|
||||
sourceList.className = "llm-sources";
|
||||
msgBox.appendChild(sourceList);
|
||||
await renderSources(sourceList, collectedSources, jumpMap);
|
||||
|
||||
insertCopyFullAnswerBtn(msgBox, answerStr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
msgBox.innerHTML += `<span style='color:red;'>LLM异常:${e}</span>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user