Files
llama-watch/src/lib/inflight-tracker.ts
T
bzuccaro c6e8a9ad11 fix: track in-flight requests by id; handle llama-swap upsert/remove events
llama-swap emits inflight events with operation 'upsert' (a single request,
re-emitted as elapsed_ms updates) and id-only 'remove' events. The decoder
ignored 'upsert' and dropped every id-only remove, so the tracker only ever
reflected the connect-time snapshot and the count got stuck (e.g. at '1').

The tracker now keys in-flight state by request id (snapshot/add replace by
id; remove deletes by id), and the decoder normalizes 'upsert' to 'add' and
extracts the id from bare removes.
2026-08-14 12:08:02 -06:00

57 lines
1.6 KiB
TypeScript

export interface InflightRequest {
id?: string;
model: string;
req_path?: string;
method?: string;
timestamp?: string;
elapsed_ms?: number;
}
export interface ModelState {
id: string;
state: string;
}
export type FeedEvent =
| { type: "inflight"; operation: "snapshot" | "add"; requests: InflightRequest[] }
| { type: "inflight"; operation: "remove"; id: string }
| { type: "modelStatus"; models: ModelState[] };
export type ModelRuntimeState = "stopped" | "loading" | "ready";
export class InflightTracker {
private requests = new Map<string, string>();
private states = new Map<string, ModelRuntimeState>();
apply(event: FeedEvent): void {
if (event.type === "inflight") {
if (event.operation === "snapshot") {
this.requests = new Map<string, string>();
for (const r of event.requests) if (r.id) this.requests.set(r.id, r.model);
} else if (event.operation === "add") {
for (const r of event.requests) if (r.id) this.requests.set(r.id, r.model);
} else if (event.operation === "remove") {
this.requests.delete(event.id);
}
} else if (event.type === "modelStatus") {
for (const m of event.models) this.states.set(m.id, normalizeState(m.state));
}
}
count(modelId: string): number {
let n = 0;
for (const model of this.requests.values()) if (model === modelId) n++;
return n;
}
state(modelId: string): ModelRuntimeState | undefined {
return this.states.get(modelId);
}
}
function normalizeState(state: string): ModelRuntimeState {
if (state === "ready") return "ready";
if (state === "loading") return "loading";
return "stopped";
}