paper-burner/js/chatbot/actions/chatbot-actions.js

255 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// js/chatbot/chatbot-actions.js
window.ChatbotActions = {
/**
* 删除指定索引的消息。
* @param {number} index - 要删除消息的索引。
*/
deleteMessage: function(index) {
if (confirm('确定要删除这条消息吗?此操作无法撤销。')) {
if (window.ChatbotCore && typeof window.ChatbotCore.deleteMessageFromHistory === 'function' && typeof window.ChatbotCore.getCurrentDocId === 'function') {
const docId = window.ChatbotCore.getCurrentDocId();
window.ChatbotCore.deleteMessageFromHistory(docId, index, window.ChatbotUI.updateChatbotUI);
} else {
console.error('ChatbotActions: ChatbotCore.deleteMessageFromHistory or ChatbotCore.getCurrentDocId function is not defined.');
alert('删除消息的功能暂未完全配置,请联系管理员。');
}
}
},
/**
* 处理 "提供 PDF" 按钮点击:直接上传当前阅读的 PDF 到服务器 OSS
* 获取 URL 后作为消息发送给大模型。
*/
handleProvidePdf: async function() {
try {
console.log('[handleProvidePdf] 开始执行');
// 获取当前文档数据
const currentData = window.data;
if (!currentData || !currentData.name) {
alert('当前没有打开任何文档。');
return;
}
// IndexedDB 中存储的 ID 格式是 ${fileName}_${fileSize}
const dbDocId = currentData.id || `${currentData.name}_${currentData.size || 0}`;
console.log('[handleProvidePdf] 文档信息:', {
name: currentData.name,
size: currentData.size,
dbDocId: dbDocId
});
// 我们需要拿到真正的 PDF 文件对象
let pdfFileContent = null;
let pdfSource = '';
const currentDocName = currentData.name;
if (currentDocName && Array.isArray(window.pdfFiles)) {
pdfFileContent = window.pdfFiles.find(f => f.name === currentDocName);
if (pdfFileContent) {
pdfSource = 'window.pdfFiles';
console.log('[handleProvidePdf] 从 window.pdfFiles 获取到文件');
}
}
// 如果界面里有全局保存着 file 或 blob可以直接调用
if (!pdfFileContent) {
pdfFileContent = window.currentOriginalFile || window.currentPdfBlob;
if (pdfFileContent) {
pdfSource = window.currentOriginalFile ? 'window.currentOriginalFile' : 'window.currentPdfBlob';
console.log('[handleProvidePdf] 从', pdfSource, '获取到文件');
}
}
// 如果内存中没有,尝试从 IndexedDB 中获取 originalPdfBase64
if (!pdfFileContent) {
console.log('[handleProvidePdf] 内存中没有,尝试从 IndexedDB 获取, dbDocId:', dbDocId);
try {
const record = await window.getResultFromDB(dbDocId);
console.log('[handleProvidePdf] IndexedDB 记录:', record ? '找到' : '未找到');
if (record) {
console.log('[handleProvidePdf] 记录 metadata 字段:', record.metadata ? Object.keys(record.metadata) : '无');
}
if (record && record.metadata && record.metadata.originalPdfBase64) {
// 将 base64 转换为 Blob
const base64 = record.metadata.originalPdfBase64;
console.log('[handleProvidePdf] originalPdfBase64 长度:', base64.length);
const binaryString = atob(base64);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
pdfFileContent = new Blob([bytes], { type: 'application/pdf' });
pdfSource = 'IndexedDB (originalPdfBase64)';
console.log('[handleProvidePdf] 从 IndexedDB 转换成功');
}
} catch (e) {
console.warn('[handleProvidePdf] 从 IndexedDB 获取 PDF 失败:', e);
}
}
if (!pdfFileContent) {
alert('未能获取 PDF 文件,请确保当前文档已保存原始 PDF 数据。');
return;
}
console.log('[handleProvidePdf] PDF 来源:', pdfSource);
console.log('[handleProvidePdf] PDF 文件信息:', {
type: pdfFileContent.type,
size: pdfFileContent.size,
sizeMB: (pdfFileContent.size / 1024 / 1024).toFixed(2) + ' MB',
isBlob: pdfFileContent instanceof Blob
});
// 禁用发送按钮防止多次点击设置UI处于加载状态
const btn = document.getElementById('chatbot-provide-pdf-btn');
const originHtml = btn.innerHTML;
btn.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i>';
btn.disabled = true;
// 调用上传
console.log('[handleProvidePdf] 开始上传到 OSS...');
const ossUrl = await window.uploadFileToOssViaProxy(pdfFileContent, `${currentDocName}`);
console.log('[handleProvidePdf] OSS 上传成功, URL:', ossUrl);
// 构建包含 file_url 的消息内容(智谱 AI 格式)
const userMessageContent = [
{
type: 'file_url',
file_url: {
url: ossUrl
}
},
{
type: 'text',
text: '请阅读这个 PDF 文档,并根据内容随时准备回答我的问题。'
}
];
console.log('[handleProvidePdf] 发送给大模型的消息内容:', JSON.stringify(userMessageContent, null, 2));
// 恢复按钮状态
btn.innerHTML = originHtml;
btn.disabled = false;
// 让聊天框自动发送消息
if (window.ChatbotCore && typeof window.ChatbotCore.sendChatbotMessage === 'function') {
// 第四个参数可以用来设置页面上的 display 文本,避免一长串 URL
const displayText = `(已向 AI 提供当前 PDF 文档供阅读)`;
console.log('[handleProvidePdf] 调用 sendChatbotMessage 发送消息');
window.ChatbotCore.sendChatbotMessage(userMessageContent, window.ChatbotUI.updateChatbotUI, null, displayText);
}
console.log('[handleProvidePdf] 执行完成');
} catch (err) {
console.error('[handleProvidePdf] 提供PDF发生错误:', err);
alert('上传或提供 PDF 失败:' + err.message);
const btn = document.getElementById('chatbot-provide-pdf-btn');
if (btn) {
btn.innerHTML = '<iconify-icon icon="carbon:document-pdf" width="20"></iconify-icon>';
btn.disabled = false;
}
}
},
/**
* 重新发送指定索引的用户消息。
* 将删除此消息之后的所有历史记录,然后重新发送此消息。
* @param {number} index - 要重发消息的索引。
*/
resendUserMessage: function(index) {
if (!window.ChatbotCore || !window.ChatbotCore.chatHistory || !window.ChatbotCore.chatHistory[index] || typeof window.ChatbotCore.getCurrentDocId !== 'function' || typeof window.ChatbotCore.saveChatHistory !== 'function') {
console.error('ChatbotActions: Cannot resend message, core functions or history/message index is invalid.');
alert('无法重发消息,核心功能或聊天记录无效。');
return;
}
const docId = window.ChatbotCore.getCurrentDocId();
const messageToResend = { ...window.ChatbotCore.chatHistory[index] }; // 浅拷贝以防意外修改
if (messageToResend.role !== 'user') {
alert('只能重发您发送的消息。');
return;
}
let textContent = '';
let hasNonTextContent = false;
let originalUserMessageParts = []; // 用于重新发送
if (Array.isArray(messageToResend.content)) {
originalUserMessageParts = messageToResend.content.map(part => ({ ...part })); // 深拷贝parts
const textPart = originalUserMessageParts.find(part => part.type === 'text');
if (textPart) {
textContent = textPart.text;
}
if (originalUserMessageParts.some(part => part.type === 'image_url')) {
hasNonTextContent = true;
}
} else if (typeof messageToResend.content === 'string') {
textContent = messageToResend.content;
originalUserMessageParts = [{ type: 'text', text: textContent }];
}
if (hasNonTextContent && textContent) {
if (!confirm('此消息包含图片。目前重发功能仅支持文本部分。是否继续重发文本内容?(此操作将删除后续所有对话)')) {
return;
}
// 如果用户确认,我们只重发文本部分
originalUserMessageParts = [{ type: 'text', text: textContent }];
} else if (hasNonTextContent && !textContent) {
alert('此消息仅包含图片,目前无法直接重发。请尝试重新上传图片并输入文本。');
return;
} else {
// 纯文本消息,或用户已确认仅重发文本
if (!confirm('确定要重发这条消息吗?此操作将删除该消息之后的所有对话记录。')) {
return;
}
}
if (!textContent.trim() && !hasNonTextContent) { // 如果清除了图片后没有文本了
alert('没有可重发的文本内容。');
return;
}
// 删除此消息之后的所有历史记录
if (index < window.ChatbotCore.chatHistory.length -1) { // 确保不是最后一条消息
window.ChatbotCore.chatHistory.splice(index + 1);
}
// 注意:此时 messageToResend 自身还在 chatHistory 中,我们会在 sendChatbotMessage 前将其移除,或者 sendChatbotMessage 内部会处理好上下文。
// 为了简化我们先从历史记录中移除旧的这条sendChatbotMessage 会重新添加它。
window.ChatbotCore.chatHistory.splice(index, 1);
// 更新并保存被截断的历史记录
window.ChatbotCore.saveChatHistory(docId, window.ChatbotCore.chatHistory);
window.ChatbotUI.updateChatbotUI(); // 更新UI以反映历史记录的变化
// 清空当前输入框(如果用户正在输入)
const inputField = document.getElementById('chatbot-input');
if (inputField) inputField.value = '';
// 清空已选图片 (因为我们只重发提取的文本或原始消息的图片)
if (window.ChatbotImageUtils) {
window.ChatbotImageUtils.selectedChatbotImages = [];
window.ChatbotImageUtils.updateSelectedImagesPreview();
}
// 准备发送内容,可以是字符串或数组
let sendVal = originalUserMessageParts.length === 1 && originalUserMessageParts[0].type === 'text'
? originalUserMessageParts[0].text
: originalUserMessageParts;
let displayVal = sendVal; // 简单起见,让显示值和发送值一致
if (window.PromptConstructor && typeof window.PromptConstructor.enhanceUserPrompt === 'function') {
sendVal = window.PromptConstructor.enhanceUserPrompt(sendVal);
}
// 确保 ChatbotCore.sendChatbotMessage 的参数和行为
// 第三个参数是 onComplete (通常是null), 第四个是 displayVal
window.ChatbotCore.sendChatbotMessage(sendVal, window.ChatbotUI.updateChatbotUI, null, displayVal);
}
};