1import { base } from '$app/paths';
  2import { ServerModelStatus } from '$lib/enums';
  3import { getJsonHeaders } from '$lib/utils';
  4
  5/**
  6 * ModelsService - Stateless service for model management API communication
  7 *
  8 * This service handles communication with model-related endpoints:
  9 * - `/v1/models` - OpenAI-compatible model list (MODEL + ROUTER mode)
 10 * - `/models/load`, `/models/unload` - Router-specific model management (ROUTER mode only)
 11 *
 12 * **Responsibilities:**
 13 * - List available models
 14 * - Load/unload models (ROUTER mode)
 15 * - Check model status (ROUTER mode)
 16 *
 17 * **Used by:**
 18 * - modelsStore: Primary consumer for model state management
 19 */
 20export class ModelsService {
 21	// ─────────────────────────────────────────────────────────────────────────────
 22	// Listing
 23	// ─────────────────────────────────────────────────────────────────────────────
 24
 25	/**
 26	 * Fetch list of models from OpenAI-compatible endpoint
 27	 * Works in both MODEL and ROUTER modes
 28	 */
 29	static async list(): Promise<ApiModelListResponse> {
 30		const response = await fetch(`${base}/v1/models`, {
 31			headers: getJsonHeaders()
 32		});
 33
 34		if (!response.ok) {
 35			throw new Error(`Failed to fetch model list (status ${response.status})`);
 36		}
 37
 38		return response.json() as Promise<ApiModelListResponse>;
 39	}
 40
 41	/**
 42	 * Fetch list of all models with detailed metadata (ROUTER mode)
 43	 * Returns models with load status, paths, and other metadata
 44	 */
 45	static async listRouter(): Promise<ApiRouterModelsListResponse> {
 46		const response = await fetch(`${base}/v1/models`, {
 47			headers: getJsonHeaders()
 48		});
 49
 50		if (!response.ok) {
 51			throw new Error(`Failed to fetch router models list (status ${response.status})`);
 52		}
 53
 54		return response.json() as Promise<ApiRouterModelsListResponse>;
 55	}
 56
 57	// ─────────────────────────────────────────────────────────────────────────────
 58	// Load/Unload
 59	// ─────────────────────────────────────────────────────────────────────────────
 60
 61	/**
 62	 * Load a model (ROUTER mode)
 63	 * POST /models/load
 64	 * @param modelId - Model identifier to load
 65	 * @param extraArgs - Optional additional arguments to pass to the model instance
 66	 */
 67	static async load(modelId: string, extraArgs?: string[]): Promise<ApiRouterModelsLoadResponse> {
 68		const payload: { model: string; extra_args?: string[] } = { model: modelId };
 69		if (extraArgs && extraArgs.length > 0) {
 70			payload.extra_args = extraArgs;
 71		}
 72
 73		const response = await fetch(`${base}/models/load`, {
 74			method: 'POST',
 75			headers: getJsonHeaders(),
 76			body: JSON.stringify(payload)
 77		});
 78
 79		if (!response.ok) {
 80			const errorData = await response.json().catch(() => ({}));
 81			throw new Error(errorData.error || `Failed to load model (status ${response.status})`);
 82		}
 83
 84		return response.json() as Promise<ApiRouterModelsLoadResponse>;
 85	}
 86
 87	/**
 88	 * Unload a model (ROUTER mode)
 89	 * POST /models/unload
 90	 * @param modelId - Model identifier to unload
 91	 */
 92	static async unload(modelId: string): Promise<ApiRouterModelsUnloadResponse> {
 93		const response = await fetch(`${base}/models/unload`, {
 94			method: 'POST',
 95			headers: getJsonHeaders(),
 96			body: JSON.stringify({ model: modelId })
 97		});
 98
 99		if (!response.ok) {
100			const errorData = await response.json().catch(() => ({}));
101			throw new Error(errorData.error || `Failed to unload model (status ${response.status})`);
102		}
103
104		return response.json() as Promise<ApiRouterModelsUnloadResponse>;
105	}
106
107	// ─────────────────────────────────────────────────────────────────────────────
108	// Status
109	// ─────────────────────────────────────────────────────────────────────────────
110
111	/**
112	 * Check if a model is loaded based on its metadata
113	 */
114	static isModelLoaded(model: ApiModelDataEntry): boolean {
115		return model.status.value === ServerModelStatus.LOADED;
116	}
117
118	/**
119	 * Check if a model is currently loading
120	 */
121	static isModelLoading(model: ApiModelDataEntry): boolean {
122		return model.status.value === ServerModelStatus.LOADING;
123	}
124}