From e585992087bc77f975deaa51097c47d1a65952be Mon Sep 17 00:00:00 2001 From: c4ch3c4d3 <23181631+c4ch3c4d3@users.noreply.github.com> Date: Fri, 14 Aug 2026 12:56:45 -0600 Subject: [PATCH] feat: add usage-stats display mode to In-Flight Monitor action --- .../ui/inflight.html | 14 +++++ src/actions/inflight-monitor.ts | 58 +++++++++++++++++-- src/lib/datasources.ts | 2 +- src/lib/render.ts | 2 +- 4 files changed, 68 insertions(+), 8 deletions(-) diff --git a/com.bryce.llamawatch.sdPlugin/ui/inflight.html b/com.bryce.llamawatch.sdPlugin/ui/inflight.html index 21f7b7a..550f6cf 100644 --- a/com.bryce.llamawatch.sdPlugin/ui/inflight.html +++ b/com.bryce.llamawatch.sdPlugin/ui/inflight.html @@ -11,6 +11,20 @@ + + + + + + + + + + + + + + diff --git a/src/actions/inflight-monitor.ts b/src/actions/inflight-monitor.ts index 0e6628f..61fd1b0 100644 --- a/src/actions/inflight-monitor.ts +++ b/src/actions/inflight-monitor.ts @@ -7,18 +7,21 @@ import streamDeck, { type WillAppearEvent, type WillDisappearEvent, } from "@elgato/streamdeck"; -import { renderInflight, svgDataUrl } from "../lib/render"; +import { renderInflight, renderUsage, svgDataUrl } from "../lib/render"; import { runtime } from "../lib/runtime"; import { cfgFromSettings, type CfgSettings } from "../lib/util"; type InflightSettings = CfgSettings & { modelId?: string; + display?: "count" | "usage"; + primaryStat?: "requests" | "input_tokens" | "output_tokens" | "gen_p95"; }; type InflightState = { settings: InflightSettings; action?: KeyAction; unsubscribe?: () => void; + unwatchStats?: () => void; history: number[]; sampler?: ReturnType; }; @@ -45,7 +48,11 @@ export class InflightMonitor extends SingletonAction { state.action = ev.action; runtime.ensureConnections(cfgFromSettings(state.settings)); state.unsubscribe = runtime.subscribe(() => this.render(state)); - state.sampler = setInterval(() => this.sample(state), 1000); + if (this.displayOf(state) === "usage") { + state.unwatchStats = runtime.watchStats(this.modelKeyOf(state)); + } else { + state.sampler = setInterval(() => this.sample(state), 1000); + } this.render(state); } @@ -53,6 +60,8 @@ export class InflightMonitor extends SingletonAction { const state = this.states.get(ev.action.id); if (!state) return; state.unsubscribe?.(); + state.unwatchStats?.(); + state.unwatchStats = undefined; if (state.sampler) clearInterval(state.sampler); state.sampler = undefined; this.states.delete(ev.action.id); @@ -65,6 +74,13 @@ export class InflightMonitor extends SingletonAction { state.action = ev.action; state.history = []; runtime.ensureConnections(cfgFromSettings(state.settings)); + state.unwatchStats?.(); + state.unwatchStats = undefined; + if (this.displayOf(state) === "usage") { + state.unwatchStats = runtime.watchStats(this.modelKeyOf(state)); + } else if (!state.sampler) { + state.sampler = setInterval(() => this.sample(state), 1000); + } this.render(state); } @@ -72,17 +88,47 @@ export class InflightMonitor extends SingletonAction { void streamDeck.system.openUrl(`${cfgFromSettings(ev.payload.settings).baseUrl}/ui`); } + private displayOf(state: InflightState): "count" | "usage" { + return state.settings.display ?? "count"; + } + + private modelKeyOf(state: InflightState): string { + const modelId = state.settings.modelId ?? ""; + return modelId === "" ? "all" : modelId; + } + + private primaryStatOf(state: InflightState): "requests" | "input_tokens" | "output_tokens" | "gen_p95" { + return state.settings.primaryStat ?? "requests"; + } + private render(state: InflightState): void { const action = state.action; if (!action) return; + if (this.displayOf(state) === "usage") { + const modelKey = this.modelKeyOf(state); + const offline = runtime.offline; + void action.setImage( + svgDataUrl( + renderUsage({ + modelName: modelKey, + stats: runtime.getStats(modelKey), + primaryStat: this.primaryStatOf(state), + offline, + }), + ), + ); + return; + } + const modelId = state.settings.modelId ?? ""; - const trackerState = runtime.tracker.state(modelId); - const count = runtime.tracker.count(modelId); + const trackerState = modelId === "all" ? "ready" : runtime.tracker.state(modelId); + const count = modelId === "all" ? runtime.tracker.total() : runtime.tracker.count(modelId); const offline = runtime.offline && modelId.length > 0; + const modelName = modelId === "" ? "unset" : modelId; void action.setImage( svgDataUrl( renderInflight({ - modelName: modelId.length > 0 ? modelId : "unset", + modelName, state: trackerState, count, offline, @@ -94,7 +140,7 @@ export class InflightMonitor extends SingletonAction { private sample(state: InflightState): void { const modelId = state.settings.modelId ?? ""; - state.history.push(runtime.tracker.count(modelId)); + state.history.push(modelId === "all" ? runtime.tracker.total() : runtime.tracker.count(modelId)); if (state.history.length > HISTORY_LIMIT) state.history.shift(); this.render(state); } diff --git a/src/lib/datasources.ts b/src/lib/datasources.ts index 11dc102..8d89e5a 100644 --- a/src/lib/datasources.ts +++ b/src/lib/datasources.ts @@ -17,7 +17,7 @@ export function registerDataSources(): void { let items: DataSourceItem[] = []; try { const models = await fetchModels(cfgFromSettings(settings)); - items = models.map((m) => ({ label: `${m.id} (${m.status})`, value: m.id })); + items = [{ label: "All models", value: "all" }, ...models.map((m) => ({ label: `${m.id} (${m.status})`, value: m.id }))]; } catch { items = []; } diff --git a/src/lib/render.ts b/src/lib/render.ts index bf3d30b..51f9dce 100644 --- a/src/lib/render.ts +++ b/src/lib/render.ts @@ -24,7 +24,7 @@ export interface InflightRenderOptions { } export function renderInflight(opts: InflightRenderOptions): string { - const name = escapeXml(shorten(opts.modelName)); + const name = escapeXml(shorten(opts.modelName === "all" ? "ALL MODELS" : opts.modelName)); if (opts.offline) { return frame("#10131a", [