npu-forge / bin /menu.js
LJTSG's picture
v0.3: NPU-validated voice ear (111KB head + trainer + runtime), start.bat menu, 47.8 tok/s baseline, snag #8
7532a11 verified
Raw
History Blame Contribute Delete
2.96 kB
#!/usr/bin/env node
// menu.js β€” npu-forge front door (start.bat runs this).
'use strict';
const readline = require('readline');
const path = require('path');
const { spawnSync, spawn } = require('child_process');
const FORGE = path.join(__dirname, '..');
const ENV = { ...process.env, PYTHONIOENCODING: 'utf-8' };
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
const pending = [], waiting = [];
let closed = false;
rl.on('line', l => { const w = waiting.shift(); if (w) w(l); else pending.push(l); });
rl.on('close', () => { closed = true; while (waiting.length) waiting.shift()(null); });
const ask = q => { process.stdout.write(q); return new Promise(r => { const b = pending.shift(); if (b !== undefined) { console.log(b); r(b); } else if (closed) r(null); else waiting.push(r); }); };
function forge(args) {
rl.pause();
spawnSync('node', [path.join(FORGE, 'forge.js'), ...args], { stdio: 'inherit', env: ENV, shell: true });
rl.resume();
}
(async () => {
while (true) {
console.log('\n==============================================');
console.log(' NPU-FORGE β€” your models, on your NPU');
console.log('==============================================');
console.log(' 1. doctor β€” NPU / driver / registry health');
console.log(' 2. list β€” models + your forge customs');
console.log(' 3. register β€” activate customs (UAC, one click)');
console.log(' 4. serve <tag> β€” start an NPU server');
console.log(' t. tune β€” how to fine-tune your own (guide)');
console.log(' q. quit');
const pick = ((await ask('\nPick: ')) || '').trim().toLowerCase();
if (pick === null || pick === 'q' || pick === '') break;
if (pick === '1') forge(['doctor']);
else if (pick === '2') forge(['list']);
else if (pick === '3') {
spawn('cmd', ['/c', 'start', '', path.join(FORGE, 'register-admin.bat')], { detached: true });
console.log('(UAC window launched β€” click Yes; results land in register-cmd-log.txt)');
}
else if (pick.startsWith('4')) {
const tag = pick.split(/\s+/)[1] || (await ask('model tag (e.g. llama3.2:1b): ')) || '';
if (tag.trim()) { console.log('(Ctrl+C stops the server and returns here)'); forge(['serve', tag.trim()]); }
}
else if (pick === 't') {
console.log('\nFine-tune your own voice onto the NPU (needs a free modal.com account):');
console.log(' 1. Make chats.jsonl β€” one line per exchange:');
console.log(' {"messages":[{"role":"user","content":"..."},{"role":"assistant","content":"..."}]}');
console.log(' 2. node forge.js tune chats.jsonl --name myvoice');
console.log(' 3. node forge.js register (one UAC click)');
console.log(' 4. flm run myvoice-forge:1b β€” your voice, on your NPU.');
console.log(' (~3 minutes and ~$0.10 of cloud time for a 1B; see README.md)');
}
}
rl.close();
})();