From 68f2d758b1a9bfaffc5fa9e3e828dc718f833b53 Mon Sep 17 00:00:00 2001 From: Storme-bit Date: Sat, 18 Apr 2026 01:52:02 -0700 Subject: [PATCH] implementing model selector --- .../src/components/SettingsView.jsx | 2 +- .../src/config/settings.js | 7 +-- .../src/routes/models.js | 47 +++++++++++++++---- .../src/routes/settings.js | 13 +++++ 4 files changed, 57 insertions(+), 12 deletions(-) diff --git a/packages/chat-client/src/components/SettingsView.jsx b/packages/chat-client/src/components/SettingsView.jsx index 20ecd29..1207223 100644 --- a/packages/chat-client/src/components/SettingsView.jsx +++ b/packages/chat-client/src/components/SettingsView.jsx @@ -138,7 +138,7 @@ function NumberSetting({ label, description, value, min, max, step = 1, onSave, function SettingsRow({ label, description, action }) { return (
{ + const { modelsFolderPath } = appSettings.load(); + try { - const raw = fs.readFileSync(MODELS_PATH, 'utf8'); - const models = JSON.parse(raw); + // Try scanning folder for .gguf files + const files = fs.readdirSync(modelsFolderPath) + .filter(f => f.endsWith('.gguf')); + + // Try loading models.json for richer metadata (label, description) + let manifest = {}; + try { + const manifestPath = path.join(modelsFolderPath, 'models.json'); + const raw = fs.readFileSync(manifestPath, 'utf8'); + // Index manifest by filename for quick lookup + const list = JSON.parse(raw); + for (const m of list) { + manifest[m.value] = m; + } + } catch { + // No manifest — scan only, that's fine + } + + const models = files.map(filename => ({ + value: filename, + label: manifest[filename]?.label ?? filename.replace('.gguf', ''), + description: manifest[filename]?.description ?? null, + size: getFileSizeMB(path.join(modelsFolderPath, filename)), + })); + res.json(models); } catch (err) { - console.error('[models] Failed to read manifest:', err.message); - res.status(500).json({ error: 'Could not load models manifest' }); + console.error('[models] Failed to scan folder:', err.message); + res.status(500).json({ error: `Could not read models folder: ${modelsFolderPath}` }); } }); +function getFileSizeMB(filepath) { + try { + const bytes = fs.statSync(filepath).size; + return (bytes / (1024 ** 3)).toFixed(1) + ' GB'; // models are big — show GB + } catch { + return null; + } +} + module.exports = router; \ No newline at end of file diff --git a/packages/orchestration-service/src/routes/settings.js b/packages/orchestration-service/src/routes/settings.js index 8a7c977..4a5ba13 100644 --- a/packages/orchestration-service/src/routes/settings.js +++ b/packages/orchestration-service/src/routes/settings.js @@ -1,5 +1,6 @@ const { Router } = require('express'); const settings = require('../config/settings'); +const fs = require('fs'); const router = Router(); @@ -32,6 +33,18 @@ router.patch('/', (req, res) => { updates.scoreThreshold = val; } + if (req.body.modelsFolderPath !== undefined) { + const val = req.body.modelsFolderPath.trim(); + if (!val) return res.status(400).json({ error: 'modelsFolderPath cannot be empty' }); + // Verify the path exists and is readable + try { + fs.readdirSync(val); + } catch { + return res.status(400).json({ error: `Path not accessible: ${val}` }); + } + updates.modelsFolderPath = val; +} + res.json(settings.save(updates)); });