import { FastifyInstance } from 'fastify'; import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs'; import { resolve, dirname } from 'path'; const CONFIG_PATH = resolve(process.env.BOOCODE_DATA_DIR || '/opt/boocode/data', 'inference-settings.json'); const DEFAULTS = { cache_type_k: 'q4_0', cache_reuse: 256, spec_type: 'ngram-mod', spec_ngram_mod_thsh: 2, ctx_checkpoints: 32, sleep_idle_seconds: 600, metrics_enabled: true, slot_save_path: '/tmp/llama-slots', }; function load(): Record { try { if (existsSync(CONFIG_PATH)) { return JSON.parse(readFileSync(CONFIG_PATH, 'utf-8')); } } catch { /* corrupt file */ } return { ...DEFAULTS }; } function save(data: Record): void { const dir = dirname(CONFIG_PATH); if (!existsSync(dir)) mkdirSync(dir, { recursive: true }); writeFileSync(CONFIG_PATH, JSON.stringify(data, null, 2) + '\n'); } const VALID_CACHE_TYPES = ['f32', 'f16', 'q8_0', 'q4_0'] as const; const VALID_SPEC_TYPES = ['off', 'ngram-mod', 'draft-simple'] as const; export function registerInferenceSettingsRoutes(app: FastifyInstance): void { app.get('/api/settings/inference', async (_req, _res) => { return { ...DEFAULTS, ...load() }; }); app.patch<{ Body: Record }>('/api/settings/inference', async (req, reply) => { const current = { ...DEFAULTS, ...load() }; const merged = { ...current, ...req.body }; if (merged.cache_type_k && !(VALID_CACHE_TYPES as readonly string[]).includes(merged.cache_type_k as string)) { return reply.status(400).send({ error: 'Invalid cache_type_k' }); } if (merged.spec_type && !(VALID_SPEC_TYPES as readonly string[]).includes(merged.spec_type as string)) { return reply.status(400).send({ error: 'Invalid spec_type' }); } save(merged); return { ...DEFAULTS, ...load() }; }); }