Luigit
repositories / dotfiles

dotfiles

bugabingas dorkfiles

owned by admin

quickshell/nuguland/noko.test.js

Raw
const assert = require('assert');
const { execFileSync } = require('child_process');
const { mkdtempSync, readFileSync, writeFileSync } = require('fs');
const { join } = require('path');
const { tmpdir } = require('os');
const N = require('./noko.js');

// stereo averaging + clamp
assert.deepStrictEqual(Array.from(N.interleavedToMonoFloat32([1, -1, 0.5, 0.25], 2)), [0, 0.375]);
assert.deepStrictEqual(Array.from(N.interleavedToMonoFloat32([2, -2], 1)), [1, -1]);

// pcm16le -> mono float32
const pcm = Buffer.from([0x00, 0x80, 0xff, 0x7f]); // -32768, 32767
const mono = N.pcm16leToMonoFloat32(pcm, 1);
assert.strictEqual(mono[0], -1);
assert.ok(Math.abs(mono[1] - 32767 / 32768) < 1e-8);
assert.throws(() => N.pcm16leToMonoFloat32(Buffer.from([1]), 1), /even/);
const f32 = Buffer.alloc(8);
f32.writeFloatLE(-0.25, 0);
f32.writeFloatLE(0.75, 4);
assert.deepStrictEqual(Array.from(N.float32leToFloat32(f32, 1)), [-0.25, 0.75]);
assert.throws(() => N.float32leToFloat32(Buffer.from([0, 1]), 1), /divisible by 4/);

// resample 48 kHz -> 16 kHz reduces by 3x and preserves endpoints
const ramp = new Float32Array([0, 1, 2, 3, 4, 5]);
const down = N.resampleLinear(ramp, 48000, 16000);
assert.strictEqual(down.length, 2);
assert.strictEqual(down[0], 0);
assert.strictEqual(down[1], 5);

// exact 30 ms frames at 16 kHz
const samples = new Float32Array(480 * 3 + 12).fill(0.25);
const frames = N.splitFrames(samples, N.FRAME_SAMPLES);
assert.strictEqual(frames.length, 3);
assert.strictEqual(frames[0].length, 480);

// VAD smoothing: 2 speech frames trigger pre-roll flush, then 1 hangover noise frame kept.
const tinyFrames = [0, 1, 2, 3, 4].map(v => new Float32Array([v]));
const gated = N.vadGate(tinyFrames, [0, 0.4, 0.5, 0, 0], {
  threshold: 0.3,
  preRoll: 1,
  onset: 2,
  hangover: 1
});
assert.deepStrictEqual(gated.frameIndexes, [0, 1, 2, 3]);
assert.deepStrictEqual(Array.from(gated.samples), [0, 1, 2, 3]);

// A second speech burst after hangover must preserve typed pre-roll entries.
const twoBursts = N.vadGate([0, 1, 2, 3, 4, 5, 6, 7].map(v => new Float32Array([v])), [0, 0.4, 0.5, 0, 0, 0.4, 0.5, 0], {
  threshold: 0.3,
  preRoll: 1,
  onset: 2,
  hangover: 1
});
assert.deepStrictEqual(twoBursts.frameIndexes, [0, 1, 2, 3, 4, 5, 6, 7]);
assert.deepStrictEqual(Array.from(twoBursts.samples), [0, 1, 2, 3, 4, 5, 6, 7]);

// postprocess: trim, collapse spaces, remove junk tokens.
assert.strictEqual(N.postprocess('  hello   <unk>   world  ', ['<unk>']), 'hello world');

// model dir concept validation.
assert.deepStrictEqual(N.validateParakeetModelFiles([
  'nemo128.onnx',
  'encoder.int8.onnx',
  'decoder_joint.int8.onnx',
  'vocab.txt'
]), { ok: true, missing: [] });
assert.deepStrictEqual(N.validateParakeetModelFiles(['encoder.onnx']).missing, [
  'preprocessor',
  'decoder-or-joint',
  'vocabulary-or-tokenizer'
]);

