feat: add SSE parser and in-flight request tracker

This commit is contained in:
2026-08-14 10:42:17 -06:00
parent c6d50f6daf
commit b54eeeadc4
4 changed files with 161 additions and 0 deletions
+58
View File
@@ -0,0 +1,58 @@
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" | "remove"; requests: InflightRequest[] }
| { type: "modelStatus"; models: ModelState[] };
export type ModelRuntimeState = "stopped" | "loading" | "ready";
export class InflightTracker {
private counts = new Map<string, number>();
private states = new Map<string, ModelRuntimeState>();
apply(event: FeedEvent): void {
if (event.type === "inflight") {
if (event.operation === "snapshot") {
const counts = new Map<string, number>();
for (const r of event.requests) counts.set(r.model, (counts.get(r.model) ?? 0) + 1);
this.counts = counts;
} else if (event.operation === "add") {
for (const r of event.requests) this.counts.set(r.model, (this.counts.get(r.model) ?? 0) + 1);
} else if (event.operation === "remove") {
for (const r of event.requests) {
const c = this.counts.get(r.model) ?? 0;
if (c <= 1) this.counts.delete(r.model);
else this.counts.set(r.model, c - 1);
}
}
} else if (event.type === "modelStatus") {
for (const m of event.models) this.states.set(m.id, normalizeState(m.state));
}
}
count(modelId: string): number {
return this.counts.get(modelId) ?? 0;
}
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";
}
+29
View File
@@ -0,0 +1,29 @@
export interface SseMessage {
event?: string;
data: string;
}
export function parseSse(chunk: string): { messages: SseMessage[]; rest: string } {
const messages: SseMessage[] = [];
let rest = chunk;
while (true) {
const idx = rest.indexOf("\n\n");
if (idx === -1) break;
const block = rest.slice(0, idx);
rest = rest.slice(idx + 2);
const message = toMessage(block);
if (message && message.data.length > 0) messages.push(message);
}
return { messages, rest };
}
function toMessage(block: string): SseMessage | null {
let event: string | undefined;
const dataLines: string[] = [];
for (const line of block.split("\n")) {
if (line.startsWith("event:")) event = line.slice(6).trim();
else if (line.startsWith("data:")) dataLines.push(line.slice(5).trimStart());
}
if (dataLines.length === 0) return null;
return { event, data: dataLines.join("\n") };
}