test_MemoriesDriftDetector.js (6766B)
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 http://mozilla.org/MPL/2.0/. 4 */ 5 6 do_get_profile(); 7 ("use strict"); 8 9 const { MemoriesDriftDetector } = ChromeUtils.importESModule( 10 "moz-src:///browser/components/aiwindow/models/memories/MemoriesDriftDetector.sys.mjs" 11 ); 12 const { MemoriesManager } = ChromeUtils.importESModule( 13 "moz-src:///browser/components/aiwindow/models/memories/MemoriesManager.sys.mjs" 14 ); 15 16 add_task(function test_computeDriftTriggerFromBaseline_no_data() { 17 const result = MemoriesDriftDetector.computeDriftTriggerFromBaseline( 18 [], 19 [], 20 {} 21 ); 22 23 Assert.ok(!result.triggered, "No data should not trigger"); 24 Assert.equal(result.jsThreshold, 0, "JS threshold should be 0 with no data"); 25 Assert.equal( 26 result.surpriseThreshold, 27 0, 28 "Surprise threshold should be 0 with no data" 29 ); 30 Assert.deepEqual( 31 result.triggeredSessionIds, 32 [], 33 "No triggered sessions without data" 34 ); 35 }); 36 37 add_task(function test_computeDriftTriggerFromBaseline_triggers_on_delta() { 38 /** @type {SessionMetric[]} */ 39 const baselineMetrics = [ 40 { 41 sessionId: "b1", 42 jsScore: 0.05, 43 avgSurprisal: 2, 44 timestampMs: 1, 45 }, 46 { 47 sessionId: "b2", 48 jsScore: 0.08, 49 avgSurprisal: 2.5, 50 timestampMs: 2, 51 }, 52 { 53 sessionId: "b3", 54 jsScore: 0.1, 55 avgSurprisal: 3, 56 timestampMs: 3, 57 }, 58 ]; 59 60 // Make delta fairly "spiky" so it's above baseline thresholds. 61 const deltaMetrics = [ 62 { 63 sessionId: "d1", 64 jsScore: 0.5, 65 avgSurprisal: 6, 66 timestampMs: 4, 67 }, 68 { 69 sessionId: "d2", 70 jsScore: 0.6, 71 avgSurprisal: 7, 72 timestampMs: 5, 73 }, 74 ]; 75 76 const result = MemoriesDriftDetector.computeDriftTriggerFromBaseline( 77 baselineMetrics, 78 deltaMetrics, 79 { 80 triggerQuantile: 0.8, 81 evalDeltaCount: 2, 82 } 83 ); 84 85 Assert.greater(result.jsThreshold, 0, "JS baseline threshold should be > 0"); 86 Assert.greater( 87 result.surpriseThreshold, 88 0, 89 "Surprise baseline threshold should be > 0" 90 ); 91 Assert.ok(result.triggered, "High delta metrics should trigger drift"); 92 Assert.deepEqual( 93 result.triggeredSessionIds.sort(), 94 ["d1", "d2"], 95 "Both delta sessions should be flagged as triggered" 96 ); 97 }); 98 99 add_task(function test_computeDriftTriggerFromBaseline_no_delta() { 100 const baselineMetrics = [ 101 { 102 sessionId: "b1", 103 jsScore: 0.1, 104 avgSurprisal: 3, 105 timestampMs: 1, 106 }, 107 ]; 108 109 const deltaMetrics = []; 110 111 const result = MemoriesDriftDetector.computeDriftTriggerFromBaseline( 112 baselineMetrics, 113 deltaMetrics, 114 {} 115 ); 116 117 Assert.ok(!result.triggered, "No delta metrics should not trigger"); 118 Assert.equal(result.jsThreshold, 0, "JS threshold should be 0 with no data"); 119 Assert.equal( 120 result.surpriseThreshold, 121 0, 122 "Surprise threshold should be 0 with no data" 123 ); 124 Assert.deepEqual( 125 result.triggeredSessionIds, 126 [], 127 "No triggered sessions without delta metrics" 128 ); 129 }); 130 131 add_task( 132 function test_computeDriftTriggerFromBaseline_respects_evalDeltaCount() { 133 const baselineMetrics = [ 134 { 135 sessionId: "b1", 136 jsScore: 0.05, 137 avgSurprisal: 2, 138 timestampMs: 1, 139 }, 140 { 141 sessionId: "b2", 142 jsScore: 0.08, 143 avgSurprisal: 2.5, 144 timestampMs: 2, 145 }, 146 ]; 147 148 // First delta is "spiky", later ones are normal-ish. 149 const deltaMetrics = [ 150 { 151 sessionId: "d1", 152 jsScore: 0.7, 153 avgSurprisal: 8, 154 timestampMs: 3, 155 }, 156 { 157 sessionId: "d2", 158 jsScore: 0.06, 159 avgSurprisal: 2.1, 160 timestampMs: 4, 161 }, 162 { 163 sessionId: "d3", 164 jsScore: 0.07, 165 avgSurprisal: 2.2, 166 timestampMs: 5, 167 }, 168 ]; 169 170 const result = MemoriesDriftDetector.computeDriftTriggerFromBaseline( 171 baselineMetrics, 172 // only d2 and d3 should be evaluated (setting evalDeltaCount = 2) 173 deltaMetrics, 174 { 175 triggerQuantile: 0.8, 176 evalDeltaCount: 2, 177 } 178 ); 179 180 Assert.ok( 181 !result.triggered, 182 "When only the last 2 non-spiky deltas are evaluated, drift should not trigger" 183 ); 184 Assert.deepEqual( 185 result.triggeredSessionIds, 186 [], 187 "No delta sessions should be flagged when only low scores are considered" 188 ); 189 } 190 ); 191 192 add_task(function test_computeDriftTriggerFromBaseline_non_spiky_no_trigger() { 193 const baselineMetrics = [ 194 { 195 sessionId: "b1", 196 jsScore: 0.1, 197 avgSurprisal: 3, 198 timestampMs: 1, 199 }, 200 { 201 sessionId: "b2", 202 jsScore: 0.12, 203 avgSurprisal: 3.2, 204 timestampMs: 2, 205 }, 206 { 207 sessionId: "b3", 208 jsScore: 0.11, 209 avgSurprisal: 3.1, 210 timestampMs: 3, 211 }, 212 ]; 213 214 const deltaMetrics = [ 215 { 216 sessionId: "d1", 217 jsScore: 0.105, 218 avgSurprisal: 3.05, 219 timestampMs: 4, 220 }, 221 { 222 sessionId: "d2", 223 jsScore: 0.115, 224 avgSurprisal: 3.1, 225 timestampMs: 5, 226 }, 227 ]; 228 229 const result = MemoriesDriftDetector.computeDriftTriggerFromBaseline( 230 baselineMetrics, 231 deltaMetrics, 232 { 233 triggerQuantile: 0.9, 234 evalDeltaCount: 2, 235 } 236 ); 237 238 Assert.ok( 239 !result.triggered, 240 "Delta sessions similar to baseline should not trigger drift" 241 ); 242 Assert.deepEqual( 243 result.triggeredSessionIds, 244 [], 245 "No sessions should be flagged when deltas match baseline distribution" 246 ); 247 }); 248 249 add_task(async function test_computeHistoryDriftAndTrigger_no_prior_memory() { 250 const originalGetLastHistoryMemoryTimestamp = 251 MemoriesManager.getLastHistoryMemoryTimestamp; 252 253 // Force "no previous memory" so computeHistoryDriftSessionMetrics bails out. 254 MemoriesManager.getLastHistoryMemoryTimestamp = async () => null; 255 256 const result = await MemoriesDriftDetector.computeHistoryDriftAndTrigger({}); 257 258 dump(`no_prior_memory result = ${JSON.stringify(result)}\n`); 259 260 Assert.ok( 261 Array.isArray(result.baselineMetrics), 262 "baselineMetrics should be an array" 263 ); 264 Assert.ok( 265 Array.isArray(result.deltaMetrics), 266 "deltaMetrics should be an array" 267 ); 268 Assert.equal( 269 result.baselineMetrics.length, 270 0, 271 "No baseline metrics when there is no prior memory timestamp" 272 ); 273 Assert.equal( 274 result.deltaMetrics.length, 275 0, 276 "No delta metrics when there is no prior memory timestamp" 277 ); 278 Assert.ok( 279 !result.trigger.triggered, 280 "Trigger should be false when there is no prior memory" 281 ); 282 283 // Restore original implementation. 284 MemoriesManager.getLastHistoryMemoryTimestamp = 285 originalGetLastHistoryMemoryTimestamp; 286 });