MemoriesConversationScheduler.sys.mjs (3968B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 5 const lazy = {}; 6 ChromeUtils.defineESModuleGetters(lazy, { 7 setInterval: "resource://gre/modules/Timer.sys.mjs", 8 clearInterval: "resource://gre/modules/Timer.sys.mjs", 9 MemoriesManager: 10 "moz-src:///browser/components/aiwindow/models/memories/MemoriesManager.sys.mjs", 11 getRecentChats: 12 "moz-src:///browser/components/aiwindow/models/memories/MemoriesChatSource.sys.mjs", 13 PREF_GENERATE_MEMORIES: 14 "moz-src:///browser/components/aiwindow/models/memories/MemoriesConstants.sys.mjs", 15 }); 16 ChromeUtils.defineLazyGetter(lazy, "console", function () { 17 return console.createInstance({ 18 prefix: "MemoriesConversationScheduler", 19 maxLogLevelPref: "browser.aiwindow.memoriesLogLevel", 20 }); 21 }); 22 23 // Generate memories if there have been at least 10 user messages since the last run 24 const MEMORIES_SCHEDULER_MESSAGES_THRESHOLD = 10; 25 26 // Memories conversation schedule every 4 hours 27 const MEMORIES_SCHEDULER_INTERVAL_MS = 4 * 60 * 60 * 1000; 28 29 /** 30 * Schedules periodic generation of conversation-based memories. 31 * Triggers memories generation when number of user messages exceeds the configured threshold ({@link MEMORIES_SCHEDULER_MESSAGES_THRESHOLD}) 32 * 33 * E.g. Usage: MemoriesConversationScheduler.maybeInit() 34 */ 35 export class MemoriesConversationScheduler { 36 #intervalHandle = 0; 37 #destroyed = false; 38 #running = false; 39 40 /** @type {MemoriesConversationScheduler | null} */ 41 static #instance = null; 42 43 static maybeInit() { 44 if (!Services.prefs.getBoolPref(lazy.PREF_GENERATE_MEMORIES, false)) { 45 return null; 46 } 47 if (!this.#instance) { 48 this.#instance = new MemoriesConversationScheduler(); 49 } 50 return this.#instance; 51 } 52 53 constructor() { 54 this.#startInterval(); 55 lazy.console.debug("Initialized"); 56 } 57 58 /** 59 * Starts the interval that periodically evaluates history drift and 60 * potentially triggers memory generation. 61 * 62 * @throws {Error} If an interval is already running. 63 */ 64 #startInterval() { 65 if (this.#intervalHandle) { 66 throw new Error( 67 "Attempting to start an interval when one already existed" 68 ); 69 } 70 this.#intervalHandle = lazy.setInterval( 71 this.#onInterval, 72 MEMORIES_SCHEDULER_INTERVAL_MS 73 ); 74 } 75 76 /** 77 * Stops the currently running interval, if any. 78 */ 79 #stopInterval() { 80 if (this.#intervalHandle) { 81 lazy.clearInterval(this.#intervalHandle); 82 this.#intervalHandle = 0; 83 } 84 } 85 86 #onInterval = async () => { 87 if (this.#destroyed) { 88 lazy.console.warn("Interval fired after destroy; ignoring."); 89 return; 90 } 91 92 if (this.#running) { 93 lazy.console.debug( 94 "Skipping run because a previous run is still in progress." 95 ); 96 return; 97 } 98 99 this.#running = true; 100 this.#stopInterval(); 101 102 try { 103 // Detect whether conversation memories were generated before. 104 const lastMemoryTs = 105 (await lazy.MemoriesManager.getLastConversationMemoryTimestamp()) ?? 0; 106 107 // Get user chat messages 108 const chatMessagesSinceLastMemory = 109 await lazy.getRecentChats(lastMemoryTs); 110 111 // Not enough new messages 112 if ( 113 chatMessagesSinceLastMemory.length < 114 MEMORIES_SCHEDULER_MESSAGES_THRESHOLD 115 ) { 116 return; 117 } 118 119 // Generate memories 120 await lazy.MemoriesManager.generateMemoriesFromConversationHistory(); 121 } catch (error) { 122 lazy.console.error("Failed to generate conversation memories", error); 123 } finally { 124 if (!this.#destroyed) { 125 this.#startInterval(); 126 } 127 this.#running = false; 128 } 129 }; 130 131 destroy() { 132 this.#stopInterval(); 133 this.#destroyed = true; 134 lazy.console.debug("Destroyed"); 135 } 136 137 async runNowForTesting() { 138 await this.#onInterval(); 139 } 140 }