feat(cluster,api,models,cli): add cluster-aware model catalog deployments and request routing
This commit is contained in:
+40
-12
@@ -8,7 +8,7 @@ import process from 'node:process';
|
||||
import { promises as fs } from 'node:fs';
|
||||
import { execSync } from 'node:child_process';
|
||||
import { logger } from './logger.ts';
|
||||
import { theme, symbols } from './colors.ts';
|
||||
import { symbols, theme } from './colors.ts';
|
||||
import { PATHS, VERSION } from './constants.ts';
|
||||
|
||||
/**
|
||||
@@ -122,7 +122,9 @@ WantedBy=multi-user.target
|
||||
// Display GPU status
|
||||
await this.displayGpuStatus();
|
||||
} catch (error) {
|
||||
logger.error(`Failed to get status: ${error instanceof Error ? error.message : String(error)}`);
|
||||
logger.error(
|
||||
`Failed to get status: ${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,9 +154,15 @@ WantedBy=multi-user.target
|
||||
|
||||
logger.log('');
|
||||
if (isActive) {
|
||||
logger.log(`${symbols.running} ${theme.success('Service:')} ${theme.statusActive('active (running)')}`);
|
||||
logger.log(
|
||||
`${symbols.running} ${theme.success('Service:')} ${
|
||||
theme.statusActive('active (running)')
|
||||
}`,
|
||||
);
|
||||
} else {
|
||||
logger.log(`${symbols.stopped} ${theme.dim('Service:')} ${theme.statusInactive('inactive')}`);
|
||||
logger.log(
|
||||
`${symbols.stopped} ${theme.dim('Service:')} ${theme.statusInactive('inactive')}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (pid || memory) {
|
||||
@@ -166,7 +174,9 @@ WantedBy=multi-user.target
|
||||
logger.log('');
|
||||
} catch (_error) {
|
||||
logger.log('');
|
||||
logger.log(`${symbols.stopped} ${theme.dim('Service:')} ${theme.statusInactive('not installed')}`);
|
||||
logger.log(
|
||||
`${symbols.stopped} ${theme.dim('Service:')} ${theme.statusInactive('not installed')}`,
|
||||
);
|
||||
logger.log('');
|
||||
}
|
||||
}
|
||||
@@ -177,8 +187,11 @@ WantedBy=multi-user.target
|
||||
private async displayContainerStatus(): Promise<void> {
|
||||
try {
|
||||
// Try to get container info from docker
|
||||
const output = execSync('docker ps --filter "name=modelgrid" --format "{{.Names}}\\t{{.Status}}"', { encoding: 'utf-8' });
|
||||
const lines = output.trim().split('\n').filter(l => l.trim());
|
||||
const output = execSync(
|
||||
'docker ps --filter "name=modelgrid" --format "{{.Names}}\\t{{.Status}}"',
|
||||
{ encoding: 'utf-8' },
|
||||
);
|
||||
const lines = output.trim().split('\n').filter((l) => l.trim());
|
||||
|
||||
if (lines.length === 0) {
|
||||
logger.info('Containers: None running');
|
||||
@@ -191,7 +204,11 @@ WantedBy=multi-user.target
|
||||
const [name, status] = line.split('\t');
|
||||
const isUp = status?.toLowerCase().includes('up');
|
||||
|
||||
logger.log(` ${isUp ? symbols.running : symbols.stopped} ${theme.highlight(name)} - ${isUp ? theme.success(status) : theme.dim(status)}`);
|
||||
logger.log(
|
||||
` ${isUp ? symbols.running : symbols.stopped} ${theme.highlight(name)} - ${
|
||||
isUp ? theme.success(status) : theme.dim(status)
|
||||
}`,
|
||||
);
|
||||
}
|
||||
logger.log('');
|
||||
} catch (_error) {
|
||||
@@ -205,7 +222,10 @@ WantedBy=multi-user.target
|
||||
private async displayGpuStatus(): Promise<void> {
|
||||
try {
|
||||
// Try nvidia-smi
|
||||
const output = execSync('nvidia-smi --query-gpu=name,utilization.gpu,memory.used,memory.total --format=csv,noheader,nounits', { encoding: 'utf-8' });
|
||||
const output = execSync(
|
||||
'nvidia-smi --query-gpu=name,utilization.gpu,memory.used,memory.total --format=csv,noheader,nounits',
|
||||
{ encoding: 'utf-8' },
|
||||
);
|
||||
const lines = output.trim().split('\n');
|
||||
|
||||
if (lines.length === 0) {
|
||||
@@ -215,11 +235,15 @@ WantedBy=multi-user.target
|
||||
logger.info(`GPUs (${lines.length}):`);
|
||||
|
||||
for (const line of lines) {
|
||||
const [name, util, memUsed, memTotal] = line.split(',').map(s => s.trim());
|
||||
const [name, util, memUsed, memTotal] = line.split(',').map((s) => s.trim());
|
||||
const memPercent = Math.round((parseInt(memUsed) / parseInt(memTotal)) * 100);
|
||||
|
||||
logger.log(` ${symbols.info} ${theme.gpuNvidia(name)}`);
|
||||
logger.log(` Utilization: ${theme.highlight(util + '%')} Memory: ${theme.info(memUsed)}/${memTotal} MB (${memPercent}%)`);
|
||||
logger.log(
|
||||
` Utilization: ${theme.highlight(util + '%')} Memory: ${
|
||||
theme.info(memUsed)
|
||||
}/${memTotal} MB (${memPercent}%)`,
|
||||
);
|
||||
}
|
||||
logger.log('');
|
||||
} catch (_error) {
|
||||
@@ -275,7 +299,11 @@ WantedBy=multi-user.target
|
||||
logger.log('');
|
||||
logger.error('No configuration found');
|
||||
logger.log(` ${theme.dim('Config file:')} ${PATHS.CONFIG_FILE}`);
|
||||
logger.log(` ${theme.dim('Run')} ${theme.command('modelgrid config init')} ${theme.dim('to create one')}`);
|
||||
logger.log(
|
||||
` ${theme.dim('Run')} ${theme.command('modelgrid config init')} ${
|
||||
theme.dim('to create one')
|
||||
}`,
|
||||
);
|
||||
logger.log('');
|
||||
throw new Error('Configuration not found');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user