197 lines · 5.6 KB · TypeScript
RawDownload
// 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,      });    }  );};