// End-to-end core flow with injected VAD and Parakeet engine.
const input = new Float32Array(480 * 6).fill(0);
for (let i = 480 * 2; i < 480 * 4; i++) input[i] = 0.6;
const writes = [];
const state = new N.EngineState();
const result = N.transcribePipeline({ samples: input, sampleRate: 16000, channels: 1 }, {
  probability: (_frame, index) => index >= 2 && index <= 3 ? 0.9 : 0.01
}, {
  transcribe: (speech, ctx) => {
    assert.strictEqual(ctx.sampleRate, 16000);
    assert.ok(speech.length > 0);
    return { text: '  hello   noko  ', meta: { model: 'fake-parakeet', quantization: 'int8', runtimeProvider: 'fake-onnx' } };
  }
}, {
  write: record => writes.push(record)
}, { state: state, vad: { preRoll: 1, onset: 2, hangover: 1 } });

assert.strictEqual(result.text, 'hello noko');
assert.strictEqual(result.state, 'ready');
assert.strictEqual(state.state, 'ready');
assert.strictEqual(writes.length, 1);
assert.strictEqual(writes[0].transcript, 'hello noko');
assert.strictEqual(writes[0].model, 'fake-parakeet');
assert.ok(writes[0].duration > 0);

assert.throws(() => N.transcribePipeline({ samples: new Float32Array(480 * 2), sampleRate: 16000 }, {
  probability: () => 0
}, {
  transcribe: () => 'nope'
}), /empty speech buffer after vad/);

// CLI fixture proves the command path saves JSONL history after a full injected flow.
const dir = mkdtempSync(join(tmpdir(), 'noko-'));
const fixture = join(dir, 'fixture.json');
const history = join(dir, 'history.jsonl');
writeFileSync(fixture, JSON.stringify({
  samples: Array.from(input),
  sampleRate: 16000,
  channels: 1,
  probabilities: [0, 0, 0.9, 0.9, 0, 0],
  transcript: ' cli noko ',
  options: { vad: { preRoll: 1, onset: 2, hangover: 1 } }
}));
const cliOut = JSON.parse(execFileSync('node', ['noko.mjs', 'transcribe-json', fixture, history], { cwd: __dirname, encoding: 'utf8' }));
assert.strictEqual(cliOut.text, 'cli noko');
assert.strictEqual(JSON.parse(readFileSync(history, 'utf8').trim()).transcript, 'cli noko');
assert.throws(() => execFileSync('node', ['noko.mjs', 'transcribe-real-json', fixture, join(dir, 'real.jsonl')], { cwd: __dirname, encoding: 'utf8', stdio: 'pipe', env: { ...process.env, NOKO_DATA_DIR: join(dir, 'missing-data') } }), /vad model missing/);

