1225 lines
43 KiB
JavaScript
1225 lines
43 KiB
JavaScript
/**
|
|
* 一级注释:本地缓存服务模块
|
|
* 统一管理历史记录和标签的本地缓存
|
|
*/
|
|
|
|
import { logger } from '../utils/logger.js';
|
|
import { PromptFormatter } from "../utils/promptFormatter.js";
|
|
|
|
// ---缓存配置---
|
|
const CACHE_CONFIG = {
|
|
// 统一前缀
|
|
GLOBAL_PREFIX: "PromptAssistant_",
|
|
|
|
// 历史缓存配置
|
|
HISTORY_CACHE_KEY: "PromptAssistant_history_cache_all",
|
|
MAX_HISTORY_PER_NODE: 20, // 每个节点最多保存的历史条数
|
|
MAX_HISTORY_GLOBAL: 100, // 全局最多保存的历史条数
|
|
MAX_CONTENT_LENGTH: 5000, // 单条历史最大长度限制
|
|
|
|
// 标签缓存配置
|
|
TAG_CACHE_KEY: "PromptAssistant_tag_cache_all",
|
|
MAX_TAGS_PER_INPUT: 100, // 每个输入框最多保存的标签数量
|
|
MAX_TAGS_GLOBAL: 500, // 全局最多保存的标签数量
|
|
|
|
// 翻译缓存配置
|
|
TRANSLATE_CACHE_KEY: "PromptAssistant_translate_cache",
|
|
MAX_TRANSLATE_CACHE: 200, // 最多保存的翻译缓存条数
|
|
};
|
|
|
|
/**
|
|
* 通用缓存服务
|
|
* 提供基础的缓存操作方法
|
|
*/
|
|
class CacheService {
|
|
/**
|
|
* 从localStorage获取数据
|
|
*/
|
|
static get(key) {
|
|
try {
|
|
const data = localStorage.getItem(key);
|
|
return data ? JSON.parse(data) : null;
|
|
} catch (error) {
|
|
logger.error(`缓存服务 | 读取失败 | 键:${key} | 错误:${error.message}`);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 将数据存储到localStorage
|
|
*/
|
|
static set(key, value) {
|
|
try {
|
|
localStorage.setItem(key, JSON.stringify(value));
|
|
return true;
|
|
} catch (error) {
|
|
logger.error(`缓存服务 | 写入失败 | 键:${key} | 错误:${error.message}`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 从localStorage删除数据
|
|
*/
|
|
static remove(key) {
|
|
try {
|
|
localStorage.removeItem(key);
|
|
} catch (error) {
|
|
logger.error(`缓存服务 | 删除失败 | 键:${key} | 错误:${error.message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 清除指定前缀的所有缓存
|
|
*/
|
|
static clearByPrefix(prefix) {
|
|
try {
|
|
const keysToRemove = [];
|
|
for (let i = 0; i < localStorage.length; i++) {
|
|
const key = localStorage.key(i);
|
|
if (key && key.startsWith(prefix)) {
|
|
keysToRemove.push(key);
|
|
}
|
|
}
|
|
keysToRemove.forEach(key => this.remove(key));
|
|
// logger.debug(`缓存服务 | 清除前缀缓存 | 前缀:${prefix} | 数量:${keysToRemove.length}`);
|
|
} catch (error) {
|
|
logger.error(`缓存服务 | 清除前缀缓存失败 | 前缀:${prefix} | 错误:${error.message}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 历史缓存服务
|
|
* 管理历史记录的缓存操作
|
|
*/
|
|
class HistoryCacheService {
|
|
// 添加撤销/重做状态记录
|
|
static undoStates = new Map(); // 格式: { "nodeId_inputId": { currentIndex: number, records: array } }
|
|
|
|
/**
|
|
* 获取所有历史记录
|
|
*/
|
|
static getAllHistory() {
|
|
const key = CACHE_CONFIG.HISTORY_CACHE_KEY;
|
|
return CacheService.get(key) || [];
|
|
}
|
|
|
|
/**
|
|
* 保存所有历史记录
|
|
*/
|
|
static saveAllHistory(history) {
|
|
const key = CACHE_CONFIG.HISTORY_CACHE_KEY;
|
|
CacheService.set(key, history);
|
|
}
|
|
|
|
/**
|
|
* 获取历史记录列表
|
|
*/
|
|
static getHistoryList({ nodeId = null, limit = 50, workflowId = null } = {}) {
|
|
try {
|
|
const allHistory = this.getAllHistory();
|
|
|
|
let filteredHistory = allHistory;
|
|
if (workflowId) {
|
|
filteredHistory = allHistory.filter(item => item.workflow_id === workflowId);
|
|
}
|
|
|
|
// 确保每条历史记录都有必要的字段
|
|
const validHistory = filteredHistory.filter(item => {
|
|
const isValid = item && item.node_id && item.input_id &&
|
|
item.content && item.timestamp && item.operation_type;
|
|
if (!isValid) {
|
|
logger.debug(`历史缓存 | 跳过无效记录 | 节点:${item?.node_id}`);
|
|
}
|
|
return isValid;
|
|
});
|
|
|
|
if (!nodeId) {
|
|
return validHistory
|
|
.sort((a, b) => b.timestamp - a.timestamp)
|
|
.slice(0, limit);
|
|
}
|
|
|
|
const currentNodeHistory = [];
|
|
const otherNodesHistory = [];
|
|
|
|
validHistory.forEach(item => {
|
|
if (item.node_id === nodeId) {
|
|
currentNodeHistory.push(item);
|
|
} else {
|
|
otherNodesHistory.push(item);
|
|
}
|
|
});
|
|
|
|
currentNodeHistory.sort((a, b) => b.timestamp - a.timestamp);
|
|
otherNodesHistory.sort((a, b) => b.timestamp - a.timestamp);
|
|
|
|
return [...currentNodeHistory, ...otherNodesHistory].slice(0, limit);
|
|
} catch (error) {
|
|
logger.error(`历史缓存 | 获取历史失败 | 错误:${error.message}`);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取指定节点和输入框的历史记录
|
|
*/
|
|
static getInputHistory(nodeId, inputId, oldToNew = false) {
|
|
try {
|
|
const allHistory = this.getAllHistory();
|
|
const workflowId = app.graph?._workflow_id;
|
|
|
|
const filtered = allHistory.filter(item =>
|
|
item.node_id === nodeId &&
|
|
item.input_id === inputId &&
|
|
(!workflowId || item.workflow_id === workflowId) // 如果有workflowId,则匹配
|
|
);
|
|
// 根据参数决定排序方向
|
|
return filtered.sort((a, b) =>
|
|
oldToNew ? a.timestamp - b.timestamp : b.timestamp - a.timestamp
|
|
);
|
|
} catch (error) {
|
|
logger.error(`历史缓存 | 获取输入框历史失败 | 节点:${nodeId} | 输入框:${inputId} | 错误:${error.message}`);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 检查是否为重复的翻译记录
|
|
*/
|
|
static isRepeatedTranslation(nodeId, inputId, content, operationType) {
|
|
try {
|
|
if (operationType !== 'translate') {
|
|
return false;
|
|
}
|
|
|
|
// 获取该节点和输入框的历史记录
|
|
const history = this.getInputHistory(nodeId, inputId, false);
|
|
if (!history || history.length === 0) {
|
|
return false;
|
|
}
|
|
|
|
// 获取最近的一条记录
|
|
const lastRecord = history[0];
|
|
if (!lastRecord || lastRecord.operation_type !== 'translate') {
|
|
return false;
|
|
}
|
|
|
|
// 检查内容是否匹配
|
|
const currentContent = content.trim();
|
|
const lastContent = lastRecord.content.trim();
|
|
|
|
// 查询翻译缓存
|
|
const cacheResult = TranslateCacheService.queryTranslateCache(currentContent);
|
|
if (!cacheResult) {
|
|
return false;
|
|
}
|
|
|
|
// 检查是否为原文-译文或译文-原文的切换
|
|
if (cacheResult.type === 'source' && cacheResult.translatedText === lastContent) {
|
|
return true;
|
|
}
|
|
if (cacheResult.type === 'translated' && cacheResult.sourceText === lastContent) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
} catch (error) {
|
|
logger.error(`历史缓存 | 检查重复翻译失败 | 节点:${nodeId} | 输入框:${inputId} | 错误:${error.message}`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 添加历史记录
|
|
*/
|
|
static addHistory(historyItem) {
|
|
try {
|
|
// 验证必要字段
|
|
if (!historyItem.node_id || !historyItem.input_id || historyItem.content === undefined) {
|
|
logger.error("历史缓存 | 添加历史失败 | 缺少必要字段");
|
|
return false;
|
|
}
|
|
|
|
// 如果内容为空或只包含空白字符,跳过记录
|
|
if (!historyItem.content || historyItem.content.trim() === '') {
|
|
logger.debug(`历史缓存 | 跳过添加 | 节点:${historyItem.node_id} | 原因:空内容`);
|
|
return false;
|
|
}
|
|
|
|
// 内容长度限制
|
|
let content = historyItem.content;
|
|
if (content.length > CACHE_CONFIG.MAX_CONTENT_LENGTH) {
|
|
content = content.substring(0, CACHE_CONFIG.MAX_CONTENT_LENGTH) + "...";
|
|
}
|
|
|
|
// 检查是否为重复的翻译记录
|
|
if (this.isRepeatedTranslation(historyItem.node_id, historyItem.input_id, content, historyItem.operation_type)) {
|
|
logger.debug(`历史缓存 | 跳过添加 | 节点:${historyItem.node_id} | 原因:重复的翻译记录`);
|
|
return false;
|
|
}
|
|
|
|
const allHistory = this.getAllHistory();
|
|
|
|
// 获取当前节点和输入框的历史记录
|
|
const nodeHistory = allHistory.filter(item =>
|
|
item.node_id === historyItem.node_id &&
|
|
item.input_id === historyItem.input_id
|
|
);
|
|
|
|
// 检查是否与最近的一条历史记录内容相同
|
|
if (nodeHistory.length > 0) {
|
|
// 按时间戳降序排序,获取最新的记录
|
|
const latestHistory = nodeHistory.sort((a, b) => b.timestamp - a.timestamp)[0];
|
|
if (latestHistory.content === content) {
|
|
logger.debug(`历史缓存 | 跳过添加 | 节点:${historyItem.node_id} | 原因:与上一条记录相同`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// 创建新的历史记录
|
|
const newItem = {
|
|
workflow_id: app.graph?._workflow_id || 'unknown',
|
|
node_id: historyItem.node_id,
|
|
input_id: historyItem.input_id,
|
|
content: content,
|
|
operation_type: historyItem.operation_type || null,
|
|
timestamp: Date.now() + 1, // 确保时间戳唯一
|
|
request_id: historyItem.request_id || null
|
|
};
|
|
|
|
// 添加到历史记录末尾
|
|
allHistory.push(newItem);
|
|
|
|
// 限制每个节点的历史记录数量
|
|
const currentNodeHistory = allHistory.filter(item => item.node_id === historyItem.node_id);
|
|
if (currentNodeHistory.length > CACHE_CONFIG.MAX_HISTORY_PER_NODE) {
|
|
// 按时间戳排序
|
|
currentNodeHistory.sort((a, b) => b.timestamp - a.timestamp);
|
|
// 获取要保留的记录的时间戳
|
|
const cutoffTimestamp = currentNodeHistory[CACHE_CONFIG.MAX_HISTORY_PER_NODE - 1].timestamp;
|
|
// 移除旧记录
|
|
const filteredHistory = allHistory.filter(item =>
|
|
item.node_id !== historyItem.node_id ||
|
|
item.timestamp >= cutoffTimestamp
|
|
);
|
|
allHistory.length = 0;
|
|
allHistory.push(...filteredHistory);
|
|
}
|
|
|
|
// 限制全局历史记录数量
|
|
if (allHistory.length > CACHE_CONFIG.MAX_HISTORY_GLOBAL) {
|
|
// 按时间戳排序
|
|
allHistory.sort((a, b) => b.timestamp - a.timestamp);
|
|
allHistory.length = CACHE_CONFIG.MAX_HISTORY_GLOBAL;
|
|
}
|
|
|
|
this.saveAllHistory(allHistory);
|
|
// logger.debug(`历史缓存 | 添加历史 | 节点:${historyItem.node_id} | 内容长度:${content.length}`);
|
|
return true;
|
|
} catch (error) {
|
|
logger.error(`历史缓存 | 添加历史失败 | 错误:${error.message}`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 初始化撤销状态
|
|
*/
|
|
static initUndoState(nodeId, inputId, currentContent) {
|
|
try {
|
|
const key = `${nodeId}_${inputId}`;
|
|
|
|
// 获取该输入框的历史记录(从旧到新)
|
|
const inputHistory = this.getInputHistory(nodeId, inputId, true);
|
|
|
|
// 创建撤销状态,指向最新的记录
|
|
const undoState = {
|
|
currentIndex: inputHistory.length - 1, // 指向最后一条记录
|
|
currentContent: currentContent, // 保存当前内容
|
|
lastHistoryTimestamp: inputHistory.length > 0 ? inputHistory[inputHistory.length - 1].timestamp : 0
|
|
};
|
|
|
|
// 更新撤销状态
|
|
this.undoStates.set(key, undoState);
|
|
|
|
// logger.debug(`历史缓存 | 初始化撤销状态 | 节点:${nodeId} | 输入框:${inputId} | 历史数:${inputHistory.length} | 当前位置:${undoState.currentIndex}`);
|
|
} catch (error) {
|
|
logger.error(`历史缓存 | 初始化撤销状态失败 | 节点:${nodeId} | 输入框:${inputId} | 错误:${error.message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 执行撤销操作
|
|
*/
|
|
static undo(nodeId, inputId) {
|
|
try {
|
|
const key = `${nodeId}_${inputId}`;
|
|
const state = this.undoStates.get(key);
|
|
|
|
if (!state) {
|
|
return null;
|
|
}
|
|
|
|
// 获取历史记录(从旧到新)
|
|
const history = this.getInputHistory(nodeId, inputId, true);
|
|
|
|
// 如果没有历史记录,返回null
|
|
if (history.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
// 如果已经是第一条记录,无法继续撤销
|
|
if (state.currentIndex <= 0) {
|
|
return null;
|
|
}
|
|
|
|
// 更新当前位置
|
|
state.currentIndex--;
|
|
|
|
// 从历史记录中获取内容
|
|
const content = history[state.currentIndex]?.content || '';
|
|
|
|
// 更新状态
|
|
this.undoStates.set(key, state);
|
|
|
|
// logger.debug(`历史缓存 | 撤销操作 | 节点:${nodeId} | 输入框:${inputId} | 位置:${state.currentIndex}/${history.length - 1} | 内容:${content}`);
|
|
return content;
|
|
} catch (error) {
|
|
logger.error(`历史缓存 | 撤销操作失败 | 节点:${nodeId} | 输入框:${inputId} | 错误:${error.message}`);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 执行重做操作
|
|
*/
|
|
static redo(nodeId, inputId) {
|
|
try {
|
|
const key = `${nodeId}_${inputId}`;
|
|
const state = this.undoStates.get(key);
|
|
|
|
if (!state) {
|
|
return null;
|
|
}
|
|
|
|
// 获取历史记录(从旧到新)
|
|
const history = this.getInputHistory(nodeId, inputId, true);
|
|
|
|
// 如果没有历史记录,返回null
|
|
if (history.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
// 如果已经是最后一条记录,无法重做
|
|
if (state.currentIndex >= history.length - 1) {
|
|
return null;
|
|
}
|
|
|
|
// 更新当前位置
|
|
state.currentIndex++;
|
|
|
|
// 从历史记录中获取内容
|
|
const content = history[state.currentIndex]?.content || '';
|
|
|
|
// 更新状态
|
|
this.undoStates.set(key, state);
|
|
|
|
logger.debug(`历史缓存 | 重做操作 | 节点:${nodeId} | 输入框:${inputId} | 位置:${state.currentIndex}/${history.length - 1} | 内容:${content}`);
|
|
return content;
|
|
} catch (error) {
|
|
logger.error(`历史缓存 | 重做操作失败 | 节点:${nodeId} | 输入框:${inputId} | 错误:${error.message}`);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 检查是否可以撤销
|
|
*/
|
|
static canUndo(nodeId, inputId) {
|
|
try {
|
|
const key = `${nodeId}_${inputId}`;
|
|
const state = this.undoStates.get(key);
|
|
return state && state.currentIndex > 0;
|
|
} catch (error) {
|
|
logger.error(`历史缓存 | 检查撤销状态失败 | 节点:${nodeId} | 输入框:${inputId} | 错误:${error.message}`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 检查是否可以重做
|
|
*/
|
|
static canRedo(nodeId, inputId) {
|
|
try {
|
|
const key = `${nodeId}_${inputId}`;
|
|
const state = this.undoStates.get(key);
|
|
if (!state) return false;
|
|
|
|
const history = this.getInputHistory(nodeId, inputId, true);
|
|
return state.currentIndex < history.length - 1;
|
|
} catch (error) {
|
|
logger.error(`历史缓存 | 检查重做状态失败 | 节点:${nodeId} | 输入框:${inputId} | 错误:${error.message}`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 清除所有历史记录
|
|
*/
|
|
static clearAllHistory() {
|
|
try {
|
|
// 清除所有历史记录
|
|
this.saveAllHistory([]);
|
|
// 清除所有撤销状态
|
|
this.undoStates.clear();
|
|
// logger.debug("历史缓存 | 清除所有历史");
|
|
} catch (error) {
|
|
logger.error(`历史缓存 | 清除所有历史失败 | 错误:${error.message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 清除指定节点的历史记录
|
|
*/
|
|
static clearNodeHistory(nodeId) {
|
|
try {
|
|
// 获取所有历史记录
|
|
const allHistory = this.getAllHistory();
|
|
|
|
// 过滤出不属于该节点的记录
|
|
const filteredHistory = allHistory.filter(item => item.node_id !== nodeId);
|
|
|
|
// 保存过滤后的历史记录
|
|
this.saveAllHistory(filteredHistory);
|
|
|
|
// 清除该节点的撤销状态
|
|
for (const [key] of this.undoStates) {
|
|
if (key.startsWith(nodeId + "_")) {
|
|
this.undoStates.delete(key);
|
|
}
|
|
}
|
|
|
|
// logger.debug(`历史缓存 | 清除节点历史 | 节点:${nodeId}`);
|
|
} catch (error) {
|
|
logger.error(`历史缓存 | 清除节点历史失败 | 节点:${nodeId} | 错误:${error.message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 清除指定工作流的所有历史记录
|
|
*/
|
|
static clearWorkflowHistory(workflowId) {
|
|
if (!workflowId) return;
|
|
try {
|
|
const allHistory = this.getAllHistory();
|
|
const filteredHistory = allHistory.filter(item => item.workflow_id !== workflowId);
|
|
this.saveAllHistory(filteredHistory);
|
|
// logger.debug(`历史缓存 | 清除工作流历史 | 工作流ID:${workflowId}`);
|
|
} catch (error) {
|
|
logger.error(`历史缓存 | 清除工作流历史失败 | 工作流ID:${workflowId} | 错误:${error.message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 添加历史记录并更新撤销状态
|
|
*/
|
|
static addHistoryAndUpdateUndoState(nodeId, inputId, content, operationType = 'input') {
|
|
// 首先尝试添加历史记录
|
|
const success = this.addHistory({
|
|
node_id: nodeId,
|
|
input_id: inputId,
|
|
content: content,
|
|
operation_type: operationType,
|
|
timestamp: Date.now()
|
|
});
|
|
|
|
if (success) {
|
|
// 更新撤销状态
|
|
const key = `${nodeId}_${inputId}`;
|
|
const state = this.undoStates.get(key) || {
|
|
currentIndex: 0,
|
|
currentContent: content,
|
|
lastHistoryTimestamp: 0
|
|
};
|
|
|
|
// 获取最新的历史记录
|
|
const history = this.getInputHistory(nodeId, inputId, true);
|
|
|
|
// 更新状态
|
|
state.currentIndex = history.length - 1;
|
|
state.currentContent = content;
|
|
state.lastHistoryTimestamp = Date.now();
|
|
|
|
// 保存状态
|
|
this.undoStates.set(key, state);
|
|
|
|
// logger.debug(`历史缓存 | 更新撤销状态 | 节点:${nodeId} | 输入框:${inputId} | 位置:${state.currentIndex}`);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
/**
|
|
* 获取历史记录统计信息
|
|
*/
|
|
static getHistoryStats() {
|
|
try {
|
|
const allHistory = this.getAllHistory();
|
|
const stats = {
|
|
total: allHistory.length,
|
|
byNode: {}
|
|
};
|
|
|
|
// 按节点统计
|
|
allHistory.forEach(item => {
|
|
if (!stats.byNode[item.node_id]) {
|
|
stats.byNode[item.node_id] = 0;
|
|
}
|
|
stats.byNode[item.node_id]++;
|
|
});
|
|
|
|
// logger.debug(`历史缓存 | 获取统计 | 总数:${stats.total} | 节点数:${Object.keys(stats.byNode).length}`);
|
|
return stats;
|
|
} catch (error) {
|
|
logger.error(`历史缓存 | 获取统计失败 | 错误:${error.message}`);
|
|
return { total: 0, byNode: {} };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 修改历史记录项的操作类型
|
|
*/
|
|
static updateHistoryItemType(nodeId, inputId, timestamp, newType) {
|
|
try {
|
|
// 获取所有历史记录
|
|
const allHistory = this.getAllHistory();
|
|
|
|
// 查找匹配的历史记录
|
|
const itemIndex = allHistory.findIndex(item =>
|
|
item.node_id === nodeId &&
|
|
item.input_id === inputId &&
|
|
item.timestamp === timestamp
|
|
);
|
|
|
|
// 如果找到匹配的记录,修改其操作类型
|
|
if (itemIndex !== -1) {
|
|
allHistory[itemIndex].operation_type = newType;
|
|
|
|
// 保存修改后的历史记录
|
|
this.saveAllHistory(allHistory);
|
|
|
|
// logger.debug(`历史缓存 | 修改操作类型 | 节点:${nodeId} | 输入框:${inputId} | 类型:${newType}`);
|
|
return true;
|
|
} else {
|
|
// logger.debug(`历史缓存 | 修改操作类型失败 | 未找到匹配记录 | 节点:${nodeId} | 输入框:${inputId}`);
|
|
return false;
|
|
}
|
|
} catch (error) {
|
|
logger.error(`历史缓存 | 修改操作类型失败 | 错误:${error.message}`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 更新历史记录项
|
|
*/
|
|
static updateHistoryItem(nodeId, inputId, timestamp, updates) {
|
|
try {
|
|
// 获取所有历史记录
|
|
const allHistory = this.getAllHistory();
|
|
|
|
// 查找匹配的历史记录
|
|
const itemIndex = allHistory.findIndex(item =>
|
|
item.node_id === nodeId &&
|
|
item.input_id === inputId &&
|
|
item.timestamp === timestamp
|
|
);
|
|
|
|
// 如果找到匹配的记录,更新指定字段
|
|
if (itemIndex !== -1) {
|
|
// 更新所有提供的字段
|
|
Object.assign(allHistory[itemIndex], updates);
|
|
|
|
// 保存修改后的历史记录
|
|
this.saveAllHistory(allHistory);
|
|
|
|
logger.debug(`历史缓存 | 更新记录 | 节点:${nodeId} | 输入框:${inputId} | 字段:${Object.keys(updates).join(',')}`);
|
|
return true;
|
|
} else {
|
|
logger.debug(`历史缓存 | 更新失败 | 未找到匹配记录 | 节点:${nodeId} | 输入框:${inputId}`);
|
|
return false;
|
|
}
|
|
} catch (error) {
|
|
logger.error(`历史缓存 | 更新记录失败 | 错误:${error.message}`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 根据请求ID获取相关联的历史记录
|
|
*/
|
|
static getHistoryByRequestId(requestId) {
|
|
try {
|
|
if (!requestId) return [];
|
|
|
|
const allHistory = this.getAllHistory();
|
|
return allHistory.filter(item =>
|
|
item.request_id === requestId
|
|
).sort((a, b) => b.timestamp - a.timestamp);
|
|
} catch (error) {
|
|
logger.error(`历史缓存 | 获取请求关联历史失败 | 请求ID:${requestId} | 错误:${error.message}`);
|
|
return [];
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 标签缓存服务
|
|
* 管理标签的缓存操作
|
|
*/
|
|
class TagCacheService {
|
|
/**
|
|
* 获取或创建标签格式
|
|
*/
|
|
static getOrCreateFormats(rawTag) {
|
|
return PromptFormatter.formatTag(rawTag);
|
|
}
|
|
|
|
/**
|
|
* 获取所有标签缓存
|
|
*/
|
|
static getAllTagCache() {
|
|
try {
|
|
const data = CacheService.get(CACHE_CONFIG.TAG_CACHE_KEY);
|
|
return data || {};
|
|
} catch (error) {
|
|
logger.error(`标签缓存 | 获取所有缓存失败 | 错误:${error.message}`);
|
|
return {};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 保存所有标签缓存
|
|
*/
|
|
static saveAllTagCache(cache) {
|
|
try {
|
|
CacheService.set(CACHE_CONFIG.TAG_CACHE_KEY, cache);
|
|
} catch (error) {
|
|
logger.error(`标签缓存 | 保存所有缓存失败 | 错误:${error.message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取标签缓存
|
|
*/
|
|
static getTagCache(nodeId, inputId) {
|
|
try {
|
|
const allCache = this.getAllTagCache();
|
|
const workflowId = app.graph?._workflow_id || 'unknown';
|
|
const key = `${workflowId}_${nodeId}_${inputId}`;
|
|
const data = allCache[key] || {};
|
|
return new Map(Object.entries(data));
|
|
} catch (error) {
|
|
logger.error(`标签缓存 | 获取缓存失败 | 节点:${nodeId} | 输入框:${inputId} | 错误:${error.message}`);
|
|
return new Map();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 添加标签到缓存
|
|
*/
|
|
static addTag(nodeId, inputId, rawTag, formats) {
|
|
try {
|
|
const allCache = this.getAllTagCache();
|
|
const workflowId = app.graph?._workflow_id || 'unknown';
|
|
const key = `${workflowId}_${nodeId}_${inputId}`;
|
|
const cache = this.getTagCache(nodeId, inputId);
|
|
|
|
// 确保formats包含所有必要的格式
|
|
const format1 = formats.format1 || ` ${rawTag}`;
|
|
const format2 = formats.format2 || ` ${rawTag},`;
|
|
const format3 = formats.format3 || `, ${rawTag}`;
|
|
const format4 = formats.format4 || `, ${rawTag},`;
|
|
const insertedFormat = formats.insertedFormat || null;
|
|
|
|
// 如果标签已存在,直接更新
|
|
if (cache.has(rawTag)) {
|
|
cache.set(rawTag, {
|
|
format1,
|
|
format2,
|
|
format3,
|
|
format4,
|
|
insertedFormat
|
|
});
|
|
allCache[key] = Object.fromEntries(cache);
|
|
this.saveAllTagCache(allCache);
|
|
logger.debug(`标签缓存 | 更新标签 | 节点:${nodeId} | 输入框:${inputId} | 标签:${rawTag}`);
|
|
return true;
|
|
}
|
|
|
|
// 检查当前输入框的标签数量限制
|
|
if (cache.size >= CACHE_CONFIG.MAX_TAGS_PER_INPUT) {
|
|
// 获取所有标签及其最后使用时间
|
|
const tagEntries = Array.from(cache.entries()).map(([tag, formats]) => ({
|
|
tag,
|
|
formats,
|
|
lastUsed: formats.lastUsed || 0
|
|
}));
|
|
|
|
// 按最后使用时间排序
|
|
tagEntries.sort((a, b) => a.lastUsed - b.lastUsed);
|
|
|
|
// 删除最旧的标签
|
|
const oldestTag = tagEntries[0].tag;
|
|
cache.delete(oldestTag);
|
|
logger.debug(`标签缓存 | 自动淘汰 | 节点:${nodeId} | 输入框:${inputId} | 淘汰标签:${oldestTag}`);
|
|
}
|
|
|
|
// 检查全局标签数量限制
|
|
let totalTags = 0;
|
|
const allTags = [];
|
|
for (const [cacheKey, inputTags] of Object.entries(allCache)) {
|
|
const [wfId, nId, iId] = cacheKey.split('_');
|
|
for (const [tag, tagFormats] of Object.entries(inputTags)) {
|
|
allTags.push({
|
|
workflowId: wfId,
|
|
nodeId: nId,
|
|
inputId: iId,
|
|
tag,
|
|
lastUsed: tagFormats.lastUsed || 0
|
|
});
|
|
}
|
|
totalTags += Object.keys(inputTags).length;
|
|
}
|
|
|
|
if (totalTags >= CACHE_CONFIG.MAX_TAGS_GLOBAL) {
|
|
// 按最后使用时间排序
|
|
allTags.sort((a, b) => a.lastUsed - b.lastUsed);
|
|
|
|
// 删除最旧的标签
|
|
const oldest = allTags[0];
|
|
const oldKey = `${oldest.workflowId}_${oldest.nodeId}_${oldest.inputId}`;
|
|
const oldCache = new Map(Object.entries(allCache[oldKey] || {}));
|
|
oldCache.delete(oldest.tag);
|
|
|
|
if (oldCache.size > 0) {
|
|
allCache[oldKey] = Object.fromEntries(oldCache);
|
|
} else {
|
|
delete allCache[oldKey];
|
|
}
|
|
|
|
logger.debug(`标签缓存 | 自动淘汰 | 全局限制 | 淘汰标签:${oldest.tag} | 位置:${oldKey}`);
|
|
}
|
|
|
|
// 添加新标签
|
|
cache.set(rawTag, {
|
|
format1,
|
|
format2,
|
|
format3,
|
|
format4,
|
|
insertedFormat,
|
|
lastUsed: Date.now() // 添加最后使用时间
|
|
});
|
|
|
|
// 更新全局缓存
|
|
allCache[key] = Object.fromEntries(cache);
|
|
this.saveAllTagCache(allCache);
|
|
|
|
logger.debug(`标签缓存 | 添加标签 | 节点:${nodeId} | 输入框:${inputId} | 标签:${rawTag}`);
|
|
return true;
|
|
} catch (error) {
|
|
logger.error(`标签缓存 | 添加标签失败 | 节点:${nodeId} | 输入框:${inputId} | 标签:${rawTag} | 错误:${error.message}`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 从缓存中移除标签
|
|
*/
|
|
static removeTag(nodeId, inputId, rawTag) {
|
|
try {
|
|
const allCache = this.getAllTagCache();
|
|
const workflowId = app.graph?._workflow_id || 'unknown';
|
|
const key = `${workflowId}_${nodeId}_${inputId}`;
|
|
const cache = this.getTagCache(nodeId, inputId);
|
|
|
|
const result = cache.delete(rawTag);
|
|
if (result) {
|
|
// 更新全局缓存
|
|
if (cache.size > 0) {
|
|
allCache[key] = Object.fromEntries(cache);
|
|
} else {
|
|
delete allCache[key];
|
|
}
|
|
this.saveAllTagCache(allCache);
|
|
logger.debug(`标签缓存 | 移除标签 | 节点:${nodeId} | 输入框:${inputId} | 标签:${rawTag}`);
|
|
}
|
|
return result;
|
|
} catch (error) {
|
|
logger.error(`标签缓存 | 移除标签失败 | 节点:${nodeId} | 输入框:${inputId} | 标签:${rawTag} | 错误:${error.message}`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 检查标签是否在输入框中
|
|
*/
|
|
static isTagInInput(nodeId, inputId, rawTag, inputValue) {
|
|
try {
|
|
const formats = this.getTagFormats(nodeId, inputId, rawTag);
|
|
if (!formats) {
|
|
return false;
|
|
}
|
|
|
|
// 按优先级顺序检查各种格式
|
|
const checkOrder = ['insertedFormat', 'format4', 'format3', 'format2', 'format1'];
|
|
|
|
for (const formatKey of checkOrder) {
|
|
const format = formats[formatKey];
|
|
if (format && inputValue.includes(format)) {
|
|
// logger.debug(`标签检查 | 结果:已存在 | 节点:${nodeId} | 输入框:${inputId} | 标签:${rawTag} | 匹配格式:${formatKey}`);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
} catch (error) {
|
|
logger.error(`标签检查 | 结果:异常 | 节点:${nodeId} | 输入框:${inputId} | 标签:${rawTag} | 错误:${error.message}`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取标签的所有格式
|
|
*/
|
|
static getTagFormats(nodeId, inputId, rawTag) {
|
|
try {
|
|
const cache = this.getTagCache(nodeId, inputId);
|
|
return cache.get(rawTag) || null;
|
|
} catch (error) {
|
|
logger.error(`标签缓存 | 获取标签格式失败 | 节点:${nodeId} | 输入框:${inputId} | 标签:${rawTag} | 错误:${error.message}`);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取标签的插入格式
|
|
*/
|
|
static getInsertedFormat(nodeId, inputId, rawTag) {
|
|
try {
|
|
const formats = this.getTagFormats(nodeId, inputId, rawTag);
|
|
return formats ? formats.insertedFormat : null;
|
|
} catch (error) {
|
|
logger.error(`标签缓存 | 获取插入格式失败 | 节点:${nodeId} | 输入框:${inputId} | 标签:${rawTag} | 错误:${error.message}`);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取所有原始标签
|
|
*/
|
|
static getAllRawTags(nodeId, inputId) {
|
|
try {
|
|
const cache = this.getTagCache(nodeId, inputId);
|
|
return Array.from(cache.keys());
|
|
} catch (error) {
|
|
logger.error(`标签缓存 | 获取所有标签失败 | 节点:${nodeId} | 输入框:${inputId} | 错误:${error.message}`);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 清除标签缓存
|
|
*/
|
|
static clearCache(nodeId, inputId) {
|
|
try {
|
|
const allCache = this.getAllTagCache();
|
|
const workflowId = app.graph?._workflow_id || 'unknown';
|
|
const key = `${workflowId}_${nodeId}_${inputId}`;
|
|
|
|
// 删除指定节点的缓存
|
|
if (allCache[key]) {
|
|
delete allCache[key];
|
|
this.saveAllTagCache(allCache);
|
|
}
|
|
|
|
// logger.debug(`标签缓存 | 清除缓存 | 节点:${nodeId} | 输入框:${inputId}`);
|
|
} catch (error) {
|
|
logger.error(`标签缓存 | 清除缓存失败 | 节点:${nodeId} | 输入框:${inputId} | 错误:${error.message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取标签统计信息
|
|
*/
|
|
static getTagStats() {
|
|
try {
|
|
const allCache = this.getAllTagCache();
|
|
let total = 0;
|
|
const byNode = {};
|
|
|
|
// 遍历所有缓存计算统计信息
|
|
for (const [key, tags] of Object.entries(allCache)) {
|
|
const [workflowId, nodeId, inputId] = key.split('_');
|
|
const count = Object.keys(tags).length;
|
|
|
|
total += count;
|
|
if (!byNode[nodeId]) {
|
|
byNode[nodeId] = {};
|
|
}
|
|
byNode[nodeId][inputId] = count;
|
|
}
|
|
|
|
const stats = { total, byNode };
|
|
// logger.debug(`标签缓存 | 获取统计 | 总数:${total} | 节点数:${Object.keys(byNode).length}`);
|
|
return stats;
|
|
} catch (error) {
|
|
logger.error(`标签缓存 | 获取统计失败 | 错误:${error.message}`);
|
|
return { total: 0, byNode: {} };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 更新标签的插入格式
|
|
*/
|
|
static updateInsertedFormat(nodeId, inputId, rawTag, insertedFormat) {
|
|
try {
|
|
const allCache = this.getAllTagCache();
|
|
const workflowId = app.graph?._workflow_id || 'unknown';
|
|
const key = `${workflowId}_${nodeId}_${inputId}`;
|
|
const cache = this.getTagCache(nodeId, inputId);
|
|
const formats = cache.get(rawTag);
|
|
|
|
if (!formats) {
|
|
logger.debug(`标签缓存 | 更新插入格式失败 | 原因:标签不存在 | 节点:${nodeId} | 输入框:${inputId} | 标签:${rawTag}`);
|
|
return false;
|
|
}
|
|
|
|
// 更新插入格式
|
|
formats.insertedFormat = insertedFormat;
|
|
cache.set(rawTag, formats);
|
|
|
|
// 更新全局缓存
|
|
allCache[key] = Object.fromEntries(cache);
|
|
this.saveAllTagCache(allCache);
|
|
|
|
logger.debug(`标签缓存 | 更新插入格式 | 节点:${nodeId} | 输入框:${inputId} | 标签:${rawTag} | 新格式:${insertedFormat}`);
|
|
return true;
|
|
} catch (error) {
|
|
logger.error(`标签缓存 | 更新插入格式失败 | 节点:${nodeId} | 输入框:${inputId} | 标签:${rawTag} | 错误:${error.message}`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 更新标签的最后使用时间
|
|
*/
|
|
static updateTagLastUsed(nodeId, inputId, rawTag) {
|
|
try {
|
|
const allCache = this.getAllTagCache();
|
|
const workflowId = app.graph?._workflow_id || 'unknown';
|
|
const key = `${workflowId}_${nodeId}_${inputId}`;
|
|
const cache = this.getTagCache(nodeId, inputId);
|
|
|
|
if (cache.has(rawTag)) {
|
|
const tagData = cache.get(rawTag);
|
|
tagData.lastUsed = Date.now();
|
|
cache.set(rawTag, tagData);
|
|
allCache[key] = Object.fromEntries(cache);
|
|
this.saveAllTagCache(allCache);
|
|
logger.debug(`标签缓存 | 更新使用时间 | 节点:${nodeId} | 输入框:${inputId} | 标签:${rawTag}`);
|
|
}
|
|
} catch (error) {
|
|
logger.error(`标签缓存 | 更新使用时间失败 | 节点:${nodeId} | 输入框:${inputId} | 标签:${rawTag} | 错误:${error.message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 清除指定工作流的所有标签
|
|
*/
|
|
static clearWorkflowTags(workflowId) {
|
|
if (!workflowId) return;
|
|
try {
|
|
const allCache = this.getAllTagCache();
|
|
const newCache = {};
|
|
for (const key in allCache) {
|
|
// key format is `${workflowId}_${nodeId}_${inputId}`
|
|
if (!key.startsWith(workflowId + '_')) {
|
|
newCache[key] = allCache[key];
|
|
}
|
|
}
|
|
this.saveAllTagCache(newCache);
|
|
logger.debug(`标签缓存 | 清除工作流标签 | 工作流ID:${workflowId}`);
|
|
} catch (error) {
|
|
logger.error(`标签缓存 | 清除工作流标签失败 | 工作流ID:${workflowId} | 错误:${error.message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 清除所有标签缓存
|
|
*/
|
|
static clearAllTagCache() {
|
|
try {
|
|
CacheService.remove(CACHE_CONFIG.TAG_CACHE_KEY);
|
|
logger.debug("标签缓存 | 清除所有缓存");
|
|
} catch (error) {
|
|
logger.error(`标签缓存 | 清除缓存失败 | 错误:${error.message}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 翻译缓存服务
|
|
* 管理翻译结果的缓存操作
|
|
*/
|
|
class TranslateCacheService {
|
|
/**
|
|
* 获取所有翻译缓存
|
|
*/
|
|
static getAllTranslateCache() {
|
|
try {
|
|
const data = CacheService.get(CACHE_CONFIG.TRANSLATE_CACHE_KEY);
|
|
if (data) {
|
|
return new Map(Object.entries(data));
|
|
}
|
|
return new Map();
|
|
} catch (error) {
|
|
logger.error(`翻译缓存 | 获取缓存失败 | 错误:${error.message}`);
|
|
return new Map();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 保存所有翻译缓存
|
|
*/
|
|
static saveAllTranslateCache(cache) {
|
|
try {
|
|
// 将Map转换为普通对象
|
|
const data = Object.fromEntries(cache);
|
|
CacheService.set(CACHE_CONFIG.TRANSLATE_CACHE_KEY, data);
|
|
} catch (error) {
|
|
logger.error(`翻译缓存 | 保存缓存失败 | 错误:${error.message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 添加翻译缓存
|
|
*/
|
|
static addTranslateCache(sourceText, translatedText) {
|
|
try {
|
|
// 验证参数
|
|
if (!sourceText || !translatedText) {
|
|
logger.error("翻译缓存 | 添加缓存失败 | 缺少必要参数");
|
|
return false;
|
|
}
|
|
|
|
// 获取现有缓存
|
|
const cache = this.getAllTranslateCache();
|
|
|
|
// 添加或更新缓存
|
|
cache.set(sourceText, translatedText);
|
|
|
|
// 如果缓存过大,删除最旧的条目
|
|
if (cache.size > CACHE_CONFIG.MAX_TRANSLATE_CACHE) {
|
|
// 将Map转换为数组以便操作
|
|
const entries = Array.from(cache.entries());
|
|
// 删除最早的条目,保留最新的MAX_TRANSLATE_CACHE条
|
|
const newEntries = entries.slice(-CACHE_CONFIG.MAX_TRANSLATE_CACHE);
|
|
// 重建缓存
|
|
cache.clear();
|
|
newEntries.forEach(([key, value]) => cache.set(key, value));
|
|
}
|
|
|
|
// 保存缓存
|
|
this.saveAllTranslateCache(cache);
|
|
logger.debug(`翻译缓存 | 添加缓存 | 原文长度:${sourceText.length} | 译文长度:${translatedText.length}`);
|
|
return true;
|
|
} catch (error) {
|
|
logger.error(`翻译缓存 | 添加缓存失败 | 错误:${error.message}`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 查询翻译缓存
|
|
*/
|
|
static queryTranslateCache(text) {
|
|
try {
|
|
if (!text) return null;
|
|
|
|
const cache = this.getAllTranslateCache();
|
|
|
|
// 检查是否为原文(key)
|
|
if (cache.has(text)) {
|
|
const translatedText = cache.get(text);
|
|
// logger.debug(`翻译缓存 | 查询结果:命中原文 | 原文长度:${text.length} | 译文长度:${translatedText.length}`);
|
|
return {
|
|
type: 'source',
|
|
text: text,
|
|
translatedText: translatedText
|
|
};
|
|
}
|
|
|
|
// 检查是否为译文(value)
|
|
for (const [source, translated] of cache.entries()) {
|
|
if (translated === text) {
|
|
logger.debug(`翻译缓存 | 查询结果:命中译文 | 原文长度:${source.length} | 译文长度:${text.length}`);
|
|
return {
|
|
type: 'translated',
|
|
text: text,
|
|
sourceText: source
|
|
};
|
|
}
|
|
}
|
|
|
|
// 未命中缓存
|
|
logger.debug(`翻译缓存 | 查询结果:未命中 | 文本长度:${text.length}`);
|
|
return null;
|
|
} catch (error) {
|
|
logger.error(`翻译缓存 | 查询缓存失败 | 错误:${error.message}`);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 清除所有翻译缓存
|
|
*/
|
|
static clearAllTranslateCache() {
|
|
try {
|
|
CacheService.remove(CACHE_CONFIG.TRANSLATE_CACHE_KEY);
|
|
logger.debug("翻译缓存 | 清除所有缓存");
|
|
} catch (error) {
|
|
logger.error(`翻译缓存 | 清除缓存失败 | 错误:${error.message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取翻译缓存统计信息
|
|
*/
|
|
static getTranslateCacheStats() {
|
|
try {
|
|
const cache = this.getAllTranslateCache();
|
|
const stats = {
|
|
total: cache.size,
|
|
sourceTextLengths: 0,
|
|
translatedTextLengths: 0
|
|
};
|
|
|
|
// 计算原文和译文的总长度
|
|
for (const [source, translated] of cache.entries()) {
|
|
stats.sourceTextLengths += source.length;
|
|
stats.translatedTextLengths += translated.length;
|
|
}
|
|
|
|
logger.debug(`翻译缓存 | 获取统计 | 总数:${stats.total} | 原文总长度:${stats.sourceTextLengths} | 译文总长度:${stats.translatedTextLengths}`);
|
|
return stats;
|
|
} catch (error) {
|
|
logger.error(`翻译缓存 | 获取统计失败 | 错误:${error.message}`);
|
|
return { total: 0, sourceTextLengths: 0, translatedTextLengths: 0 };
|
|
}
|
|
}
|
|
}
|
|
|
|
export { CacheService, HistoryCacheService, TagCacheService, TranslateCacheService, CACHE_CONFIG }; |