History

refactor(repo): adopt src/features convention, controller RouteRegistrar, shared validation

- frontend: src/features/{agent,recipes,discover,settings,usage,plugins,setup,logs,dashboard}; src/ui reduced to primitives; src/components retired; agent trees merged (lib/agent + hooks/agent + app/agent/_components -> features/agent) - controller: RouteRegistrar type across modules, core/validation.ts body helpers, studio provider routes split out - shared API route helpers (app/api/_lib/route-helpers.ts), themes data/API split, lib/types consolidated to recipes.ts + system.ts - structure validator rewritten to enforce the new layering Verified: check:quality (incl. production build), controller integration 49/49, frontend e2e 155/155, contracts check.

seroxdesign committed 5 months ago

Commit c9d1ea51 parent 8d0aacf334 files · +2556 −2495Browse files

Showing the changes to controller/src/modules (12 of 334 files). Show all

Unified · Split

12 files
  1. controller/src/modules/engines/routes.ts +15 −22
  2. controller/src/modules/models/routes.ts +4 −7
  3. controller/src/modules/proxy/interfaces.ts +1 −4
  4. controller/src/modules/proxy/openai-routes.ts +2 −3
  5. controller/src/modules/proxy/routes.ts +2 −3
  6. controller/src/modules/proxy/tokenization-routes.ts +2 −3
  7. controller/src/modules/studio/provider-routes.ts +119 −0
  8. controller/src/modules/studio/routes.ts +4 −116
  9. controller/src/modules/system/logs-routes.ts +2 −3
  10. controller/src/modules/system/metrics-routes.ts +2 −2
  11. controller/src/modules/system/routes.ts +4 −7
  12. controller/src/modules/system/usage-routes.ts +2 −2
