Measure semantic activation refresh independently from download and idle time

This commit is contained in:
karl committed 2026-10-13 09:56:34 +02:00
1 parent 9aae3f8216
commit 19d949fd13
6 files changed
+140 -22

No files matched your search

+28 -13
View File
@@ -30,7 +30,11 @@ import {
spanDepth,
briefMs,
} from "./search-performance";
import type { MeasuredResponse, SearchLane } from "./search-performance";
import type {
MeasuredResponse,
SearchLane,
SearchRefreshTrigger,
} from "./search-performance";
import { displayPath } from "./path-display";
import { ResultSelection } from "./selection-state";
import {
@@ -449,7 +453,7 @@ async function refreshSemanticStatus() {
canSearchSemantically() &&
latestSearch?.id === requestId
)
submitSearch(latestSearch, "hybrid");
refreshIndexedSearch("semantic_activation");
if (working || (!status.ready && !error)) queueSemanticRefresh();
} catch (error) {
element("semantic-status").textContent =
@@ -1088,7 +1092,22 @@ function submitSearch(request: SearchRequest, lane: SearchLane) {
(lane === "lexical" ? lexicalLane : hybridLane).submit(request);
}
function refreshIndexedSearch() {
function searchMeasurementContext() {
return {
model: semanticStatus?.worker_info?.model ?? null,
device: semanticStatus?.worker_info?.device ?? null,
runtime: semanticStatus?.worker_info?.runtime ?? null,
threads: semanticStatus?.worker_info?.threads ?? null,
dimensions: semanticStatus?.worker_info?.dimensions ?? null,
semantic_ready: canSearchSemantically(),
semantic_generation: semanticStatus?.generation ?? null,
indexed_chunks: semanticStatus?.chunks ?? null,
};
}
function refreshIndexedSearch(
trigger: SearchRefreshTrigger = "filesystem_refresh",
) {
const previous = latestSearch;
if (
!previous ||
@@ -1106,7 +1125,11 @@ function refreshIndexedSearch() {
...previous,
id: ++requestId,
started: refreshStarted,
measurement: previous.measurement.freshForRefresh(refreshStarted),
measurement: previous.measurement.freshForRefresh(
refreshStarted,
trigger,
searchMeasurementContext(),
),
};
latestSearch = refreshed;
submitSearch(refreshed, "lexical");
@@ -1188,15 +1211,7 @@ async function search(inputAt = performance.now()) {
inputAt,
performance.now(),
element<HTMLInputElement>("performance-toggle").checked,
{
model: semanticStatus?.worker_info?.model ?? null,
device: semanticStatus?.worker_info?.device ?? null,
runtime: semanticStatus?.worker_info?.runtime ?? null,
threads: semanticStatus?.worker_info?.threads ?? null,
dimensions: semanticStatus?.worker_info?.dimensions ?? null,
semantic_ready: canSearchSemantically(),
indexed_chunks: semanticStatus?.chunks ?? null,
},
searchMeasurementContext(),
),
};
latestSearch = request;
+11 -4
View File
@@ -17,6 +17,7 @@ export interface MeasuredResponse<T> {
metrics: NativeMetrics;
}
export type SearchLane = "lexical" | "hybrid";
export type SearchRefreshTrigger = "filesystem_refresh" | "semantic_activation";
export interface AttemptStart {
lane: SearchLane;
startedAt: number;
@@ -44,6 +45,7 @@ interface InputTimingSummary {
export class SearchMeasurement {
private originalInput: Readonly<InputTimingSummary> | null = null;
private trigger: "user_search" | SearchRefreshTrigger = "user_search";
debug: boolean;
inputAt: number;
scheduledAt: number;
@@ -74,9 +76,14 @@ export class SearchMeasurement {
this.debug = debug;
this.context = { ...context };
}
/** Filesystem updates are independent requests, not elapsed user idle time. */
freshForRefresh(now: number): SearchMeasurement {
const refreshed = new SearchMeasurement(now, now, this.debug, this.context);
/** Background updates are independent requests, not elapsed user idle time. */
freshForRefresh(
now: number,
trigger: SearchRefreshTrigger = "filesystem_refresh",
context = this.context,
): SearchMeasurement {
const refreshed = new SearchMeasurement(now, now, this.debug, context);
refreshed.trigger = trigger;
// Preserve one fixed-size numeric snapshot, never an ancestor trace/history.
// Pending responses on the old request cannot rewrite this snapshot.
refreshed.originalInput = Object.freeze({
@@ -165,7 +172,7 @@ export class SearchMeasurement {
export() {
return {
version: 1,
trigger: this.originalInput ? "filesystem_refresh" : "user_search",
trigger: this.trigger,
original_input: this.originalInput ? { ...this.originalInput } : null,
context: this.context,
clock_note:
@@ -161,3 +161,30 @@ test('repeated refreshes retain a bounded immutable numeric origin without repla
assert.equal(origin.first_results_ms, 72);
assert.equal(origin.hybrid_final_results_ms, null);
});
test('semantic activation excludes download time and records the promoted context separately', () => {
const original = new SearchMeasurement(100, 100, true, {indexed_chunks:3});
original.submitted('hybrid', 220);
finish(original, 'hybrid', 220, 230, native, false);
original.displayed('hybrid', 230, 238, 3);
const context = {indexed_chunks:4, semantic_ready:true};
const promoted = original.freshForRefresh(144000, 'semantic_activation', context);
context.indexed_chunks = 999;
promoted.submitted('lexical', 144000);
finish(promoted, 'lexical', 144001, 144004, native, false);
promoted.displayed('lexical', 144004, 144005, 1);
promoted.submitted('hybrid', 144120);
finish(promoted, 'hybrid', 144120, 144128, native, false);
promoted.displayed('hybrid', 144128, 144138, 4);
const report = promoted.export();
assert.equal(report.trigger, 'semantic_activation');
assert.equal(report.first_results_ms, 5);
assert.equal(report.hybrid_final_results_ms, 138);
assert.equal(report.context.indexed_chunks, 4);
assert.equal(report.original_input.hybrid_final_results_ms, 138);
assert.equal(report.attempts.length, 2);
assert.equal(original.export().context.indexed_chunks, 3);
const next = promoted.freshForRefresh(300000);
assert.equal(next.export().trigger, 'filesystem_refresh');
assert.deepEqual(next.export().original_input, report.original_input);
});