taSummaryStore is now a Class and is using taStorage. see #675 #580

This commit is contained in:
Mic 2026-03-23 18:39:21 +01:00
parent 6d53e108d0
commit 83b8544404
3 changed files with 156 additions and 52 deletions

View file

@ -28,6 +28,9 @@ export class taStorage {
taLog = null; taLog = null;
/**
* @param {boolean} [do_debug=false] - Enable debug logging.
*/
constructor(do_debug = false) { constructor(do_debug = false) {
this.taLog = new taLogger("mzta-storage", do_debug); this.taLog = new taLogger("mzta-storage", do_debug);
} }
@ -179,10 +182,11 @@ export class taStorage {
/** /**
* Write the summary field for a given Message-ID. * Write the summary field for a given Message-ID.
* @param {string} messageId - The Message-ID header string. * @param {string} messageId - The Message-ID header string.
* @param {string} text - The summary text. * @param {object} summary_data - The summary data object with fields:
* summary, error, message, summary_date.
* @param {boolean} [force=true] - If true, overwrite existing summary data. * @param {boolean} [force=true] - If true, overwrite existing summary data.
*/ */
async writeSummary(messageId, text, force = true) { async writeSummary(messageId, summary_data, force = true) {
this.taLog.log('[writeSummary] messageId: ' + messageId + ', force: ' + force); this.taLog.log('[writeSummary] messageId: ' + messageId + ', force: ' + force);
try { try {
let key = this._buildKey(messageId); let key = this._buildKey(messageId);
@ -192,7 +196,15 @@ export class taStorage {
return; return;
} }
let now = Date.now(); let now = Date.now();
record[taStorage.FIELD_SUMMARY] = { text: text, ts: now }; record[taStorage.FIELD_SUMMARY] = {
summary: summary_data.summary,
error: summary_data.error || false,
message: summary_data.message || '',
summary_date: summary_data.summary_date instanceof Date
? summary_data.summary_date.toISOString()
: summary_data.summary_date,
ts: now,
};
record.ts = now; record.ts = now;
await messenger.storage.local.set({ [key]: record }); await messenger.storage.local.set({ [key]: record });
} catch (e) { } catch (e) {
@ -200,6 +212,64 @@ export class taStorage {
} }
} }
/**
* Get all records that contain a summary field.
* @returns {Promise<object>} Map of messageId -> summary data object.
*/
async getAllSummaryRecords() {
this.taLog.log('[getAllSummaryRecords] loading all summary records');
try {
let all = await messenger.storage.local.get(null);
let result = {};
for (let [key, record] of Object.entries(all)) {
if (!key.startsWith(taStorage.STORAGE_KEY_PREFIX)) continue;
if (!this.hasField(record, taStorage.FIELD_SUMMARY)) continue;
let messageId = key.slice(taStorage.STORAGE_KEY_PREFIX.length);
let summary = record[taStorage.FIELD_SUMMARY];
result[messageId] = {
headerMessageId: messageId,
summary: summary.summary,
error: summary.error || false,
message: summary.message || '',
summary_date: new Date(summary.summary_date || summary.ts),
};
}
this.taLog.log('[getAllSummaryRecords] found ' + Object.keys(result).length + ' summary records');
return result;
} catch (e) {
this.taLog.error('getAllSummaryRecords error: ' + e);
return {};
}
}
/**
* Delete only the summary field from a record.
* Deletes the entire record if no other data fields remain.
* @param {string} messageId - The Message-ID header string.
*/
async deleteSummaryField(messageId) {
this.taLog.log('[deleteSummaryField] messageId: ' + messageId);
try {
let key = this._buildKey(messageId);
let record = await this.getRecord(messageId);
if (!record || !(taStorage.FIELD_SUMMARY in record)) {
this.taLog.log('[deleteSummaryField] no summary field found for messageId: ' + messageId);
return;
}
delete record[taStorage.FIELD_SUMMARY];
const remainingFields = Object.keys(record).filter(k => k !== 'v' && k !== 'ts');
if (remainingFields.length === 0) {
this.taLog.log('[deleteSummaryField] no remaining fields, deleting entire record');
await messenger.storage.local.remove(key);
} else {
this.taLog.log('[deleteSummaryField] remaining fields: ' + remainingFields.join(', '));
await messenger.storage.local.set({ [key]: record });
}
} catch (e) {
this.taLog.error('deleteSummaryField error: ' + e);
}
}
/** /**
* Write the translation field for a given Message-ID. * Write the translation field for a given Message-ID.
* @param {string} messageId - The Message-ID header string. * @param {string} messageId - The Message-ID header string.

View file

@ -16,30 +16,49 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
export const taSummaryStore = { import { taStorage } from './mzta-storage.js';
logger: console, import { taLogger } from './mzta-logger.js';
_data_prefix: 'mzta-summary-',
_processing_prefix: 'mzta-summary-processing-', export class taSummaryStore {
_max_summaries: 100,
_processing_prefix = 'mzta-summary-processing-';
_max_summaries = 100;
_storage = null;
taLog = null;
constructor(do_debug = false) {
this._storage = new taStorage(do_debug);
this.taLog = new taLogger('mzta-summarystore', do_debug);
}
async setProcessing(data_id) { async setProcessing(data_id) {
this.taLog.log("[setProcessing] data_id: " + data_id);
const key = this._processing_prefix + data_id; const key = this._processing_prefix + data_id;
await browser.storage.session.set({ [key]: true }); await browser.storage.session.set({ [key]: true });
}, }
async isProcessing(data_id) { async isProcessing(data_id) {
this.taLog.log("[isProcessing] data_id: " + data_id);
const key = this._processing_prefix + data_id; const key = this._processing_prefix + data_id;
let output = await browser.storage.session.get(key); let output = await browser.storage.session.get(key);
return output[key] || false; let result = output[key] || false;
}, this.taLog.log("[isProcessing] result: " + result);
return result;
}
async saveSummary(data, data_id) { async saveSummary(data, data_id) {
const key = this._data_prefix + data_id; this.taLog.log("[saveSummary] data_id: " + data_id);
await browser.storage.session.set({ [key]: data }); try {
await browser.storage.session.remove(this._processing_prefix + data_id); await this._storage.writeSummary(data_id, data, true);
}, await browser.storage.session.remove(this._processing_prefix + data_id);
} catch (e) {
this.taLog.error("[saveSummary] error: " + e);
throw e;
}
}
async saveError(data_id, error_message) { async saveError(data_id, error_message) {
this.taLog.log("[saveError] data_id: " + data_id + ", error_message: " + error_message);
let data = { let data = {
error: true, error: true,
message: error_message, message: error_message,
@ -48,53 +67,67 @@ export const taSummaryStore = {
}; };
await this.saveSummary(data, data_id); await this.saveSummary(data, data_id);
return data; return data;
}, }
async loadSummary(data_id) { async loadSummary(data_id) {
const key = this._data_prefix + data_id; this.taLog.log("[loadSummary] data_id: " + data_id);
let output = await browser.storage.session.get(key); let record = await this._storage.getRecord(data_id);
return output[key] || null; if (!record || !this._storage.hasField(record, taStorage.FIELD_SUMMARY)) {
}, this.taLog.log("[loadSummary] no record found for data_id: " + data_id);
return null;
}
let summary = record.summary;
return {
headerMessageId: data_id,
summary: summary.summary,
error: summary.error || false,
message: summary.message || '',
summary_date: new Date(summary.summary_date || summary.ts),
};
}
async removeSummary(data_id) { async removeSummary(data_id) {
const key = this._data_prefix + data_id; this.taLog.log("[removeSummary] data_id: " + data_id);
await browser.storage.session.remove(key); await this._storage.deleteSummaryField(data_id);
await browser.storage.session.remove(this._processing_prefix + data_id); await browser.storage.session.remove(this._processing_prefix + data_id);
}, }
async getAllSummaries() { async getAllSummaries() {
let allData = await browser.storage.session.get(null); this.taLog.log("[getAllSummaries] loading all summaries");
let summaryData = {}; return await this._storage.getAllSummaryRecords();
}
for (const [key, value] of Object.entries(allData)) {
if (key.startsWith(this._data_prefix)) {
summaryData[key.replace(this._data_prefix, '')] = value;
}
}
return summaryData;
},
async clearSummaries() { async clearSummaries() {
let allData = await browser.storage.session.get(null); this.taLog.log("[clearSummaries] clearing all summary data");
let keysToDelete = Object.keys(allData).filter(key => key.startsWith(this._data_prefix) || key.startsWith(this._processing_prefix)); let allSummaries = await this._storage.getAllSummaryRecords();
let summaryKeys = Object.keys(allSummaries);
this.taLog.log("[clearSummaries] deleting " + summaryKeys.length + " summary records");
for (let messageId of summaryKeys) {
await this._storage.deleteSummaryField(messageId);
}
let allSession = await browser.storage.session.get(null);
let keysToDelete = Object.keys(allSession).filter(k => k.startsWith(this._processing_prefix));
this.taLog.log("[clearSummaries] deleting " + keysToDelete.length + " session keys");
for (let key of keysToDelete) { for (let key of keysToDelete) {
await browser.storage.session.remove(key); await browser.storage.session.remove(key);
} }
}, }
async truncSummaries() { async truncSummaries() {
let data = await this.getAllSummaries(); this.taLog.log("[truncSummaries] checking summary count");
let data = await this._storage.getAllSummaryRecords();
let sortedData = this.sortSummariesByDate(data); let sortedData = this.sortSummariesByDate(data);
let keys = Object.keys(sortedData); let keys = Object.keys(sortedData);
this.taLog.log("[truncSummaries] total summaries: " + keys.length + ", max: " + this._max_summaries);
if (keys.length > this._max_summaries) { if (keys.length > this._max_summaries) {
let toDelete = keys.length - this._max_summaries;
this.taLog.log("[truncSummaries] truncating " + toDelete + " oldest summaries");
for (let i = this._max_summaries; i < keys.length; i++) { for (let i = this._max_summaries; i < keys.length; i++) {
await browser.storage.session.remove(this._data_prefix + keys[i]); await this._storage.deleteSummaryField(keys[i]);
} }
} }
}, }
sortSummariesByDate(data) { sortSummariesByDate(data) {
if (!data) return {}; if (!data) return {};
@ -112,4 +145,4 @@ export const taSummaryStore = {
return sortedSummaries; return sortedSummaries;
} }
}; }

View file

@ -96,6 +96,7 @@ await reload_pref_init();
let taLog = new taLogger("mzta-background",prefs_init.do_debug); let taLog = new taLogger("mzta-background",prefs_init.do_debug);
taWorkingStatus.taLog = taLog; taWorkingStatus.taLog = taLog;
let spamReport = new taSpamReport(prefs_init.do_debug); let spamReport = new taSpamReport(prefs_init.do_debug);
let summaryStore = new taSummaryStore(prefs_init.do_debug);
let special_prompts_ids = getActiveSpecialPromptsIDs({ let special_prompts_ids = getActiveSpecialPromptsIDs({
addtags: prefs_init.add_tags, addtags: prefs_init.add_tags,
@ -227,13 +228,13 @@ messenger.runtime.onMessage.addListener((message, sender, sendResponse) => {
let message = await browser.messageDisplay.getDisplayedMessage(tabId); let message = await browser.messageDisplay.getDisplayedMessage(tabId);
if (!message) return; if (!message) return;
let cachedSummary = await taSummaryStore.loadSummary(message.headerMessageId); let cachedSummary = await summaryStore.loadSummary(message.headerMessageId);
if (cachedSummary && !cachedSummary.error) { if (cachedSummary && !cachedSummary.error) {
browser.tabs.sendMessage(tabId, { command: "showSummary", data: cachedSummary }); browser.tabs.sendMessage(tabId, { command: "showSummary", data: cachedSummary });
return; return;
} }
if (await taSummaryStore.isProcessing(message.headerMessageId)) { if (await summaryStore.isProcessing(message.headerMessageId)) {
browser.tabs.sendMessage(tabId, { command: "showSummaryGenerating" }); browser.tabs.sendMessage(tabId, { command: "showSummaryGenerating" });
return; return;
} }
@ -265,7 +266,7 @@ messenger.runtime.onMessage.addListener((message, sender, sendResponse) => {
case 'refreshSummary': case 'refreshSummary':
async function _refreshSummary(message) { async function _refreshSummary(message) {
let tabId = sender.tab.id; let tabId = sender.tab.id;
await taSummaryStore.removeSummary(message.headerMessageId); await summaryStore.removeSummary(message.headerMessageId);
await _generateSummaryForMessage(message.headerMessageId, tabId); await _generateSummaryForMessage(message.headerMessageId, tabId);
} }
_refreshSummary(message); _refreshSummary(message);
@ -445,23 +446,23 @@ async function _generateSummaryForMessage(headerMessageId, tabId) {
...getDynamicSettingsDefaults(['use_specific_integration', 'connection_type']) ...getDynamicSettingsDefaults(['use_specific_integration', 'connection_type'])
}); });
let cachedSummary = await taSummaryStore.loadSummary(headerMessageId); let cachedSummary = await summaryStore.loadSummary(headerMessageId);
if (cachedSummary && !cachedSummary.error) { if (cachedSummary && !cachedSummary.error) {
browser.tabs.sendMessage(tabId, { command: "showSummary", data: cachedSummary }); browser.tabs.sendMessage(tabId, { command: "showSummary", data: cachedSummary });
return; return;
} }
if (await taSummaryStore.isProcessing(headerMessageId)) { if (await summaryStore.isProcessing(headerMessageId)) {
browser.tabs.sendMessage(tabId, { command: "showSummaryGenerating" }); browser.tabs.sendMessage(tabId, { command: "showSummaryGenerating" });
return; return;
} }
await taSummaryStore.setProcessing(headerMessageId); await summaryStore.setProcessing(headerMessageId);
browser.tabs.sendMessage(tabId, { command: "showSummaryGenerating" }); browser.tabs.sendMessage(tabId, { command: "showSummaryGenerating" });
const messageResult = await browser.messages.query({ headerMessageId: headerMessageId }); const messageResult = await browser.messages.query({ headerMessageId: headerMessageId });
if (!messageResult || messageResult.messages.length === 0) { if (!messageResult || messageResult.messages.length === 0) {
await taSummaryStore.saveError(headerMessageId, "Message not found"); await summaryStore.saveError(headerMessageId, "Message not found");
browser.tabs.sendMessage(tabId, { command: "showSummary", data: { error: true, message: "Message not found" } }); browser.tabs.sendMessage(tabId, { command: "showSummary", data: { error: true, message: "Message not found" } });
return; return;
} }
@ -479,7 +480,7 @@ async function _generateSummaryForMessage(headerMessageId, tabId) {
if (connectionType === 'chatgpt_web') { if (connectionType === 'chatgpt_web') {
const errorMsg = browser.i18n.getMessage('summarize_chatgpt_web_not_supported'); const errorMsg = browser.i18n.getMessage('summarize_chatgpt_web_not_supported');
await taSummaryStore.saveError(headerMessageId, errorMsg); await summaryStore.saveError(headerMessageId, errorMsg);
browser.tabs.sendMessage(tabId, { command: "showSummary", data: { error: true, message: errorMsg } }); browser.tabs.sendMessage(tabId, { command: "showSummary", data: { error: true, message: errorMsg } });
return; return;
} }
@ -503,12 +504,12 @@ async function _generateSummaryForMessage(headerMessageId, tabId) {
summary_date: new Date(), summary_date: new Date(),
headerMessageId: headerMessageId headerMessageId: headerMessageId
}; };
await taSummaryStore.saveSummary(summaryData, headerMessageId); await summaryStore.saveSummary(summaryData, headerMessageId);
browser.tabs.sendMessage(tabId, { command: "showSummary", data: summaryData }); browser.tabs.sendMessage(tabId, { command: "showSummary", data: summaryData });
} catch (error) { } catch (error) {
console.error("[ThunderAI] Error generating summary:", error); console.error("[ThunderAI] Error generating summary:", error);
await taSummaryStore.saveError(headerMessageId, error.message || String(error)); await summaryStore.saveError(headerMessageId, error.message || String(error));
browser.tabs.sendMessage(tabId, { command: "showSummary", data: { error: true, message: error.message || "Failed to generate summary" } }); browser.tabs.sendMessage(tabId, { command: "showSummary", data: { error: true, message: error.message || "Failed to generate summary" } });
} }
} }