// CRITICALimport type { OpenAPIHono } from "@hono/zod-openapi";import type { AppContext } from "../types/context";import { HealthResponseSchema, StatusResponseSchema, GPUListResponseSchema, SystemConfigResponseSchema,} from "../types/schemas";import { getGpuInfo } from "../services/gpu"; /** * Register system routes with OpenAPI. * @param app - OpenAPIHono application. * @param context - App context. */export const registerSystemRoutes = (app: OpenAPIHono, context: AppContext): void => { // GET /health - Health check endpoint app.openapi( { method: "get", path: "/health", description: "Health check endpoint for monitoring and load balancers", responses: { 200: { description: "Service is healthy", content: { "application/json": { schema: HealthResponseSchema, }, }, }, }, }, async (ctx) => { const current = await context.processManager.findInferenceProcess(context.config.inference_port); let inferenceReady = false; if (current) { try { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 5000); const response = await fetch(`http://localhost:${context.config.inference_port}/health`, { signal: controller.signal, }); clearTimeout(timeout); inferenceReady = response.status === 200; } catch { inferenceReady = false; } } return ctx.json({ status: "ok", version: "0.3.1", inference_ready: inferenceReady, backend_reachable: inferenceReady, running_model: current ? (current.served_model_name ?? current.model_path ?? null) : null, }); } ); // GET /status - Get running model status app.openapi( { method: "get", path: "/status", description: "Get current status of the inference backend and any running model", responses: { 200: { description: "Status information", content: { "application/json": { schema: StatusResponseSchema, }, }, }, }, }, async (ctx) => { const current = await context.processManager.findInferenceProcess(context.config.inference_port); // Transform process to match schema (narrowing backend type and ensuring model_path is string) const processInfo = current ? { pid: current.pid, backend: current.backend as "vllm" | "sglang" | "tabby", model_path: current.model_path ?? "", port: current.port, served_model_name: current.served_model_name ?? undefined, } : undefined; return ctx.json({ running: Boolean(current), process: processInfo, inference_port: context.config.inference_port, launching: context.launchState.getLaunchingRecipeId() ?? undefined, }); } ); // GET /gpus - List available GPUs app.openapi( { method: "get", path: "/gpus", description: "Get GPU information including memory usage, utilization, and temperature", responses: { 200: { description: "GPU information", content: { "application/json": { schema: GPUListResponseSchema, }, }, }, }, }, async (ctx) => { const gpus = getGpuInfo(); return ctx.json({ count: gpus.length, gpus, }); } ); // GET /config - System configuration app.openapi( { method: "get", path: "/config", description: "Get system topology and service discovery information", responses: { 200: { description: "System configuration", content: { "application/json": { schema: SystemConfigResponseSchema, }, }, }, }, }, async (ctx) => { const services: Array<{ name: string; port: number; internal_port: number; protocol: string; status: string; description?: string | null; }> = []; services.push({ name: "Controller", port: context.config.port, internal_port: context.config.port, protocol: "http", status: "running", description: "FastAPI model lifecycle manager", }); let inferenceStatus = "unknown"; try { const current = await context.processManager.findInferenceProcess(context.config.inference_port); if (current) { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 2000); const response = await fetch(`http://localhost:${context.config.inference_port}/health`, { signal: controller.signal, }); clearTimeout(timeout); inferenceStatus = response.status === 200 ? "running" : "error"; } else { inferenceStatus = "stopped"; } } catch { inferenceStatus = "stopped"; } services.push({ name: "vLLM/SGLang", port: context.config.inference_port, internal_port: context.config.inference_port, protocol: "http", status: inferenceStatus, description: "Inference backend (vLLM or SGLang)", }); return ctx.json({ port: context.config.port, inference_port: context.config.inference_port, models_dir: context.config.models_dir, data_dir: context.config.data_dir, services, }); } );};
// CRITICAL
import type { OpenAPIHono } from "@hono/zod-openapi";
import type { AppContext } from "../types/context";
import {
HealthResponseSchema,
StatusResponseSchema,
GPUListResponseSchema,
SystemConfigResponseSchema,
} from "../types/schemas";
import { getGpuInfo } from "../services/gpu";
/**
* Register system routes with OpenAPI.
* @param app - OpenAPIHono application.
* @param context - App context.
*/
export const registerSystemRoutes = (app: OpenAPIHono, context: AppContext): void => {
// GET /health - Health check endpoint
app.openapi(
{
method: "get",
path: "/health",
description: "Health check endpoint for monitoring and load balancers",
responses: {
200: {
description: "Service is healthy",
content: {
"application/json": {
schema: HealthResponseSchema,
},
},
},
},
},
async (ctx) => {
const current = await context.processManager.findInferenceProcess(context.config.inference_port);
let inferenceReady = false;
if (current) {
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);
const response = await fetch(`http://localhost:${context.config.inference_port}/health`, {
signal: controller.signal,
});
clearTimeout(timeout);
inferenceReady = response.status === 200;
} catch {
inferenceReady = false;
}
}
return ctx.json({
status: "ok",
version: "0.3.1",
inference_ready: inferenceReady,
backend_reachable: inferenceReady,
running_model: current ? (current.served_model_name ?? current.model_path ?? null) : null,
});
}
);
// GET /status - Get running model status
app.openapi(
{
method: "get",
path: "/status",
description: "Get current status of the inference backend and any running model",
responses: {
200: {
description: "Status information",
content: {
"application/json": {
schema: StatusResponseSchema,
},
},
},
},
},
async (ctx) => {
const current = await context.processManager.findInferenceProcess(context.config.inference_port);
// Transform process to match schema (narrowing backend type and ensuring model_path is string)
const processInfo = current ? {
pid: current.pid,
backend: current.backend as "vllm" | "sglang" | "tabby",
model_path: current.model_path ?? "",
port: current.port,
served_model_name: current.served_model_name ?? undefined,
} : undefined;
return ctx.json({
running: Boolean(current),
process: processInfo,
inference_port: context.config.inference_port,
launching: context.launchState.getLaunchingRecipeId() ?? undefined,
});
}
);
// GET /gpus - List available GPUs
app.openapi(
{
method: "get",
path: "/gpus",
description: "Get GPU information including memory usage, utilization, and temperature",
responses: {
200: {
description: "GPU information",
content: {
"application/json": {
schema: GPUListResponseSchema,
},
},
},
},
},
async (ctx) => {
const gpus = getGpuInfo();
return ctx.json({
count: gpus.length,
gpus,
});
}
);
// GET /config - System configuration
app.openapi(
{
method: "get",
path: "/config",
description: "Get system topology and service discovery information",
responses: {
200: {
description: "System configuration",
content: {
"application/json": {
schema: SystemConfigResponseSchema,
},
},
},
},
},
async (ctx) => {
const services: Array<{
name: string;
port: number;
internal_port: number;
protocol: string;
status: string;
description?: string | null;
}> = [];
services.push({
name: "Controller",
port: context.config.port,
internal_port: context.config.port,
protocol: "http",
status: "running",
description: "FastAPI model lifecycle manager",
});
let inferenceStatus = "unknown";
try {
const current = await context.processManager.findInferenceProcess(context.config.inference_port);
if (current) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 2000);
const response = await fetch(`http://localhost:${context.config.inference_port}/health`, {
signal: controller.signal,
});
clearTimeout(timeout);
inferenceStatus = response.status === 200 ? "running" : "error";
} else {
inferenceStatus = "stopped";
}
} catch {
inferenceStatus = "stopped";
}
services.push({
name: "vLLM/SGLang",
port: context.config.inference_port,
internal_port: context.config.inference_port,
protocol: "http",
status: inferenceStatus,
description: "Inference backend (vLLM or SGLang)",
});
return ctx.json({
port: context.config.port,
inference_port: context.config.inference_port,
models_dir: context.config.models_dir,
data_dir: context.config.data_dir,
services,
});
}
);
};