fix: support multiple keys per action and harden runtime/parsing
This commit is contained in:
+29
-26
@@ -7,36 +7,39 @@ type DataSourceItem = { label: string; value: string };
|
||||
|
||||
export function registerDataSources(): void {
|
||||
streamDeck.ui.onSendToPlugin(async (ev) => {
|
||||
const request = ev.payload as { event?: string } | undefined;
|
||||
const event = request?.event;
|
||||
if (!event) return;
|
||||
const settings = await ev.action.getSettings<CfgSettings>();
|
||||
try {
|
||||
const request = ev.payload as { event?: string } | undefined;
|
||||
const event = request?.event;
|
||||
if (!event) return;
|
||||
const settings = await ev.action.getSettings<CfgSettings>();
|
||||
|
||||
if (event === "models") {
|
||||
let items: DataSourceItem[] = [];
|
||||
try {
|
||||
const models = await fetchModels(cfgFromSettings(settings));
|
||||
items = models.map((m) => ({ label: `${m.id} (${m.status})`, value: m.id }));
|
||||
} catch {
|
||||
items = [];
|
||||
if (event === "models") {
|
||||
let items: DataSourceItem[] = [];
|
||||
try {
|
||||
const models = await fetchModels(cfgFromSettings(settings));
|
||||
items = models.map((m) => ({ label: `${m.id} (${m.status})`, value: m.id }));
|
||||
} catch {
|
||||
items = [];
|
||||
}
|
||||
await streamDeck.ui.sendToPropertyInspector({ event, items });
|
||||
return;
|
||||
}
|
||||
await streamDeck.ui.sendToPropertyInspector({ event, items });
|
||||
return;
|
||||
}
|
||||
|
||||
if (event === "gpus") {
|
||||
let items: DataSourceItem[] = [];
|
||||
try {
|
||||
const text = await fetchMetrics(cfgFromSettings(settings));
|
||||
const gpus = gpuInfos(parseGpuMetrics(text));
|
||||
items = [
|
||||
{ label: "All GPUs", value: "all" },
|
||||
...gpus.map((g) => ({ label: `GPU ${g.id} · ${g.name}`, value: g.id })),
|
||||
];
|
||||
} catch {
|
||||
items = [{ label: "All GPUs", value: "all" }];
|
||||
if (event === "gpus") {
|
||||
let items: DataSourceItem[] = [];
|
||||
try {
|
||||
const text = await fetchMetrics(cfgFromSettings(settings));
|
||||
const gpus = gpuInfos(parseGpuMetrics(text));
|
||||
items = [
|
||||
{ label: "All GPUs", value: "all" },
|
||||
...gpus.map((g) => ({ label: `GPU ${g.id} · ${g.name}`, value: g.id })),
|
||||
];
|
||||
} catch {
|
||||
items = [{ label: "All GPUs", value: "all" }];
|
||||
}
|
||||
await streamDeck.ui.sendToPropertyInspector({ event, items });
|
||||
}
|
||||
await streamDeck.ui.sendToPropertyInspector({ event, items });
|
||||
} catch {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -9,8 +9,13 @@ export function decodeEvent(msg: SseMessage): FeedEvent | null {
|
||||
const inner = JSON.parse(outer.data) as Record<string, unknown>;
|
||||
|
||||
if (outer.type === "inflight") {
|
||||
const requests: InflightRequest[] =
|
||||
(inner.requests as InflightRequest[]) ?? (inner.request ? [inner.request as InflightRequest] : []);
|
||||
const requests: InflightRequest[] = Array.isArray(inner.requests)
|
||||
? (inner.requests as InflightRequest[])
|
||||
: Array.isArray(inner.request)
|
||||
? (inner.request as InflightRequest[])
|
||||
: inner.request && typeof inner.request === "object"
|
||||
? [inner.request as InflightRequest]
|
||||
: [];
|
||||
return {
|
||||
type: "inflight",
|
||||
operation: inner.operation as "snapshot" | "add" | "remove",
|
||||
@@ -19,6 +24,7 @@ export function decodeEvent(msg: SseMessage): FeedEvent | null {
|
||||
}
|
||||
|
||||
if (outer.type === "modelStatus") {
|
||||
if (!Array.isArray(inner)) return null;
|
||||
return { type: "modelStatus", models: inner as unknown as ModelState[] };
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { fetchMetrics } from "./llamaswap";
|
||||
import { type LlamaSwapConfig } from "./util";
|
||||
import { gpuInfos, parseGpuMetrics, type GpuMetricKind, type GpuSample } from "./metrics-parser";
|
||||
import { parseGpuMetrics, type GpuMetricKind, type GpuSample } from "./metrics-parser";
|
||||
|
||||
const RING_SIZE = 60;
|
||||
|
||||
@@ -34,6 +34,7 @@ export class MetricsPoller {
|
||||
private gpuIds: string[] = [];
|
||||
private listeners = new Set<() => void>();
|
||||
private timer?: ReturnType<typeof setInterval>;
|
||||
private ticking = false;
|
||||
private lastError?: string;
|
||||
|
||||
constructor(
|
||||
@@ -43,6 +44,7 @@ export class MetricsPoller {
|
||||
) {}
|
||||
|
||||
start(): void {
|
||||
if (this.timer) return;
|
||||
void this.tick();
|
||||
this.timer = setInterval(() => void this.tick(), this.intervalMs);
|
||||
}
|
||||
@@ -60,6 +62,8 @@ export class MetricsPoller {
|
||||
}
|
||||
|
||||
async tick(): Promise<void> {
|
||||
if (this.ticking) return;
|
||||
this.ticking = true;
|
||||
try {
|
||||
const text = await this.fetchFn(this.cfg);
|
||||
this.apply(parseGpuMetrics(text));
|
||||
@@ -67,6 +71,7 @@ export class MetricsPoller {
|
||||
} catch (err) {
|
||||
this.lastError = err instanceof Error ? err.message : String(err);
|
||||
} finally {
|
||||
this.ticking = false;
|
||||
for (const listener of this.listeners) listener();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,20 @@ import { type LlamaSwapConfig } from "./util";
|
||||
class Runtime {
|
||||
readonly tracker = new InflightTracker();
|
||||
offline = true;
|
||||
private cfg?: LlamaSwapConfig;
|
||||
private feed?: EventFeed;
|
||||
private poller?: MetricsPoller;
|
||||
private listeners = new Set<() => void>();
|
||||
|
||||
ensureConnections(cfg: LlamaSwapConfig): void {
|
||||
const changed = !this.cfg || this.cfg.baseUrl !== cfg.baseUrl || this.cfg.apiKey !== cfg.apiKey;
|
||||
if (changed) {
|
||||
this.feed?.stop();
|
||||
this.poller?.stop();
|
||||
this.feed = undefined;
|
||||
this.poller = undefined;
|
||||
this.cfg = cfg;
|
||||
}
|
||||
if (!this.feed) {
|
||||
this.feed = new EventFeed(cfg, (ev) => {
|
||||
this.tracker.apply(ev);
|
||||
|
||||
@@ -4,6 +4,7 @@ export interface SseMessage {
|
||||
}
|
||||
|
||||
export function parseSse(chunk: string): { messages: SseMessage[]; rest: string } {
|
||||
chunk = chunk.replace(/\r\n/g, "\n");
|
||||
const messages: SseMessage[] = [];
|
||||
let rest = chunk;
|
||||
while (true) {
|
||||
|
||||
Reference in New Issue
Block a user