大模型调用优化
This commit is contained in:
parent
23d8ff6d37
commit
8c311bce87
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "渠道应用开发部知识库",
|
"name": "渠道应用开发部知识库",
|
||||||
"version": "1.3.1",
|
"version": "1.3.2",
|
||||||
"description": "通过关键词快速搜索内部文档",
|
"description": "通过关键词快速搜索内部文档",
|
||||||
"permissions": ["storage"],
|
"permissions": ["storage"],
|
||||||
"host_permissions": [
|
"host_permissions": [
|
||||||
|
|||||||
@ -22,10 +22,11 @@ function doLogin() {
|
|||||||
.then(res => res.json().then(data => ({ status: res.status, body: data })))
|
.then(res => res.json().then(data => ({ status: res.status, body: data })))
|
||||||
.then(({ status, body }) => {
|
.then(({ status, body }) => {
|
||||||
if (status === 200 && body.token) {
|
if (status === 200 && body.token) {
|
||||||
|
// 登录成功,保存登录时间
|
||||||
chrome.storage.local.set({
|
chrome.storage.local.set({
|
||||||
token: body.token,
|
token: body.token,
|
||||||
username: body.username || username,
|
username: body.username || username,
|
||||||
loginAt: Date.now()
|
loginAt: Date.now() // 毫秒时间戳
|
||||||
}, () => {
|
}, () => {
|
||||||
window.location.href = chrome.runtime.getURL("pages/popup.html");
|
window.location.href = chrome.runtime.getURL("pages/popup.html");
|
||||||
});
|
});
|
||||||
|
|||||||
@ -37,8 +37,13 @@
|
|||||||
<!-- AnythingLLM结果区域 -->
|
<!-- AnythingLLM结果区域 -->
|
||||||
<div id="llmResultContainer" class="llm-result-container hidden">
|
<div id="llmResultContainer" class="llm-result-container hidden">
|
||||||
<div class="llm-title">智能助手答案</div>
|
<div class="llm-title">智能助手答案</div>
|
||||||
<div id="llmResult"></div>
|
<div class="llm-result-content" id="llmResultContent"></div>
|
||||||
|
<div class="llm-input-bar">
|
||||||
|
<input id="llmInput" type="text" placeholder="请输入您的问题..." />
|
||||||
|
<button id="llmSendBtn">发送</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<ul id="results"></ul>
|
<ul id="results"></ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
111
pages/popup.js
111
pages/popup.js
@ -8,7 +8,9 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
|
|
||||||
// AnythingLLM 相关节点
|
// AnythingLLM 相关节点
|
||||||
const llmResultContainer = document.getElementById("llmResultContainer");
|
const llmResultContainer = document.getElementById("llmResultContainer");
|
||||||
const llmResultBox = document.getElementById("llmResult");
|
const llmResultContent = document.getElementById("llmResultContent");
|
||||||
|
const llmInput = document.getElementById("llmInput");
|
||||||
|
const llmSendBtn = document.getElementById("llmSendBtn");
|
||||||
|
|
||||||
const userDropdown = document.getElementById("userDropdown");
|
const userDropdown = document.getElementById("userDropdown");
|
||||||
const logoutBtn = document.getElementById("logoutBtn");
|
const logoutBtn = document.getElementById("logoutBtn");
|
||||||
@ -20,7 +22,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
let totalPages = 1;
|
let totalPages = 1;
|
||||||
let lastKeywords = [];
|
let lastKeywords = [];
|
||||||
|
|
||||||
const TOKEN_EXPIRE_MS = 24 * 60 * 60 * 1000;
|
const TOKEN_EXPIRE_MS = 7 * 24 * 60 * 60 * 1000; // 7天有效期
|
||||||
|
|
||||||
// === AnythingLLM 配置 ===
|
// === AnythingLLM 配置 ===
|
||||||
const LLM_PASSWORD = "lukeye@6";
|
const LLM_PASSWORD = "lukeye@6";
|
||||||
@ -69,7 +71,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取 LLM workspaceSlug(修正)
|
// 获取 LLM workspaceSlug
|
||||||
async function getLLMWorkspaceSlug(token, forceRefresh = false) {
|
async function getLLMWorkspaceSlug(token, forceRefresh = false) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
chrome.storage.local.get(["llmWorkspaceSlug", "llmWorkspaceAt"], async (res) => {
|
chrome.storage.local.get(["llmWorkspaceSlug", "llmWorkspaceAt"], async (res) => {
|
||||||
@ -111,17 +113,36 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 调用 AnythingLLM stream-chat 并流式渲染
|
// 追加智能助手问答内容
|
||||||
|
function appendLLMMsg(msg, type) {
|
||||||
|
const item = document.createElement("div");
|
||||||
|
item.className = "llm-msg-item " + (type === "user" ? "llm-user" : "llm-assistant");
|
||||||
|
item.style.margin = "6px 0";
|
||||||
|
item.innerHTML = `<span style="color:${type === "user" ? "#2a63c8" : "#2453b5"};">${type === "user" ? "我:" : "智能助手:"}</span> ${msg}`;
|
||||||
|
llmResultContent.appendChild(item);
|
||||||
|
llmResultContent.scrollTop = llmResultContent.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支持多次提问、历史堆叠
|
||||||
|
// 新增/修正流式渲染 assistant 的方式
|
||||||
async function fetchLLMAnswer(question) {
|
async function fetchLLMAnswer(question) {
|
||||||
llmResultBox.innerHTML = "";
|
if (!question || !question.trim()) return;
|
||||||
|
appendLLMMsg(question, "user");
|
||||||
llmResultContainer.classList.remove("hidden");
|
llmResultContainer.classList.remove("hidden");
|
||||||
|
|
||||||
|
// 创建 assistant 消息卡片并持续写入
|
||||||
|
const msgBox = document.createElement("div");
|
||||||
|
msgBox.className = "llm-msg-item llm-assistant";
|
||||||
|
msgBox.style.margin = "3px 0 7px 0";
|
||||||
|
msgBox.innerHTML = `<span style="color:#2453b5;">智能助手:</span> `;
|
||||||
|
llmResultContent.appendChild(msgBox);
|
||||||
|
llmResultContent.scrollTop = llmResultContent.scrollHeight;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log("开始获取 LLM token 和 workspaceSlug...");
|
|
||||||
const llmToken = await getLLMToken();
|
const llmToken = await getLLMToken();
|
||||||
const workspaceSlug = await getLLMWorkspaceSlug(llmToken);
|
const workspaceSlug = await getLLMWorkspaceSlug(llmToken);
|
||||||
|
|
||||||
const url = `${LLM_STREAM_API_BASE}/${workspaceSlug}/stream-chat`;
|
const url = `${LLM_STREAM_API_BASE}/${workspaceSlug}/stream-chat`;
|
||||||
console.log("开始请求 LLM stream-chat,POST", url, "参数:", { message: question, attachments: [] });
|
|
||||||
const res = await fetch(url, {
|
const res = await fetch(url, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
@ -131,8 +152,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
body: JSON.stringify({ message: question, attachments: [] }),
|
body: JSON.stringify({ message: question, attachments: [] }),
|
||||||
});
|
});
|
||||||
if (!res.ok || !res.body) {
|
if (!res.ok || !res.body) {
|
||||||
console.log("LLM stream-chat 请求失败,状态:", res.status);
|
msgBox.innerHTML += "<span style='color:red;'>LLM接口请求失败</span>";
|
||||||
llmResultBox.innerHTML = "<span style='color:red'>LLM接口请求失败</span>";
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const reader = res.body.getReader();
|
const reader = res.body.getReader();
|
||||||
@ -140,6 +160,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
let buffer = "";
|
let buffer = "";
|
||||||
let done = false;
|
let done = false;
|
||||||
let stop = false;
|
let stop = false;
|
||||||
|
let answer = "";
|
||||||
while (!done) {
|
while (!done) {
|
||||||
const { value, done: readerDone } = await reader.read();
|
const { value, done: readerDone } = await reader.read();
|
||||||
done = readerDone;
|
done = readerDone;
|
||||||
@ -152,30 +173,28 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
const data = line.replace("data: ", "");
|
const data = line.replace("data: ", "");
|
||||||
try {
|
try {
|
||||||
const chunk = JSON.parse(data);
|
const chunk = JSON.parse(data);
|
||||||
console.log("LLM 返回片段:", chunk);
|
|
||||||
if (chunk.type === "textResponseChunk") {
|
if (chunk.type === "textResponseChunk") {
|
||||||
if (chunk.close) {
|
if (chunk.close) {
|
||||||
stop = true;
|
stop = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (chunk.textResponse) {
|
if (chunk.textResponse) {
|
||||||
llmResultBox.innerHTML += chunk.textResponse.replace(/\n/g, "<br>");
|
answer += chunk.textResponse.replace(/\n/g, "<br>");
|
||||||
llmResultBox.scrollTop = llmResultBox.scrollHeight;
|
msgBox.innerHTML = `<span style="color:#2453b5;">智能助手:</span> ${answer}`;
|
||||||
|
llmResultContent.scrollTop = llmResultContent.scrollHeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
console.log("LLM 片段解析异常:", e, "行内容:", line);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (stop) break;
|
if (stop) break;
|
||||||
}
|
}
|
||||||
console.log("LLM stream-chat 已完成。");
|
if (!answer) {
|
||||||
|
msgBox.innerHTML += "无有效回答";
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("LLM stream-chat 全流程异常:", e);
|
msgBox.innerHTML += "<span style='color:red;'>LLM请求异常:" + e + "</span>";
|
||||||
llmResultBox.innerHTML = `<span style='color:red'>LLM请求异常:${e}</span>`;
|
|
||||||
llmResultContainer.classList.remove("hidden");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,16 +203,17 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
console.log("[v" + version + "]", ...args);
|
console.log("[v" + version + "]", ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 长期免登录机制
|
||||||
async function loadToken() {
|
async function loadToken() {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
chrome.storage.local.get(["token", "loginAt", "username"], (res) => {
|
chrome.storage.local.get(["token", "loginAt", "username"], (res) => {
|
||||||
log("加载本地存储 token:", res);
|
console.log("本地存储内容:", res);
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
if (!res.token || !res.loginAt || (now - res.loginAt > TOKEN_EXPIRE_MS)) {
|
if (!res.token || !res.loginAt || isNaN(res.loginAt) || (now - res.loginAt > TOKEN_EXPIRE_MS)) {
|
||||||
log("token 不存在或已过期");
|
console.log("token 不存在或已过期");
|
||||||
resolve(null);
|
resolve(null);
|
||||||
} else {
|
} else {
|
||||||
log("token 加载成功");
|
console.log("token 加载成功");
|
||||||
resolve({
|
resolve({
|
||||||
token: res.token,
|
token: res.token,
|
||||||
username: res.username || "用户"
|
username: res.username || "用户"
|
||||||
@ -215,7 +235,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
|
|
||||||
function clearResults() {
|
function clearResults() {
|
||||||
resultBox.innerHTML = "";
|
resultBox.innerHTML = "";
|
||||||
llmResultBox.innerHTML = "";
|
llmResultContent.innerHTML = "";
|
||||||
llmResultContainer.classList.add("hidden");
|
llmResultContainer.classList.add("hidden");
|
||||||
historyWrapper.classList.add("hidden");
|
historyWrapper.classList.add("hidden");
|
||||||
}
|
}
|
||||||
@ -385,6 +405,19 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
userDropdown.classList.add("hidden");
|
userDropdown.classList.add("hidden");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 多轮智能助手输入/回车发送
|
||||||
|
llmSendBtn.addEventListener("click", () => {
|
||||||
|
const val = llmInput.value.trim();
|
||||||
|
if (!val) return;
|
||||||
|
llmInput.value = "";
|
||||||
|
fetchLLMAnswer(val);
|
||||||
|
});
|
||||||
|
llmInput.addEventListener("keydown", function(e) {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
llmSendBtn.click();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const auth = await loadToken();
|
const auth = await loadToken();
|
||||||
@ -398,3 +431,31 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
bindEvents();
|
bindEvents();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 拖动+缩放
|
||||||
|
(function() {
|
||||||
|
const panel = document.getElementById('draggablePanel');
|
||||||
|
const header = document.querySelector('.header');
|
||||||
|
let dragging = false, offsetX = 0, offsetY = 0;
|
||||||
|
|
||||||
|
header.addEventListener('mousedown', function(e) {
|
||||||
|
dragging = true;
|
||||||
|
offsetX = e.clientX - panel.offsetLeft;
|
||||||
|
offsetY = e.clientY - panel.offsetTop;
|
||||||
|
document.body.style.userSelect = 'none';
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', function(e) {
|
||||||
|
if (dragging) {
|
||||||
|
let left = Math.max(0, e.clientX - offsetX);
|
||||||
|
let top = Math.max(0, e.clientY - offsetY);
|
||||||
|
panel.style.left = left + 'px';
|
||||||
|
panel.style.top = top + 'px';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('mouseup', function() {
|
||||||
|
dragging = false;
|
||||||
|
document.body.style.userSelect = '';
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|||||||
104
pages/styles.css
104
pages/styles.css
@ -280,12 +280,22 @@ h1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.llm-title {
|
.llm-title {
|
||||||
|
background: linear-gradient(90deg, #2560de 90%, #418cfb 100%);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 16px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #2453b5;
|
padding: 12px 12px 10px 12px;
|
||||||
margin-bottom: 4px;
|
margin: 0 -10px 6px -10px;
|
||||||
font-size: 14px;
|
border-bottom: 1px solid #b3cdfb;
|
||||||
|
border-radius: 8px 8px 0 0;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 2;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
box-shadow: 0 2px 6px #2764bb11;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#llmResult {
|
#llmResult {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #233;
|
color: #233;
|
||||||
@ -294,3 +304,91 @@ h1 {
|
|||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.llm-result-container {
|
||||||
|
margin-bottom: 14px;
|
||||||
|
max-height: 300px;
|
||||||
|
min-height: 60px;
|
||||||
|
overflow: auto;
|
||||||
|
border: 1px solid #e0e9f4;
|
||||||
|
border-radius: 7px;
|
||||||
|
padding: 0 12px 8px 12px;
|
||||||
|
background: #f8fbff;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.llm-title {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
background: #f8fbff;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2453b5;
|
||||||
|
margin: 0 -12px 8px -12px;
|
||||||
|
padding: 8px 12px 6px 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
border-bottom: 1px solid #e0e9f4;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.llm-result-content {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
min-height: 44px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #233;
|
||||||
|
line-height: 1.7;
|
||||||
|
word-break: break-all;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.llm-input-bar {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 8px;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#llmInput {
|
||||||
|
flex: 1;
|
||||||
|
padding: 6px 10px;
|
||||||
|
border: 1px solid #c9dafc;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 14px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#llmSendBtn {
|
||||||
|
padding: 6px 14px;
|
||||||
|
background: #4179f6;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
transition: background 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#llmSendBtn:hover {
|
||||||
|
background: #2350b8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.llm-msg-item.llm-assistant {
|
||||||
|
color: #2453b5;
|
||||||
|
background: #e7eefb;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin: 3px 0 9px 0;
|
||||||
|
padding: 7px 8px;
|
||||||
|
word-break: break-all;
|
||||||
|
font-weight: 500;
|
||||||
|
border: 1px solid #b3cdfb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 只让“智能助手:”四字加粗/蓝色(需配合js输出span) */
|
||||||
|
.llm-msg-item.llm-assistant .assistant-label {
|
||||||
|
color: #2560de;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user