Add custom logging framework and debug option
This commit is contained in:
parent
8eb6812f54
commit
656d0322e6
8 changed files with 147 additions and 72 deletions
|
@ -13,5 +13,6 @@
|
|||
"template.qwen": { "message": "Qwen" },
|
||||
"template.mistral": { "message": "Mistral" },
|
||||
"template.custom": { "message": "Custom" },
|
||||
"options.save": { "message": "Save" }
|
||||
"options.save": { "message": "Save" },
|
||||
"options.debugLogging": { "message": "Enable debug logging" }
|
||||
}
|
||||
|
|
|
@ -10,56 +10,59 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
let logger;
|
||||
// Startup
|
||||
console.log("[ai-filter] background.js loaded – ready to classify");
|
||||
(async () => {
|
||||
logger = await import(browser.runtime.getURL("logger.js"));
|
||||
logger.aiLog("background.js loaded – ready to classify", {debug: true});
|
||||
try {
|
||||
const store = await browser.storage.local.get(["endpoint", "templateName", "customTemplate", "customSystemPrompt", "aiParams"]);
|
||||
const store = await browser.storage.local.get(["endpoint", "templateName", "customTemplate", "customSystemPrompt", "aiParams", "debugLogging"]);
|
||||
logger.setDebug(store.debugLogging);
|
||||
await browser.aiFilter.initConfig(store);
|
||||
console.log("[ai-filter] configuration loaded", store);
|
||||
logger.aiLog("configuration loaded", {debug: true}, store);
|
||||
try {
|
||||
await browser.DomContentScript.registerWindow(
|
||||
"chrome://messenger/content/FilterEditor.xhtml",
|
||||
"resource://aifilter/content/filterEditor.js"
|
||||
);
|
||||
console.log("[ai-filter] registered FilterEditor content script");
|
||||
logger.aiLog("registered FilterEditor content script", {debug: true});
|
||||
} catch (e) {
|
||||
console.error("[ai-filter] failed to register content script", e);
|
||||
logger.aiLog("failed to register content script", {level: 'error'}, e);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("[ai-filter] failed to load config:", err);
|
||||
logger.aiLog("failed to load config", {level: 'error'}, err);
|
||||
}
|
||||
})();
|
||||
|
||||
// Listen for messages from UI/devtools
|
||||
browser.runtime.onMessage.addListener((msg) => {
|
||||
console.log("[ai-filter] onMessage received:", msg);
|
||||
logger.aiLog("onMessage received", {debug: true}, msg);
|
||||
|
||||
if (msg?.type === "aiFilter:test") {
|
||||
const { text = "", criterion = "" } = msg;
|
||||
console.log("[ai-filter] aiFilter:test – text:", text);
|
||||
console.log("[ai-filter] aiFilter:test – criterion:", criterion);
|
||||
logger.aiLog("aiFilter:test – text", {debug: true}, text);
|
||||
logger.aiLog("aiFilter:test – criterion", {debug: true}, criterion);
|
||||
|
||||
try {
|
||||
console.log("[ai-filter] Calling browser.aiFilter.classify()");
|
||||
logger.aiLog("Calling browser.aiFilter.classify()", {debug: true});
|
||||
const result = browser.aiFilter.classify(text, criterion);
|
||||
console.log("[ai-filter] classify() returned:", result);
|
||||
logger.aiLog("classify() returned", {debug: true}, result);
|
||||
return { match: result };
|
||||
}
|
||||
catch (err) {
|
||||
console.error("[ai-filter] Error in classify():", err);
|
||||
logger.aiLog("Error in classify()", {level: 'error'}, err);
|
||||
// rethrow so the caller sees the failure
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.warn("[ai-filter] Unknown message type, ignoring:", msg?.type);
|
||||
logger.aiLog("Unknown message type, ignoring", {level: 'warn'}, msg?.type);
|
||||
}
|
||||
});
|
||||
|
||||
// Catch any unhandled rejections
|
||||
window.addEventListener("unhandledrejection", ev => {
|
||||
console.error("[ai-filter] Unhandled promise rejection:", ev.reason);
|
||||
logger.aiLog("Unhandled promise rejection", {level: 'error'}, ev.reason);
|
||||
});
|
||||
|
||||
browser.runtime.onInstalled.addListener(async ({ reason }) => {
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
var { ExtensionCommon } = ChromeUtils.importESModule("resource://gre/modules/ExtensionCommon.sys.mjs");
|
||||
var { Services } = globalThis || ChromeUtils.importESModule("resource://gre/modules/Services.sys.mjs");
|
||||
var { MailServices } = ChromeUtils.importESModule("resource:///modules/MailServices.sys.mjs");
|
||||
var { aiLog, setDebug } = ChromeUtils.import("resource://aifilter/modules/logger.jsm");
|
||||
|
||||
console.log("[ai-filter][api] Experiment API module loaded");
|
||||
aiLog("[api] Experiment API module loaded", {debug: true});
|
||||
|
||||
var resProto = Cc["@mozilla.org/network/protocol;1?name=resource"]
|
||||
.getService(Ci.nsISubstitutingProtocolHandler);
|
||||
|
||||
function registerResourceUrl(extension, namespace) {
|
||||
console.log(`[ai-filter][api] registerResourceUrl called for namespace="${namespace}"`);
|
||||
aiLog(`[api] registerResourceUrl called for namespace="${namespace}"`, {debug: true});
|
||||
if (resProto.hasSubstitution(namespace)) {
|
||||
console.log(`[ai-filter][api] namespace="${namespace}" already registered, skipping`);
|
||||
aiLog(`[api] namespace="${namespace}" already registered, skipping`, {debug: true});
|
||||
return;
|
||||
}
|
||||
let uri = Services.io.newURI(".", null, extension.rootURI);
|
||||
console.log(`[ai-filter][api] setting substitution for "${namespace}" → ${uri.spec}`);
|
||||
aiLog(`[api] setting substitution for "${namespace}" → ${uri.spec}`, {debug: true});
|
||||
resProto.setSubstitutionWithFlags(namespace, uri, resProto.ALLOW_CONTENT_ACCESS);
|
||||
}
|
||||
|
||||
|
@ -23,63 +24,66 @@ var AIFilterMod;
|
|||
|
||||
var aiFilter = class extends ExtensionCommon.ExtensionAPI {
|
||||
async onStartup() {
|
||||
console.log("[ai-filter][api] onStartup()");
|
||||
aiLog("[api] onStartup()", {debug: true});
|
||||
let { extension } = this;
|
||||
|
||||
registerResourceUrl(extension, "aifilter");
|
||||
|
||||
|
||||
try {
|
||||
console.log("[ai-filter][api] importing ExpressionSearchFilter.jsm");
|
||||
aiLog("[api] importing ExpressionSearchFilter.jsm", {debug: true});
|
||||
AIFilterMod = ChromeUtils.import("resource://aifilter/modules/ExpressionSearchFilter.jsm");
|
||||
console.log("[ai-filter][api] ExpressionSearchFilter.jsm import succeeded");
|
||||
aiLog("[api] ExpressionSearchFilter.jsm import succeeded", {debug: true});
|
||||
}
|
||||
catch (err) {
|
||||
console.error("[ai-filter][api] failed to import ExpressionSearchFilter.jsm:", err);
|
||||
aiLog("[api] failed to import ExpressionSearchFilter.jsm", {level: 'error'}, err);
|
||||
}
|
||||
}
|
||||
|
||||
onShutdown(isAppShutdown) {
|
||||
console.log("[ai-filter][api] onShutdown(), isAppShutdown =", isAppShutdown);
|
||||
aiLog("[api] onShutdown()", {debug: true}, isAppShutdown);
|
||||
if (!isAppShutdown && resProto.hasSubstitution("aifilter")) {
|
||||
console.log("[ai-filter][api] removing substitution for namespace='aifilter'");
|
||||
aiLog("[api] removing substitution for namespace='aifilter'", {debug: true});
|
||||
resProto.setSubstitution("aifilter", null);
|
||||
}
|
||||
}
|
||||
|
||||
getAPI(context) {
|
||||
console.log("[ai-filter][api] getAPI()");
|
||||
aiLog("[api] getAPI()", {debug: true});
|
||||
return {
|
||||
aiFilter: {
|
||||
initConfig: async (config) => {
|
||||
try {
|
||||
if (AIFilterMod?.AIFilter?.setConfig) {
|
||||
AIFilterMod.AIFilter.setConfig(config);
|
||||
console.log("[ai-filter][api] configuration applied", config);
|
||||
if (typeof config.debugLogging === "boolean") {
|
||||
setDebug(config.debugLogging);
|
||||
}
|
||||
aiLog("[api] configuration applied", {debug: true}, config);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("[ai-filter][api] failed to apply config:", err);
|
||||
aiLog("[api] failed to apply config", {level: 'error'}, err);
|
||||
}
|
||||
},
|
||||
classify: (msg) => {
|
||||
console.log("[ai-filter][api] classify() called with msg:", msg);
|
||||
aiLog("[api] classify() called with msg", {debug: true}, msg);
|
||||
try {
|
||||
if (!gTerm) {
|
||||
console.log("[ai-filter][api] instantiating new ClassificationTerm");
|
||||
aiLog("[api] instantiating new ClassificationTerm", {debug: true});
|
||||
let mod = AIFilterMod || ChromeUtils.import("resource://aifilter/modules/ExpressionSearchFilter.jsm");
|
||||
gTerm = new mod.ClassificationTerm();
|
||||
}
|
||||
console.log("[ai-filter][api] calling gTerm.match()");
|
||||
aiLog("[api] calling gTerm.match()", {debug: true});
|
||||
let matchResult = gTerm.match(
|
||||
msg.msgHdr,
|
||||
msg.value,
|
||||
Ci.nsMsgSearchOp.Contains
|
||||
);
|
||||
console.log("[ai-filter][api] gTerm.match() returned:", matchResult);
|
||||
aiLog("[api] gTerm.match() returned", {debug: true}, matchResult);
|
||||
return matchResult;
|
||||
}
|
||||
catch (err) {
|
||||
console.error("[ai-filter][api] error in classify():", err);
|
||||
aiLog("[api] error in classify()", {level: 'error'}, err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
|
24
logger.js
Normal file
24
logger.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
let debugEnabled = false;
|
||||
export function setDebug(value) {
|
||||
debugEnabled = !!value;
|
||||
}
|
||||
|
||||
function getCaller() {
|
||||
try {
|
||||
const stack = new Error().stack.split("\n");
|
||||
if (stack.length >= 3) {
|
||||
return stack[2].trim().replace(/^at\s+/, '');
|
||||
}
|
||||
} catch (e) {}
|
||||
return '';
|
||||
}
|
||||
|
||||
export function aiLog(message, opts = {}, ...args) {
|
||||
const { level = 'log', debug = false } = opts;
|
||||
if (debug && !debugEnabled) {
|
||||
return;
|
||||
}
|
||||
const caller = getCaller();
|
||||
const prefix = caller ? `[ai-filter][${caller}]` : '[ai-filter]';
|
||||
console[level](`%c${prefix}`, 'color:#1c92d2;font-weight:bold', message, ...args);
|
||||
}
|
|
@ -5,6 +5,7 @@ var { Services } = globalThis || ChromeUtils.importESModule("resource://g
|
|||
var { NetUtil } = ChromeUtils.importESModule("resource://gre/modules/NetUtil.sys.mjs");
|
||||
var { MimeParser } = ChromeUtils.importESModule("resource:///modules/mimeParser.sys.mjs");
|
||||
var { FileUtils } = ChromeUtils.importESModule("resource://gre/modules/FileUtils.sys.mjs");
|
||||
var { aiLog, setDebug } = ChromeUtils.import("resource://aifilter/modules/logger.jsm");
|
||||
|
||||
function sha256Hex(str) {
|
||||
const hasher = Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
|
||||
|
@ -41,36 +42,36 @@ class CustomerTermBase {
|
|||
this._cacheFile.append("aifilter_cache.json");
|
||||
this._loadCache();
|
||||
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] Initialized term base "${this.id}"`);
|
||||
aiLog(`[ExpressionSearchFilter] Initialized term base "${this.id}"`, {debug: true});
|
||||
}
|
||||
|
||||
_loadCache() {
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] Loading cache from ${this._cacheFile.path}`);
|
||||
aiLog(`[ExpressionSearchFilter] Loading cache from ${this._cacheFile.path}` , {debug: true});
|
||||
try {
|
||||
if (this._cacheFile.exists()) {
|
||||
let stream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
|
||||
stream.init(this._cacheFile, -1, 0, 0);
|
||||
let data = NetUtil.readInputStreamToString(stream, stream.available());
|
||||
stream.close();
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] Cache file contents: ${data}`);
|
||||
aiLog(`[ExpressionSearchFilter] Cache file contents: ${data}`, {debug: true});
|
||||
let obj = JSON.parse(data);
|
||||
for (let [k, v] of Object.entries(obj)) {
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] ⮡ Loaded entry '${k}' → ${v}`);
|
||||
aiLog(`[ExpressionSearchFilter] ⮡ Loaded entry '${k}' → ${v}`, {debug: true});
|
||||
this.cache.set(k, v);
|
||||
}
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] Loaded ${this.cache.size} cache entries`);
|
||||
aiLog(`[ExpressionSearchFilter] Loaded ${this.cache.size} cache entries`, {debug: true});
|
||||
} else {
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] Cache file does not exist`);
|
||||
aiLog(`[ExpressionSearchFilter] Cache file does not exist`, {debug: true});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`[ai-filter][ExpressionSearchFilter] Failed to load cache`, e);
|
||||
aiLog(`Failed to load cache`, {level: 'error'}, e);
|
||||
}
|
||||
}
|
||||
|
||||
_saveCache(updatedKey, updatedValue) {
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] Saving cache to ${this._cacheFile.path}`);
|
||||
aiLog(`[ExpressionSearchFilter] Saving cache to ${this._cacheFile.path}`, {debug: true});
|
||||
if (typeof updatedKey !== "undefined") {
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] ⮡ Persisting entry '${updatedKey}' → ${updatedValue}`);
|
||||
aiLog(`[ExpressionSearchFilter] ⮡ Persisting entry '${updatedKey}' → ${updatedValue}`, {debug: true});
|
||||
}
|
||||
try {
|
||||
let obj = Object.fromEntries(this.cache);
|
||||
|
@ -83,39 +84,39 @@ class CustomerTermBase {
|
|||
stream.write(data, data.length);
|
||||
stream.close();
|
||||
} catch (e) {
|
||||
console.error(`[ai-filter][ExpressionSearchFilter] Failed to save cache`, e);
|
||||
aiLog(`Failed to save cache`, {level: 'error'}, e);
|
||||
}
|
||||
}
|
||||
|
||||
getEnabled() {
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] getEnabled() called on "${this.id}"`);
|
||||
aiLog(`[ExpressionSearchFilter] getEnabled() called on "${this.id}"`, {debug: true});
|
||||
return true;
|
||||
}
|
||||
|
||||
getAvailable() {
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] getAvailable() called on "${this.id}"`);
|
||||
aiLog(`[ExpressionSearchFilter] getAvailable() called on "${this.id}"`, {debug: true});
|
||||
return true;
|
||||
}
|
||||
|
||||
getAvailableOperators() {
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] getAvailableOperators() called on "${this.id}"`);
|
||||
aiLog(`[ExpressionSearchFilter] getAvailableOperators() called on "${this.id}"`, {debug: true});
|
||||
return this.operators;
|
||||
}
|
||||
|
||||
getAvailableValues() {
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] getAvailableValues() called on "${this.id}"`);
|
||||
aiLog(`[ExpressionSearchFilter] getAvailableValues() called on "${this.id}"`, {debug: true});
|
||||
return null;
|
||||
}
|
||||
|
||||
get attrib() {
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] attrib getter called for "${this.id}"`);
|
||||
aiLog(`[ExpressionSearchFilter] attrib getter called for "${this.id}"`, {debug: true});
|
||||
|
||||
//return Ci.nsMsgSearchAttrib.Custom;
|
||||
}
|
||||
}
|
||||
|
||||
function getPlainText(msgHdr) {
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] Extracting plain text for message ID ${msgHdr.messageId}`);
|
||||
aiLog(`[ExpressionSearchFilter] Extracting plain text for message ID ${msgHdr.messageId}`, {debug: true});
|
||||
let folder = msgHdr.folder;
|
||||
if (!folder.getMsgInputStream) return "";
|
||||
let reusable = {};
|
||||
|
@ -161,7 +162,7 @@ function loadTemplate(name) {
|
|||
return xhr.responseText;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`[ai-filter][ExpressionSearchFilter] Failed to load template '${name}':`, e);
|
||||
aiLog(`Failed to load template '${name}':`, {level: 'error'}, e);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
@ -186,9 +187,12 @@ function setConfig(config = {}) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (typeof config.debugLogging === "boolean") {
|
||||
setDebug(config.debugLogging);
|
||||
}
|
||||
gTemplateText = gTemplateName === "custom" ? gCustomTemplate : loadTemplate(gTemplateName);
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] Endpoint set to ${gEndpoint}`);
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] Template set to ${gTemplateName}`);
|
||||
aiLog(`[ExpressionSearchFilter] Endpoint set to ${gEndpoint}`, {debug: true});
|
||||
aiLog(`[ExpressionSearchFilter] Template set to ${gTemplateName}`, {debug: true});
|
||||
}
|
||||
|
||||
function buildSystemPrompt() {
|
||||
|
@ -196,7 +200,7 @@ function buildSystemPrompt() {
|
|||
}
|
||||
|
||||
function buildPrompt(body, criterion) {
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] Building prompt with criterion: "${criterion}"`);
|
||||
aiLog(`[ExpressionSearchFilter] Building prompt with criterion: "${criterion}"`, {debug: true});
|
||||
const data = {
|
||||
system: buildSystemPrompt(),
|
||||
email: body,
|
||||
|
@ -209,7 +213,7 @@ function buildPrompt(body, criterion) {
|
|||
class ClassificationTerm extends CustomerTermBase {
|
||||
constructor() {
|
||||
super("classification", [Ci.nsMsgSearchOp.Matches, Ci.nsMsgSearchOp.DoesntMatch]);
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] ClassificationTerm constructed`);
|
||||
aiLog(`[ExpressionSearchFilter] ClassificationTerm constructed`, {debug: true});
|
||||
}
|
||||
|
||||
needsBody() { return true; }
|
||||
|
@ -217,11 +221,11 @@ class ClassificationTerm extends CustomerTermBase {
|
|||
match(msgHdr, value, op) {
|
||||
const opName = op === Ci.nsMsgSearchOp.Matches ? "matches" :
|
||||
op === Ci.nsMsgSearchOp.DoesntMatch ? "doesn't match" : `unknown (${op})`;
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] Matching message ${msgHdr.messageId} using op "${opName}" and value "${value}"`);
|
||||
aiLog(`[ExpressionSearchFilter] Matching message ${msgHdr.messageId} using op "${opName}" and value "${value}"`, {debug: true});
|
||||
|
||||
let key = [msgHdr.messageId, op, value].map(sha256Hex).join("|");
|
||||
if (this.cache.has(key)) {
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] Cache hit for key: ${key}`);
|
||||
aiLog(`[ExpressionSearchFilter] Cache hit for key: ${key}`, {debug: true});
|
||||
return this.cache.get(key);
|
||||
}
|
||||
|
||||
|
@ -232,7 +236,7 @@ class ClassificationTerm extends CustomerTermBase {
|
|||
let payload = JSON.stringify(payloadObj);
|
||||
|
||||
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] Sending classification request to ${gEndpoint}`);
|
||||
aiLog(`[ExpressionSearchFilter] Sending classification request to ${gEndpoint}`, {debug: true});
|
||||
|
||||
let matched = false;
|
||||
try {
|
||||
|
@ -242,44 +246,44 @@ class ClassificationTerm extends CustomerTermBase {
|
|||
xhr.send(payload);
|
||||
|
||||
if (xhr.status < 200 || xhr.status >= 300) {
|
||||
console.warn(`[ai-filter][ExpressionSearchFilter] HTTP status ${xhr.status}`);
|
||||
aiLog(`HTTP status ${xhr.status}`, {level: 'warn'});
|
||||
} else {
|
||||
const result = JSON.parse(xhr.responseText);
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] Received response:`, result);
|
||||
aiLog(`[ExpressionSearchFilter] Received response:`, {debug: true}, result);
|
||||
const rawText = result.choices?.[0]?.text || "";
|
||||
const thinkText = rawText.match(/<think>[\s\S]*?<\/think>/gi)?.join('') || '';
|
||||
console.log('[ai-filter][ExpressionSearchFilter] ⮡ Reasoning: ', thinkText);
|
||||
aiLog('[ExpressionSearchFilter] ⮡ Reasoning:', {debug: true}, thinkText);
|
||||
const cleanedText = rawText.replace(/<think>[\s\S]*?<\/think>/gi, "").trim();
|
||||
console.log('[ai-filter][ExpressionSearchFilter] ⮡ Cleaned Response Text: ', cleanedText);
|
||||
aiLog('[ExpressionSearchFilter] ⮡ Cleaned Response Text:', {debug: true}, cleanedText);
|
||||
const obj = JSON.parse(cleanedText);
|
||||
matched = obj.matched === true || obj.match === true;
|
||||
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] Caching entry '${key}' → ${matched}`);
|
||||
aiLog(`[ExpressionSearchFilter] Caching entry '${key}' → ${matched}`, {debug: true});
|
||||
this.cache.set(key, matched);
|
||||
this._saveCache(key, matched);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`[ai-filter][ExpressionSearchFilter] HTTP request failed:`, e);
|
||||
aiLog(`HTTP request failed`, {level: 'error'}, e);
|
||||
}
|
||||
|
||||
if (op === Ci.nsMsgSearchOp.DoesntMatch) {
|
||||
matched = !matched;
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] Operator is "doesn't match" → inverting to ${matched}`);
|
||||
aiLog(`[ExpressionSearchFilter] Operator is "doesn't match" → inverting to ${matched}`, {debug: true});
|
||||
}
|
||||
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] Final match result: ${matched}`);
|
||||
aiLog(`[ExpressionSearchFilter] Final match result: ${matched}`, {debug: true});
|
||||
return matched;
|
||||
}
|
||||
}
|
||||
|
||||
(function register() {
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] Registering custom filter term...`);
|
||||
aiLog(`[ExpressionSearchFilter] Registering custom filter term...`, {debug: true});
|
||||
let term = new ClassificationTerm();
|
||||
if (!MailServices.filters.getCustomTerm(term.id)) {
|
||||
MailServices.filters.addCustomTerm(term);
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] Registered term: ${term.id}`);
|
||||
aiLog(`[ExpressionSearchFilter] Registered term: ${term.id}`, {debug: true});
|
||||
} else {
|
||||
console.log(`[ai-filter][ExpressionSearchFilter] Term already registered: ${term.id}`);
|
||||
aiLog(`[ExpressionSearchFilter] Term already registered: ${term.id}`, {debug: true});
|
||||
}
|
||||
})();
|
||||
|
||||
|
|
26
modules/logger.jsm
Normal file
26
modules/logger.jsm
Normal file
|
@ -0,0 +1,26 @@
|
|||
var EXPORTED_SYMBOLS = ['aiLog', 'setDebug'];
|
||||
let debugEnabled = false;
|
||||
|
||||
function setDebug(value) {
|
||||
debugEnabled = !!value;
|
||||
}
|
||||
|
||||
function getCaller() {
|
||||
try {
|
||||
let stack = new Error().stack.split('\n');
|
||||
if (stack.length >= 3) {
|
||||
return stack[2].trim().replace(/^@?\s*\(?/,'').replace(/^at\s+/, '');
|
||||
}
|
||||
} catch (e) {}
|
||||
return '';
|
||||
}
|
||||
|
||||
function aiLog(message, opts = {}, ...args) {
|
||||
const { level = 'log', debug = false } = opts;
|
||||
if (debug && !debugEnabled) {
|
||||
return;
|
||||
}
|
||||
const caller = getCaller();
|
||||
const prefix = caller ? `[ai-filter][${caller}]` : '[ai-filter]';
|
||||
console[level](`%c${prefix}`, 'color:#1c92d2;font-weight:bold', message, ...args);
|
||||
}
|
|
@ -147,6 +147,11 @@
|
|||
</div>
|
||||
|
||||
<div id="advanced-options" style="display:none">
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" id="debug-logging"> Enable debug logging
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="max_tokens">Max tokens:</label>
|
||||
<input type="number" id="max_tokens">
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
const logger = await import(browser.runtime.getURL('logger.js'));
|
||||
const defaults = await browser.storage.local.get([
|
||||
'endpoint',
|
||||
'templateName',
|
||||
'customTemplate',
|
||||
'customSystemPrompt',
|
||||
'aiParams'
|
||||
'aiParams',
|
||||
'debugLogging'
|
||||
]);
|
||||
logger.setDebug(defaults.debugLogging === true);
|
||||
const DEFAULT_AI_PARAMS = {
|
||||
max_tokens: 4096,
|
||||
temperature: 0.6,
|
||||
|
@ -52,6 +55,9 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
advancedBox.style.display = advancedBox.style.display === 'none' ? 'block' : 'none';
|
||||
});
|
||||
|
||||
const debugToggle = document.getElementById('debug-logging');
|
||||
debugToggle.checked = defaults.debugLogging === true;
|
||||
|
||||
const aiParams = Object.assign({}, DEFAULT_AI_PARAMS, defaults.aiParams || {});
|
||||
for (const [key, val] of Object.entries(aiParams)) {
|
||||
const el = document.getElementById(key);
|
||||
|
@ -78,11 +84,13 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
aiParamsSave[key] = isNaN(num) ? DEFAULT_AI_PARAMS[key] : num;
|
||||
}
|
||||
}
|
||||
await browser.storage.local.set({ endpoint, templateName, customTemplate: customTemplateText, customSystemPrompt, aiParams: aiParamsSave });
|
||||
const debugLogging = debugToggle.checked;
|
||||
await browser.storage.local.set({ endpoint, templateName, customTemplate: customTemplateText, customSystemPrompt, aiParams: aiParamsSave, debugLogging });
|
||||
try {
|
||||
await browser.aiFilter.initConfig({ endpoint, templateName, customTemplate: customTemplateText, customSystemPrompt, aiParams: aiParamsSave });
|
||||
await browser.aiFilter.initConfig({ endpoint, templateName, customTemplate: customTemplateText, customSystemPrompt, aiParams: aiParamsSave, debugLogging });
|
||||
logger.setDebug(debugLogging);
|
||||
} catch (e) {
|
||||
console.error('[ai-filter][options] failed to apply config', e);
|
||||
logger.aiLog('[options] failed to apply config', {level: 'error'}, e);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue