project-nomad/admin/app/controllers/settings_controller.ts
Chris Sherwood 755807f95e feat: Add system benchmark feature with NOMAD Score
Add comprehensive benchmarking capability to measure server performance:

Backend:
- BenchmarkService with CPU, memory, disk, and AI benchmarks using sysbench
- Database migrations for benchmark_results and benchmark_settings tables
- REST API endpoints for running benchmarks and retrieving results
- CLI commands: benchmark:run, benchmark:results, benchmark:submit
- BullMQ job for async benchmark execution with SSE progress updates
- Synchronous mode option (?sync=true) for simpler local dev setup

Frontend:
- Benchmark settings page with circular gauges for scores
- NOMAD Score display with weighted composite calculation
- System Performance section (CPU, Memory, Disk Read/Write)
- AI Performance section (tokens/sec, time to first token)
- Hardware Information display
- Expandable Benchmark Details section
- Progress simulation during sync benchmark execution

Easy Setup Integration:
- Added System Benchmark to Additional Tools section
- Built-in capability pattern for non-Docker features
- Click-to-navigate behavior for built-in tools

Fixes:
- Docker log multiplexing issue (Tty: true) for proper output parsing
- Consolidated disk benchmarks into single container execution

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 21:48:12 -08:00

91 lines
3.0 KiB
TypeScript

import { BenchmarkService } from '#services/benchmark_service';
import { MapService } from '#services/map_service';
import { OpenWebUIService } from '#services/openwebui_service';
import { SystemService } from '#services/system_service';
import { inject } from '@adonisjs/core';
import type { HttpContext } from '@adonisjs/core/http'
@inject()
export default class SettingsController {
constructor(
private systemService: SystemService,
private mapService: MapService,
private openWebUIService: OpenWebUIService,
private benchmarkService: BenchmarkService
) { }
async system({ inertia }: HttpContext) {
const systemInfo = await this.systemService.getSystemInfo();
return inertia.render('settings/system', {
system: {
info: systemInfo
}
});
}
async apps({ inertia }: HttpContext) {
const services = await this.systemService.getServices({ installedOnly: false });
return inertia.render('settings/apps', {
system: {
services
}
});
}
async legal({ inertia }: HttpContext) {
return inertia.render('settings/legal');
}
async maps({ inertia }: HttpContext) {
const baseAssetsCheck = await this.mapService.checkBaseAssetsExist();
const regionFiles = await this.mapService.listRegions();
return inertia.render('settings/maps', {
maps: {
baseAssetsExist: baseAssetsCheck,
regionFiles: regionFiles.files
}
});
}
async models({ inertia }: HttpContext) {
const availableModels = await this.openWebUIService.getAvailableModels();
const installedModels = await this.openWebUIService.getInstalledModels();
return inertia.render('settings/models', {
models: {
availableModels: availableModels || [],
installedModels: installedModels || []
}
});
}
async update({ inertia }: HttpContext) {
const updateInfo = await this.systemService.checkLatestVersion();
return inertia.render('settings/update', {
system: {
updateAvailable: updateInfo.updateAvailable,
latestVersion: updateInfo.latestVersion,
currentVersion: updateInfo.currentVersion
}
});
}
async zim({ inertia }: HttpContext) {
return inertia.render('settings/zim/index')
}
async zimRemote({ inertia }: HttpContext) {
return inertia.render('settings/zim/remote-explorer');
}
async benchmark({ inertia }: HttpContext) {
const latestResult = await this.benchmarkService.getLatestResult();
const status = this.benchmarkService.getStatus();
return inertia.render('settings/benchmark', {
benchmark: {
latestResult,
status: status.status,
currentBenchmarkId: status.benchmarkId
}
});
}
}