const customDataDir = join(dir, 'noko-data');
const assets = JSON.parse(execFileSync('node', ['noko.mjs', 'assets-json'], { cwd: __dirname, encoding: 'utf8', env: { ...process.env, NOKO_DATA_DIR: customDataDir } }));
assert.strictEqual(assets.dataDir, customDataDir);
assert.ok(assets.vad.path.startsWith(customDataDir));
assert.ok(assets.parakeet.path.startsWith(customDataDir));
assert.ok(assets.runtime.path.startsWith(customDataDir));
assert.equal(assets.runtime.package, 'onnxruntime-node@1.27.0');
assert.match(assets.parakeet.files.encoder.url, /^https:\/\/huggingface\.co\//);
assert.doesNotMatch(JSON.stringify(assets), /com\.pais\.handy|Handy/);
for (const file of [assets.vad, ...Object.values(assets.parakeet.files)]) {
  require('fs').mkdirSync(require('path').dirname(file.path), { recursive: true });
  writeFileSync(file.path, 'x');
}
require('fs').mkdirSync(assets.runtime.path, { recursive: true });
const progressLines = execFileSync('node', ['noko.mjs', 'install-assets-progress'], { cwd: __dirname, encoding: 'utf8', env: { ...process.env, NOKO_DATA_DIR: customDataDir } }).trim().split('\n').map(JSON.parse);
assert.equal(progressLines.at(-1).event, 'done');
assert.equal(progressLines.at(-1).progress, 1);
const deps = JSON.parse(execFileSync('node', ['noko.mjs', 'system-deps-json'], { cwd: __dirname, encoding: 'utf8' }));
assert.ok(deps.dependencies.some(dep => dep.command === 'pw-record' && dep.required === true));
assert.ok(deps.dependencies.some(dep => dep.command === 'wtype' && dep.required === true));
assert.ok(Array.isArray(deps.missing));
assert.ok(deps.installCommand.length === 0 || deps.installCommand[0] === 'pkexec');
const doctor = JSON.parse(execFileSync('node', ['noko.mjs', 'doctor'], { cwd: __dirname, encoding: 'utf8', env: { ...process.env, NOKO_DATA_DIR: customDataDir } }));
assert.strictEqual(typeof doctor.capture.pwRecord, 'boolean');
assert.ok(Array.isArray(doctor.systemDependencies));
assert.ok(doctor.systemDependencies.some(dep => dep.command === 'pw-record' && dep.required === true));
assert.ok(doctor.systemDependencies.some(dep => dep.command === 'wtype' && dep.required === true));
assert.strictEqual(typeof doctor.missingSystemDependencies.length, 'number');
assert.strictEqual(typeof doctor.ok, 'boolean');
assert.strictEqual(doctor.vad.path, assets.vad.path);
assert.strictEqual(doctor.parakeet.path, assets.parakeet.path);
assert.doesNotMatch(JSON.stringify(doctor), /com\.pais\.handy|Handy/);

execFileSync('node', ['--input-type=module', '-e', `
import assert from 'node:assert/strict';
import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
import { createServer } from 'node:http';
import { mkdirSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
const dir = join(tmpdir(), 'noko-update-check-' + process.pid);
const assetDir = join(dir, 'assets');
mkdirSync(join(assetDir, 'vad'), { recursive: true });
mkdirSync(join(assetDir, 'parakeet-tdt-0.6b-v3-int8'), { recursive: true });
writeFileSync(join(assetDir, 'vad', 'silero_vad.onnx'), 'old');
for (const name of ['config.json', 'nemo128.onnx', 'encoder-model.int8.onnx', 'decoder_joint-model.int8.onnx', 'vocab.txt'])
  writeFileSync(join(assetDir, 'parakeet-tdt-0.6b-v3-int8', name), name === 'encoder-model.int8.onnx' ? 'old' : 'same');
const payload = {
  '/silero_vad.onnx': 'old',
  '/config.json': 'same',
  '/nemo128.onnx': 'same',
  '/encoder-model.int8.onnx': 'newer-remote',
  '/decoder_joint-model.int8.onnx': 'same',
  '/vocab.txt': 'same'
};
const server = createServer((req, res) => {
  const body = payload[req.url] || '';
  res.setHeader('content-length', Buffer.byteLength(body));
  res.setHeader('connection', 'close');
  res.end(req.method === 'HEAD' ? '' : body);
});
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
const base = 'http://127.0.0.1:' + server.address().port;
const env = { ...process.env, NOKO_DATA_DIR: assetDir, NOKO_VAD_URL: base + '/silero_vad.onnx', NOKO_PARAKEET_REPO: base };
const run = promisify(execFile);
const { stdout } = await run('node', ['noko.mjs', 'check-assets-json'], { cwd: process.cwd(), encoding: 'utf8', env });
const check = JSON.parse(stdout);
assert.equal(check.updateAvailable, true);
assert.equal(check.files.encoder.updateAvailable, true);
assert.equal(check.files.vad.updateAvailable, false);
server.closeAllConnections();
await new Promise(resolve => server.close(resolve));
`], { cwd: __dirname, encoding: 'utf8' });

// Real VAD bridge shape test: the public API accepts 480-sample frames, then
// prepares a Silero-compatible ONNX feed with context/state/sr when required.
execFileSync('node', ['--input-type=module', '-e', `
import assert from 'node:assert/strict';
import { OnnxVad, vadRuntimeReport } from './noko-vad.mjs';
class Tensor { constructor(type, data, dims) { this.type = type; this.data = data; this.dims = dims; } }
const calls = [];
const session = {
  inputNames: ['input', 'state', 'sr'],
  outputNames: ['output', 'stateN'],
  async run(feeds) {
    calls.push(feeds);
    assert.deepEqual(feeds.input.dims, [1, 576]);
    assert.deepEqual(feeds.state.dims, [2, 1, 128]);
    assert.deepEqual(feeds.sr.dims, []);
    return { output: new Tensor('float32', new Float32Array([0.7]), [1, 1]), stateN: new Tensor('float32', new Float32Array(256).fill(0.1), [2, 1, 128]) };
  }
};
const vad = new OnnxVad({ Tensor }, session);
assert.ok(Math.abs(await vad.probability(new Float32Array(480).fill(0.2)) - 0.7) < 1e-6);
assert.equal(calls.length, 1);
assert.throws(() => vad.feeds(new Float32Array(479)), /exactly 480/);
const report = await vadRuntimeReport('./noko.test.js', { ort: { Tensor }, session });
assert.equal(report.loadable, true);
assert.deepEqual(report.inputNames, ['input', 'state', 'sr']);
assert.deepEqual(report.outputNames, ['output', 'stateN']);
assert.equal(report.stateful, true);
assert.equal(report.srInput, true);
assert.ok(Math.abs(report.smokeProbability - 0.7) < 1e-6);
`], { cwd: __dirname, encoding: 'utf8' });

execFileSync('node', ['--input-type=module', '-e', `
import assert from 'node:assert/strict';
import { mkdirSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
import { decodeTokens, discoverParakeetDir, ParakeetOnnx } from './noko-parakeet.mjs';
const dir = join(tmpdir(), 'noko-parakeet-' + process.pid);
mkdirSync(join(dir, 'onnx_int8'), { recursive: true });
writeFileSync(join(dir, 'onnx_int8', '._encoder.int8.onnx'), '');
writeFileSync(join(dir, 'onnx_int8', 'nemo128.onnx'), '');
writeFileSync(join(dir, 'onnx_int8', 'encoder.int8.onnx'), '');
writeFileSync(join(dir, 'onnx_int8', 'decoder_joint.int8.onnx'), '');
writeFileSync(join(dir, 'onnx_int8', 'tokenizer.model'), '');
const found = discoverParakeetDir(dir);
assert.equal(found.ok, true);
assert.equal(found.files.preprocessor, 'onnx_int8/nemo128.onnx');
assert.equal(found.files.encoder, 'onnx_int8/encoder.int8.onnx');
assert.deepEqual(discoverParakeetDir(join(dir, 'missing')).missing, ['preprocessor', 'encoder', 'decoder-or-joint', 'vocabulary-or-tokenizer']);
await assert.rejects(() => ParakeetOnnx.create(join(dir, 'missing'), { ort: {} }), /model dir incomplete/);
assert.equal(decodeTokens({ decodeIds: ids => ids.join(',') }, [1, 2, 3]), '1,2,3');
assert.throws(() => decodeTokens(null, [1]), /tokenizer missing/);
const runtime = new ParakeetOnnx({}, {}, found, { size: 1, blankIdx: 0, decodeIds: () => '' });
await assert.rejects(() => runtime.transcribe(new Float32Array()), /empty speech buffer/);
runtime.busy = true;
await assert.rejects(() => runtime.transcribe(new Float32Array([0])), /already running/);
runtime.busy = false;
assert.equal(runtime.state, 'ready');
class Tensor { constructor(type, data, dims) { this.type = type; this.data = data; this.dims = dims; } }
const stateTensor = new Tensor('float32', new Float32Array(2 * 1 * 640), [2, 1, 640]);
const fake = new ParakeetOnnx({ Tensor }, {
  preprocessor: { async run() { return { features: new Tensor('float32', new Float32Array([0]), [1, 1, 1]), features_lens: new Tensor('int64', BigInt64Array.from([1n]), [1]) }; } },
  encoder: { async run() { return { outputs: new Tensor('float32', new Float32Array([0.1, 0.2]), [1, 2, 1]), encoded_lengths: new Tensor('int64', BigInt64Array.from([1n]), [1]) }; } },
  decoderJoint: {
    inputMetadata: [
      { name: 'input_states_1', shape: [2, 'batch', 640] },
      { name: 'input_states_2', shape: [2, 'batch', 640] }
    ],
    async run() { return { outputs: new Tensor('float32', new Float32Array([0.9, 0.1, 0, 1]), [1, 1, 1, 4]), output_states_1: stateTensor, output_states_2: stateTensor }; }
  }
}, { files: { encoder: 'encoder-model.int8.onnx' } }, { size: 2, blankIdx: 1, decodeIds: ids => ids.map(id => id === 0 ? 'hello' : '').join('') });
const fakeResult = await fake.transcribe(new Float32Array([0.4]));
assert.equal(fakeResult.text, 'hello');
assert.equal(fakeResult.meta.runtimeProvider, 'onnxruntime-node');
`], { cwd: __dirname, encoding: 'utf8' });

console.log('noko.test.js: ALL PASS');