From 0ea98caed63a255cba51446c8cef7953265e6034 Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Tue, 21 Apr 2026 12:45:16 +0000 Subject: [PATCH] refactor(config): extract config loading from modelgrid --- test/modelgrid-config_test.ts | 11 ++--- ts/config/config-manager.ts | 75 ++++++++++++++++++++++++++++++++ ts/modelgrid.ts | 82 +++-------------------------------- 3 files changed, 85 insertions(+), 83 deletions(-) create mode 100644 ts/config/config-manager.ts diff --git a/test/modelgrid-config_test.ts b/test/modelgrid-config_test.ts index 83bac92..75eecca 100644 --- a/test/modelgrid-config_test.ts +++ b/test/modelgrid-config_test.ts @@ -1,14 +1,11 @@ import { assertEquals } from 'jsr:@std/assert@^1.0.0'; -import { ModelGrid } from '../ts/modelgrid.ts'; +import { ConfigManager } from '../ts/config/config-manager.ts'; import type { IModelGridConfig } from '../ts/interfaces/config.ts'; -Deno.test('ModelGrid normalizes current config defaults', () => { - const modelgrid = new ModelGrid(); - const normalizeConfig = (modelgrid as unknown as { - normalizeConfig: (config: Partial) => IModelGridConfig; - }).normalizeConfig.bind(modelgrid); +Deno.test('ConfigManager normalizes current config defaults', () => { + const configManager = new ConfigManager(); - const normalized = normalizeConfig({ + const normalized = configManager.normalizeConfig({ version: '1.0.0', api: { port: 9000, diff --git a/ts/config/config-manager.ts b/ts/config/config-manager.ts new file mode 100644 index 0000000..07facf2 --- /dev/null +++ b/ts/config/config-manager.ts @@ -0,0 +1,75 @@ +import * as fs from 'node:fs/promises'; +import { PATHS, VERSION } from '../constants.ts'; +import type { IModelGridConfig } from '../interfaces/config.ts'; + +export class ConfigManager { + public async loadConfig(): Promise { + try { + const configContent = await fs.readFile(PATHS.CONFIG_FILE, 'utf-8'); + return this.normalizeConfig(JSON.parse(configContent) as Partial); + } catch (error) { + if ((error as NodeJS.ErrnoException).code === 'ENOENT') { + throw new Error(`Configuration file not found: ${PATHS.CONFIG_FILE}`); + } + throw error; + } + } + + public async saveConfig(config: IModelGridConfig): Promise { + await fs.mkdir(PATHS.CONFIG_DIR, { recursive: true }); + await fs.writeFile(PATHS.CONFIG_FILE, JSON.stringify(config, null, 2)); + } + + public normalizeConfig(config: Partial): IModelGridConfig { + const filteredContainers = (config.containers || []).filter( + (container) => (container as { type?: string }).type !== 'ollama', + ); + + return { + version: config.version || VERSION, + api: { + port: config.api?.port || 8080, + host: config.api?.host || '0.0.0.0', + apiKeys: config.api?.apiKeys || [], + rateLimit: config.api?.rateLimit, + cors: config.api?.cors ?? true, + corsOrigins: config.api?.corsOrigins || ['*'], + }, + ui: { + enabled: config.ui?.enabled ?? true, + port: config.ui?.port || 8081, + host: config.ui?.host || '0.0.0.0', + assetSource: config.ui?.assetSource === 'disk' ? 'disk' : 'bundle', + }, + docker: { + networkName: config.docker?.networkName || 'modelgrid', + runtime: config.docker?.runtime || 'docker', + socketPath: config.docker?.socketPath, + }, + gpus: { + autoDetect: config.gpus?.autoDetect ?? true, + assignments: config.gpus?.assignments || {}, + }, + containers: filteredContainers, + models: { + registryUrl: config.models?.registryUrl || 'https://list.modelgrid.com/catalog/models.json', + autoDeploy: config.models?.autoDeploy ?? true, + defaultEngine: 'vllm', + autoLoad: config.models?.autoLoad || [], + }, + cluster: { + enabled: config.cluster?.enabled ?? false, + nodeName: config.cluster?.nodeName || 'modelgrid-local', + role: config.cluster?.role || 'standalone', + bindHost: config.cluster?.bindHost || '0.0.0.0', + gossipPort: config.cluster?.gossipPort || 7946, + sharedSecret: config.cluster?.sharedSecret, + advertiseUrl: config.cluster?.advertiseUrl, + controlPlaneUrl: config.cluster?.controlPlaneUrl, + heartbeatIntervalMs: config.cluster?.heartbeatIntervalMs || 5000, + seedNodes: config.cluster?.seedNodes || [], + }, + checkInterval: config.checkInterval || 30000, + }; + } +} diff --git a/ts/modelgrid.ts b/ts/modelgrid.ts index 8d2c431..fc3e26a 100644 --- a/ts/modelgrid.ts +++ b/ts/modelgrid.ts @@ -24,7 +24,7 @@ import { ClusterHandler } from './cli/cluster-handler.ts'; import { ModelHandler } from './cli/model-handler.ts'; import { ConfigHandler } from './cli/config-handler.ts'; import { ServiceHandler } from './cli/service-handler.ts'; -import * as fs from 'node:fs/promises'; +import { ConfigManager } from './config/config-manager.ts'; /** * ModelGrid - Main application coordinator @@ -42,6 +42,7 @@ export class ModelGrid { private clusterCoordinator?: ClusterCoordinator; private modelRegistry: ModelRegistry; private modelLoader?: ModelLoader; + private configManager: ConfigManager; // CLI Handlers private gpuHandler: GpuHandler; @@ -60,6 +61,7 @@ export class ModelGrid { this.containerManager = new ContainerManager(); this.clusterManager = new ClusterManager(); this.modelRegistry = new ModelRegistry(); + this.configManager = new ConfigManager(); this.systemd = new Systemd(); this.daemon = new Daemon(this); @@ -80,23 +82,8 @@ export class ModelGrid { * Load configuration from file */ public async loadConfig(): Promise { - try { - const configContent = await fs.readFile(PATHS.CONFIG_FILE, 'utf-8'); - this.config = this.normalizeConfig( - JSON.parse(configContent) as Partial & { - models?: { - greenlistUrl?: string; - autoPull?: boolean; - } & Partial; - }, - ); - logger.dim(`Configuration loaded from ${PATHS.CONFIG_FILE}`); - } catch (error) { - if ((error as NodeJS.ErrnoException).code === 'ENOENT') { - throw new Error(`Configuration file not found: ${PATHS.CONFIG_FILE}`); - } - throw error; - } + this.config = await this.configManager.loadConfig(); + logger.dim(`Configuration loaded from ${PATHS.CONFIG_FILE}`); } /** @@ -107,8 +94,7 @@ export class ModelGrid { throw new Error('No configuration to save'); } - await fs.mkdir(PATHS.CONFIG_DIR, { recursive: true }); - await fs.writeFile(PATHS.CONFIG_FILE, JSON.stringify(this.config, null, 2)); + await this.configManager.saveConfig(this.config); logger.dim(`Configuration saved to ${PATHS.CONFIG_FILE}`); } @@ -294,62 +280,6 @@ export class ModelGrid { logger.success('ModelGrid initialized'); } - private normalizeConfig( - config: Partial, - ): IModelGridConfig { - const filteredContainers = (config.containers || []).filter( - (container) => (container as { type?: string }).type !== 'ollama', - ); - - return { - version: config.version || VERSION, - api: { - port: config.api?.port || 8080, - host: config.api?.host || '0.0.0.0', - apiKeys: config.api?.apiKeys || [], - rateLimit: config.api?.rateLimit, - cors: config.api?.cors ?? true, - corsOrigins: config.api?.corsOrigins || ['*'], - }, - ui: { - enabled: config.ui?.enabled ?? true, - port: config.ui?.port || 8081, - host: config.ui?.host || '0.0.0.0', - assetSource: config.ui?.assetSource === 'disk' ? 'disk' : 'bundle', - }, - docker: { - networkName: config.docker?.networkName || 'modelgrid', - runtime: config.docker?.runtime || 'docker', - socketPath: config.docker?.socketPath, - }, - gpus: { - autoDetect: config.gpus?.autoDetect ?? true, - assignments: config.gpus?.assignments || {}, - }, - containers: filteredContainers, - models: { - registryUrl: config.models?.registryUrl || - 'https://list.modelgrid.com/catalog/models.json', - autoDeploy: config.models?.autoDeploy ?? true, - defaultEngine: 'vllm', - autoLoad: config.models?.autoLoad || [], - }, - cluster: { - enabled: config.cluster?.enabled ?? false, - nodeName: config.cluster?.nodeName || 'modelgrid-local', - role: config.cluster?.role || 'standalone', - bindHost: config.cluster?.bindHost || '0.0.0.0', - gossipPort: config.cluster?.gossipPort || 7946, - sharedSecret: config.cluster?.sharedSecret, - advertiseUrl: config.cluster?.advertiseUrl, - controlPlaneUrl: config.cluster?.controlPlaneUrl, - heartbeatIntervalMs: config.cluster?.heartbeatIntervalMs || 5000, - seedNodes: config.cluster?.seedNodes || [], - }, - checkInterval: config.checkInterval || 30000, - }; - } - /** * Shutdown the ModelGrid system */