controller/src/modules/engines/routes.ts+15−22
@@ -1,7 +1,7 @@
1−import type { Hono } from "hono";
2−import type { AppContext } from "../../types/context";
1+import type { RouteRegistrar } from "../../http/route-registrar";
32 import { delay } from "../../core/async";
43 import { HttpStatus, badRequest, notFound, serviceUnavailable } from "../../core/errors";
4+import { optionalEnum, optionalStringArray, parseJsonObjectBody } from "../../core/validation";
55 import { observeControllerFunction } from "../../core/function-observability";
66 import { parseRecipe } from "../models/recipes/recipe-serializer";
77 import { Event } from "../system/event-manager";
@@ -40,37 +40,30 @@ const resolveHfToken = ( · 32 unmodified lines
4040 return bodyToken || headerToken || envToken;
4141 };
4242
43+const RUNTIME_JOB_BACKENDS = ["vllm", "sglang", "llamacpp", "mlx", "cuda", "rocm"] as const;
44+const RUNTIME_JOB_TYPES = ["install", "update", "download", "inspect"] as const;
45+
4346 const parseRuntimeJobBody = async (ctx: {
4447 req: { json: () => Promise<unknown> };
4548 }): Promise<{
46− backend?: "vllm" | "sglang" | "llamacpp" | "mlx" | "cuda" | "rocm";
49+ backend?: (typeof RUNTIME_JOB_BACKENDS)[number];
4750 targetId?: string;
48− type?: "install" | "update" | "download" | "inspect";
51+ type?: (typeof RUNTIME_JOB_TYPES)[number];
4952 command?: string;
5053 args?: string[];
5154 version?: string;
5255 preferBundled?: boolean;
5356 }> => {
54− const body = await ctx.req.json().catch(() => ({}));
55− if (!body || typeof body !== "object" || Array.isArray(body)) throw badRequest("Invalid payload");
56− const record = body as Record<string, unknown>;
57− const backend = typeof record["backend"] === "string" ? record["backend"] : undefined;
58− if (backend && !["vllm", "sglang", "llamacpp", "mlx", "cuda", "rocm"].includes(backend))
59− throw badRequest("Invalid backend");
60− const type = typeof record["type"] === "string" ? record["type"] : undefined;
61− if (type && !["install", "update", "download", "inspect"].includes(type))
62− throw badRequest("Invalid job type");
63− const args = Array.isArray(record["args"]) ? record["args"] : undefined;
64− if (args?.some((value) => typeof value !== "string"))
65− throw badRequest("args must be an array of strings");
57+ const record = await parseJsonObjectBody(ctx);
58+ const backend = optionalEnum(record, "backend", RUNTIME_JOB_BACKENDS);
59+ const type = optionalEnum(record, "type", RUNTIME_JOB_TYPES, "job type");
60+ const args = optionalStringArray(record, "args");
6661 return {
67− ...(backend
68− ? { backend: backend as "vllm" | "sglang" | "llamacpp" | "mlx" | "cuda" | "rocm" }
69− : {}),
62+ ...(backend ? { backend } : {}),
7063 ...(typeof record["targetId"] === "string" ? { targetId: record["targetId"] } : {}),
71− ...(type ? { type: type as "install" | "update" | "download" | "inspect" } : {}),
64+ ...(type ? { type } : {}),
7265 ...(typeof record["command"] === "string" ? { command: record["command"] } : {}),
73− ...(args ? { args: args as string[] } : {}),
66+ ...(args ? { args } : {}),
7467 ...(typeof record["version"] === "string" ? { version: record["version"] } : {}),
7568 ...(typeof record["prefer_bundled"] === "boolean"
7669 ? { preferBundled: record["prefer_bundled"] }
@@ -78,7 +71,7 @@ const parseRuntimeJobBody = async (ctx: { · 1 unmodified line
7871 };
7972 };
8073
81−export const registerEngineRoutes = (app: Hono, context: AppContext): void => {
74+export const registerEngineRoutes: RouteRegistrar = (app, context) => {
8275 const launchAbortControllers = new Map<string, AbortController>();
8376
8477 app.get("/recipes", async (ctx) => {
controller/src/modules/models/routes.ts+4−7
@@ -1,8 +1,7 @@
1−import type { Hono } from "hono";
21 import { basename, dirname, resolve } from "node:path";
32 import { existsSync } from "node:fs";
43 import { homedir } from "node:os";
5−import type { AppContext } from "../../types/context";
4+import type { RouteRegistrar } from "../../http/route-registrar";
65 import type { Recipe } from "../models/types";
76
87 /**
@@ -28,13 +27,11 @@ interface OpenAIModelList { · 19 unmodified lines
2827 import { buildModelInfo, discoverModelDirectories } from "./model-browser";
2928 import { notFound } from "../../core/errors";
3029 import { observeControllerFunction } from "../../core/function-observability";
30+import { parseBooleanFlag } from "../../core/validation";
3131 import { fetchInference } from "../../services/inference/inference-client";
3232
3333 function isMockInferenceEnabled(): boolean {
34− const raw = process.env["VLLM_STUDIO_MOCK_INFERENCE"];
35− if (!raw) return false;
36− const normalized = String(raw).trim().toLowerCase();
37− return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
34+ return parseBooleanFlag(process.env["VLLM_STUDIO_MOCK_INFERENCE"]);
3835 }
3936
4037 function recipeMetadata(recipe: Recipe): Record<string, unknown> | undefined {
@@ -45,7 +42,7 @@ function recipeMetadata(recipe: Recipe): Record<string, unknown> | undefined { · 4 unmodified lines
4542 return metadata as Record<string, unknown>;
4643 }
4744
48−export const registerModelsRoutes = (app: Hono, context: AppContext): void => {
45+export const registerModelsRoutes: RouteRegistrar = (app, context) => {
4946 app.get("/v1/models", async (ctx) => {
5047 const recipes = context.stores.recipeStore.list();
5148 const current = await observeControllerFunction(
controller/src/modules/proxy/interfaces.ts+1−4
@@ -1,4 +1 @@
1−import type { Hono } from "hono";
2−import type { AppContext } from "../../types/context";
3−
4−export type ProxyRouteRegistrar = (app: Hono, context: AppContext) => void;
1+export type { RouteRegistrar as ProxyRouteRegistrar } from "../../http/route-registrar";
controller/src/modules/proxy/openai-routes.ts+2−3
@@ -1,9 +1,8 @@
11 import { performance } from "node:perf_hooks";
2−import type { Hono } from "hono";
32 import { HttpStatus, notFound, serviceUnavailable } from "../../core/errors";
43 import { isRecipeRunning } from "../models/recipes/recipe-matching";
54 import { buildSseHeaders } from "../../http/sse";
6−import type { AppContext } from "../../types/context";
5+import type { RouteRegistrar } from "../../http/route-registrar";
76 import type { Recipe } from "../models/types";
87 import { getDefaultReasoningParser } from "../engines/process/model-runtime-defaults";
98 import { buildInferenceUrl } from "../../services/inference/inference-client";
@@ -89,7 +88,7 @@ const shouldBufferImplicitReasoningContent = ( · 79 unmodified lines
8988 );
9089 };
9190
92−export const registerOpenAIRoutes = (app: Hono, context: AppContext): void => {
91+export const registerOpenAIRoutes: RouteRegistrar = (app, context) => {
9392 const nonRunningModelWarnings = new Map<string, NonRunningModelWarningState>();
9493
9594 const warnNonRunningModel = (details: {
controller/src/modules/proxy/routes.ts+2−3
@@ -1,9 +1,8 @@
1−import type { Hono } from "hono";
2−import type { AppContext } from "../../types/context";
1+import type { RouteRegistrar } from "../../http/route-registrar";
32 import { registerOpenAIRoutes } from "./openai-routes";
43 import { registerTokenizationRoutes } from "./tokenization-routes";
54
6−export const registerAllProxyRoutes = (app: Hono, context: AppContext): void => {
5+export const registerAllProxyRoutes: RouteRegistrar = (app, context) => {
76 registerOpenAIRoutes(app, context);
87 registerTokenizationRoutes(app, context);
98 };
controller/src/modules/proxy/tokenization-routes.ts+2−3
@@ -1,9 +1,8 @@
1−import type { Hono } from "hono";
21 import { observeControllerFunction } from "../../core/function-observability";
3−import type { AppContext } from "../../types/context";
2+import type { RouteRegistrar } from "../../http/route-registrar";
43 import { fetchInference } from "../../services/inference/inference-client";
54
6−export const registerTokenizationRoutes = (app: Hono, context: AppContext): void => {
5+export const registerTokenizationRoutes: RouteRegistrar = (app, context) => {
76 app.post("/v1/tokenize", async (ctx) => {
87 const current = await observeControllerFunction(context, "tokenize.findInferenceProcess", () =>
98 context.processManager.findInferenceProcess(context.config.inference_port)
controller/src/modules/studio/provider-routes.ts+119−0
@@ -0,0 +1,119 @@
1+import { badRequest, notFound } from "../../core/errors";
2+import { parseJsonObjectBody } from "../../core/validation";
3+import type { RouteRegistrar } from "../../http/route-registrar";
4+import { savePersistedConfig, type ProviderConfig } from "../../config/persisted-config";
5+
6+/** CRUD for external inference providers plus model discovery across them. */
7+export const registerStudioProviderRoutes: RouteRegistrar = (app, context) => {
8+ app.get("/studio/providers", async (ctx) => {
9+ const providers = context.config.providers.map((p) => ({
10+ id: p.id,
11+ name: p.name,
12+ base_url: p.base_url,
13+ enabled: p.enabled,
14+ has_api_key: Boolean(p.api_key),
15+ }));
16+ return ctx.json({ providers });
17+ });
18+
19+ app.post("/studio/providers", async (ctx) => {
20+ const body = await parseJsonObjectBody(ctx);
21+
22+ const id = typeof body["id"] === "string" ? body["id"].trim().toLowerCase() : "";
23+ const name = typeof body["name"] === "string" ? body["name"].trim() : "";
24+ const baseUrl = typeof body["base_url"] === "string" ? body["base_url"].trim() : "";
25+ const apiKey = typeof body["api_key"] === "string" ? body["api_key"].trim() : "";
26+ const enabled = typeof body["enabled"] === "boolean" ? body["enabled"] : true;
27+
28+ if (!id) throw badRequest("id is required");
29+ if (!name) throw badRequest("name is required");
30+ if (!baseUrl) throw badRequest("base_url is required");
31+
32+ const existing = context.config.providers.find((p) => p.id === id);
33+ if (existing) throw badRequest(`Provider "${id}" already exists`);
34+
35+ const provider: ProviderConfig = { id, name, base_url: baseUrl, api_key: apiKey, enabled };
36+ const providers = [...context.config.providers, provider];
37+ savePersistedConfig(context.config.data_dir, { providers });
38+ context.config.providers = providers;
39+
40+ return ctx.json({
41+ success: true,
42+ provider: { id, name, base_url: baseUrl, enabled, has_api_key: Boolean(apiKey) },
43+ });
44+ });
45+
46+ app.put("/studio/providers/:id", async (ctx) => {
47+ const providerId = ctx.req.param("id");
48+ const body = await ctx.req.json().catch(() => ({}));
49+ if (!body || typeof body !== "object") throw badRequest("Invalid payload");
50+
51+ const index = context.config.providers.findIndex((p) => p.id === providerId);
52+ if (index < 0) throw notFound(`Provider "${providerId}" not found`);
53+
54+ const current = context.config.providers[index];
55+ if (!current) throw notFound(`Provider "${providerId}" not found`);
56+
57+ const name = typeof body.name === "string" ? body.name.trim() : current.name;
58+ const baseUrl = typeof body.base_url === "string" ? body.base_url.trim() : current.base_url;
59+ const apiKey = typeof body.api_key === "string" ? body.api_key.trim() : current.api_key;
60+ const enabled = typeof body.enabled === "boolean" ? body.enabled : current.enabled;
61+
62+ const updated: ProviderConfig = {
63+ id: providerId,
64+ name,
65+ base_url: baseUrl,
66+ api_key: apiKey,
67+ enabled,
68+ };
69+ const providers = [...context.config.providers];
70+ providers[index] = updated;
71+ savePersistedConfig(context.config.data_dir, { providers });
72+ context.config.providers = providers;
73+
74+ return ctx.json({
75+ success: true,
76+ provider: { id: providerId, name, base_url: baseUrl, enabled, has_api_key: Boolean(apiKey) },
77+ });
78+ });
79+
80+ app.delete("/studio/providers/:id", async (ctx) => {
81+ const providerId = ctx.req.param("id");
82+ const index = context.config.providers.findIndex((p) => p.id === providerId);
83+ if (index < 0) throw notFound(`Provider "${providerId}" not found`);
84+
85+ const providers = context.config.providers.filter((p) => p.id !== providerId);
86+ savePersistedConfig(context.config.data_dir, { providers });
87+ context.config.providers = providers;
88+
89+ return ctx.json({ success: true });
90+ });
91+
92+ // Fetch models from all configured providers
93+ app.get("/studio/provider-models", async (ctx) => {
94+ const enabledProviders = context.config.providers.filter((p) => p.enabled && p.api_key);
95+ const results: Array<{ provider: string; models: Array<{ id: string; name?: string }> }> = [];
96+
97+ await Promise.all(
98+ enabledProviders.map(async (provider) => {
99+ try {
100+ const url = `${provider.base_url.replace(/\/+$/, "")}/v1/models`;
101+ const res = await fetch(url, {
102+ headers: { Authorization: `Bearer ${provider.api_key}` },
103+ signal: AbortSignal.timeout(10_000),
104+ });
105+ if (!res.ok) return;
106+ const data = (await res.json()) as { data?: Array<{ id?: string }> };
107+ const models = (data.data ?? [])
108+ .filter((m) => typeof m.id === "string" && m.id.length > 0)
109+ .map((m) => ({ id: m.id as string }));
110+ results.push({ provider: provider.id, models });
111+ } catch {
112+ // skip unreachable providers
113+ }
114+ })
115+ );
116+
117+ return ctx.json({ providers: results });
118+ });
119+};
controller/src/modules/studio/routes.ts+4−116
@@ -1,4 +1,3 @@
1−import type { Hono } from "hono";
21 import { cpus, freemem, totalmem, platform, arch, release } from "node:os";
32 import {
43 existsSync,
@@ -12,7 +11,8 @@ import { · 7 unmodified lines
1211 } from "node:fs";
1312 import { basename, resolve, sep } from "node:path";
1413 import { badRequest, notFound } from "../../core/errors";
15−import type { AppContext } from "../../types/context";
14+import type { RouteRegistrar } from "../../http/route-registrar";
15+import { registerStudioProviderRoutes } from "./provider-routes";
1616 import { getGpuInfo } from "../system/platform/gpu";
1717 import type { GpuInfo } from "../models/types";
1818 import { discoverModelDirectories, estimateWeightsSizeBytes } from "../models/model-browser";
@@ -21,7 +21,6 @@ import { · 2 unmodified lines
2121 getPersistedConfigPath,
2222 loadPersistedConfig,
2323 savePersistedConfig,
24− type ProviderConfig,
2524 } from "../../config/persisted-config";
2625 import { getVllmRuntimeInfo } from "../engines/runtimes/vllm-runtime";
2726
@@ -111,7 +110,7 @@ const parseUiPreferencesUpdate = (value: unknown): Record<string, string> | null · 83 unmodified lines
111110 return clean;
112111 };
113112
114−export const registerStudioRoutes = (app: Hono, context: AppContext): void => {
113+export const registerStudioRoutes: RouteRegistrar = (app, context) => {
115114 const buildSettingsPayload = (): {
116115 config_path: string;
117116 persisted: {
@@ -318,116 +317,5 @@ export const registerStudioRoutes = (app: Hono, context: AppContext): void => { · 200 unmodified lines
318317 return ctx.json({ success: true, target });
319318 });
320319
321− app.get("/studio/providers", async (ctx) => {
322− const providers = context.config.providers.map((p) => ({
323− id: p.id,
324− name: p.name,
325− base_url: p.base_url,
326− enabled: p.enabled,
327− has_api_key: Boolean(p.api_key),
328− }));
329− return ctx.json({ providers });
330− });
331−
332− app.post("/studio/providers", async (ctx) => {
333− const body = await ctx.req.json().catch(() => ({}));
334− if (!body || typeof body !== "object") throw badRequest("Invalid payload");
335−
336− const id = typeof body.id === "string" ? body.id.trim().toLowerCase() : "";
337− const name = typeof body.name === "string" ? body.name.trim() : "";
338− const baseUrl = typeof body.base_url === "string" ? body.base_url.trim() : "";
339− const apiKey = typeof body.api_key === "string" ? body.api_key.trim() : "";
340− const enabled = typeof body.enabled === "boolean" ? body.enabled : true;
341−
342− if (!id) throw badRequest("id is required");
343− if (!name) throw badRequest("name is required");
344− if (!baseUrl) throw badRequest("base_url is required");
345−
346− const existing = context.config.providers.find((p) => p.id === id);
347− if (existing) throw badRequest(`Provider "${id}" already exists`);
348−
349− const provider: ProviderConfig = { id, name, base_url: baseUrl, api_key: apiKey, enabled };
350− const providers = [...context.config.providers, provider];
351− savePersistedConfig(context.config.data_dir, { providers });
352− context.config.providers = providers;
353−
354− return ctx.json({
355− success: true,
356− provider: { id, name, base_url: baseUrl, enabled, has_api_key: Boolean(apiKey) },
357− });
358− });
359−
360− app.put("/studio/providers/:id", async (ctx) => {
361− const providerId = ctx.req.param("id");
362− const body = await ctx.req.json().catch(() => ({}));
363− if (!body || typeof body !== "object") throw badRequest("Invalid payload");
364−
365− const index = context.config.providers.findIndex((p) => p.id === providerId);
366− if (index < 0) throw notFound(`Provider "${providerId}" not found`);
367−
368− const current = context.config.providers[index];
369− if (!current) throw notFound(`Provider "${providerId}" not found`);
370−
371− const name = typeof body.name === "string" ? body.name.trim() : current.name;
372− const baseUrl = typeof body.base_url === "string" ? body.base_url.trim() : current.base_url;
373− const apiKey = typeof body.api_key === "string" ? body.api_key.trim() : current.api_key;
374− const enabled = typeof body.enabled === "boolean" ? body.enabled : current.enabled;
375−
376− const updated: ProviderConfig = {
377− id: providerId,
378− name,
379− base_url: baseUrl,
380− api_key: apiKey,
381− enabled,
382− };
383− const providers = [...context.config.providers];
384− providers[index] = updated;
385− savePersistedConfig(context.config.data_dir, { providers });
386− context.config.providers = providers;
387−
388− return ctx.json({
389− success: true,
390− provider: { id: providerId, name, base_url: baseUrl, enabled, has_api_key: Boolean(apiKey) },
391− });
392− });
393−
394− app.delete("/studio/providers/:id", async (ctx) => {
395− const providerId = ctx.req.param("id");
396− const index = context.config.providers.findIndex((p) => p.id === providerId);
397− if (index < 0) throw notFound(`Provider "${providerId}" not found`);
398−
399− const providers = context.config.providers.filter((p) => p.id !== providerId);
400− savePersistedConfig(context.config.data_dir, { providers });
401− context.config.providers = providers;
402−
403− return ctx.json({ success: true });
404− });
405−
406− // Fetch models from all configured providers
407− app.get("/studio/provider-models", async (ctx) => {
408− const enabledProviders = context.config.providers.filter((p) => p.enabled && p.api_key);
409− const results: Array<{ provider: string; models: Array<{ id: string; name?: string }> }> = [];
410−
411− await Promise.all(
412− enabledProviders.map(async (provider) => {
413− try {
414− const url = `${provider.base_url.replace(/\/+$/, "")}/v1/models`;
415− const res = await fetch(url, {
416− headers: { Authorization: `Bearer ${provider.api_key}` },
417− signal: AbortSignal.timeout(10_000),
418− });
419− if (!res.ok) return;
420− const data = (await res.json()) as { data?: Array<{ id?: string }> };
421− const models = (data.data ?? [])
422− .filter((m) => typeof m.id === "string" && m.id.length > 0)
423− .map((m) => ({ id: m.id as string }));
424− results.push({ provider: provider.id, models });
425− } catch {
426− // skip unreachable providers
427− }
428− })
429− );
430−
431− return ctx.json({ providers: results });
432− });
320+ registerStudioProviderRoutes(app, context);
433321 };
controller/src/modules/system/logs-routes.ts+2−3
@@ -1,9 +1,8 @@
1−import type { Hono } from "hono";
21 import { spawn, spawnSync } from "node:child_process";
32 import { unlinkSync } from "node:fs";
43 import { createInterface } from "node:readline";
54 import { PassThrough } from "node:stream";
6−import type { AppContext } from "../../types/context";
5+import type { RouteRegistrar } from "../../http/route-registrar";
76 import { badRequest, notFound } from "../../core/errors";
87 import { observeControllerFunction } from "../../core/function-observability";
98 import { streamAsyncStrings, buildSseHeaders, withSseHeartbeat } from "../../http/sse";
@@ -21,7 +20,7 @@ import { · 11 unmodified lines
2120 tailFileLines,
2221 } from "../../core/log-files";
2322
24−export const registerLogsRoutes = (app: Hono, context: AppContext): void => {
23+export const registerLogsRoutes: RouteRegistrar = (app, context) => {
2524 let lastCleanupAt = 0;
2625
2726 const maybeCleanup = (): void => {
controller/src/modules/system/metrics-routes.ts+2−2
@@ -1,6 +1,6 @@
1−import type { Hono } from "hono";
21 import { performance } from "node:perf_hooks";
32 import { observeControllerFunction } from "../../core/function-observability";
3+import type { RouteRegistrar } from "../../http/route-registrar";
44 import type { AppContext } from "../../types/context";
55 import { getGpuInfo } from "./platform/gpu";
66 import { fetchInference } from "../../services/inference/inference-client";
@@ -241,7 +241,7 @@ const buildCurrentMetrics = async (context: AppContext): Promise<Record<string, · 234 unmodified lines
241241 };
242242 };
243243
244−export const registerMonitoringRoutes = (app: Hono, context: AppContext): void => {
244+export const registerMonitoringRoutes: RouteRegistrar = (app, context) => {
245245 app.get("/metrics", async (_ctx) => {
246246 const current = await observeControllerFunction(
247247 context,
controller/src/modules/system/routes.ts+4−7
@@ -1,11 +1,11 @@
1−import type { Hono } from "hono";
21 import { connect } from "node:net";
32 import { hostname } from "node:os";
43 import { existsSync, readFileSync } from "node:fs";
54 import { join, resolve, sep } from "node:path";
6−import type { AppContext } from "../../types/context";
5+import type { RouteRegistrar } from "../../http/route-registrar";
76 import type { SystemConfigResponse } from "../models/types";
87 import { badRequest, notFound } from "../../core/errors";
8+import { parseJsonObjectBody } from "../../core/validation";
99 import { observeControllerFunction } from "../../core/function-observability";
1010 import { estimateWeightsSizeBytes } from "../models/model-browser";
1111 import { getGpuInfo } from "./platform/gpu";
@@ -21,7 +21,7 @@ import { · 9 unmodified lines
2121 SYSTEM_SERVICE_CHECK_HOST,
2222 } from "./configs";
2323
24−export const registerSystemRoutes = (app: Hono, context: AppContext): void => {
24+export const registerSystemRoutes: RouteRegistrar = (app, context) => {
2525 const checkService = (
2626 host: string,
2727 port: number,
@@ -87,10 +87,7 @@ export const registerSystemRoutes = (app: Hono, context: AppContext): void => { · 59 unmodified lines
8787 });
8888
8989 app.post("/vram-calculator", async (ctx) => {
90− const body = await ctx.req.json().catch(() => ({}));
91− if (!body || typeof body !== "object") {
92− throw badRequest("Invalid payload");
93− }
90+ const body = await parseJsonObjectBody(ctx);
9491
9592 const model = typeof body["model"] === "string" ? body["model"].trim() : "";
9693 const contextLength = Number(body["context_length"] ?? 0);
controller/src/modules/system/usage-routes.ts+2−2
@@ -1,5 +1,5 @@
1−import type { Hono } from "hono";
21 import { observeControllerFunction } from "../../core/function-observability";
2+import type { RouteRegistrar } from "../../http/route-registrar";
33 import type { AppContext } from "../../types/context";
44 import { getUsageFromPiSessions } from "./usage/pi-sessions";
55 import { emptyResponse } from "./usage/usage-utilities";
@@ -20,7 +20,7 @@ const collectKnownModels = async (context: AppContext): Promise<Set<string>> => · 14 unmodified lines
2020 return knownModels;
2121 };
2222
23−export const registerUsageRoutes = (app: Hono, context: AppContext): void => {
23+export const registerUsageRoutes: RouteRegistrar = (app, context) => {
2424 app.get("/usage", async (ctx) => {
2525 try {
2626 const knownModels = await observeControllerFunction(context, "usage.collectKnownModels", () =>

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t