Quality gates / quality (pull_request) Successful in 3m29s
A worker that answers /v1/models has not necessarily loaded its weights, so the app was offering an AI control that could only fail after the upload. Readiness now passes only once the pinned model is loaded, and the not-loaded state reaches manual Bristol entry in one tap. Verified against the real pinned llama.cpp build: it serves 503 while loading and the gate reports the distinct not-loaded state. Adds the reproducible container recipe (digest-pinned bases, pinned llama.cpp commit, hash-verified read-only weights, unprivileged, loopback-only publish), one shared post-load readiness gate used by the host bootstrap and the image healthcheck, and a target-GPU benchmark that refuses to write a receipt it did not measure. Refs #15
66 lines
3.1 KiB
JavaScript
66 lines
3.1 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { visionReadiness } from '../src/vision-readiness.js';
|
|
|
|
test('unsolved readiness means manual logging is always reachable in one action', () => {
|
|
for (const status of [null, undefined, { enabled: false }, { enabled: true, providerReady: false }]) {
|
|
const readiness = visionReadiness(status);
|
|
assert.equal(readiness.ready, false, JSON.stringify(status));
|
|
assert.equal(readiness.manualOnly, true, JSON.stringify(status));
|
|
assert.equal(typeof readiness.manualLabel, 'string');
|
|
assert.match(readiness.manualLabel, /manual|without AI/i);
|
|
}
|
|
});
|
|
|
|
test('reports distinct phases so the UI never claims a worker is ready when it is not', () => {
|
|
assert.equal(visionReadiness(null).phase, 'checking');
|
|
assert.equal(visionReadiness({ enabled: false }).phase, 'disabled');
|
|
assert.equal(visionReadiness({ enabled: true, providerReady: false }).phase, 'offline');
|
|
// The readiness contract widened in #15: answering the provider probe is no longer enough.
|
|
assert.equal(visionReadiness({ enabled: true, providerReady: true }).phase, 'loading');
|
|
assert.equal(visionReadiness({ enabled: true, providerReady: true, modelSeen: false }).phase, 'loading');
|
|
assert.equal(visionReadiness({ enabled: true, providerReady: true, modelSeen: true }).phase, 'ready');
|
|
});
|
|
|
|
test('readiness waits for the pinned model to load before promising AI', () => {
|
|
// Issue #15: a worker that answers /v1/models has not necessarily loaded the model yet.
|
|
// Readiness must pass only after the pinned model is loaded, or the user is offered an
|
|
// AI control that fails after the upload.
|
|
const notLoaded = visionReadiness({
|
|
enabled: true,
|
|
profile: 'selfhost',
|
|
providerReady: true,
|
|
modelSeen: false,
|
|
model: 'SmolVLM2-2.2B-Instruct',
|
|
});
|
|
assert.equal(notLoaded.phase, 'loading');
|
|
assert.equal(notLoaded.ready, false);
|
|
assert.equal(notLoaded.manualOnly, true);
|
|
assert.match(notLoaded.message, /model/i);
|
|
assert.match(notLoaded.manualLabel, /manual|without AI/i);
|
|
|
|
const unverified = visionReadiness({ enabled: true, profile: 'selfhost', providerReady: true });
|
|
assert.equal(unverified.ready, false, 'an unconfirmed model must never be treated as loaded');
|
|
});
|
|
|
|
test('a loaded pinned model keeps AI analysis available and does not force the manual path', () => {
|
|
const readiness = visionReadiness({ enabled: true, providerReady: true, modelSeen: true, model: 'SmolVLM2-2.2B-Instruct' });
|
|
assert.equal(readiness.ready, true);
|
|
assert.equal(readiness.manualOnly, false);
|
|
assert.equal(readiness.manualLabel, '');
|
|
});
|
|
|
|
test('readiness never promises a symptom or diagnosis capability', () => {
|
|
for (const status of [
|
|
null,
|
|
{ enabled: false },
|
|
{ enabled: true, providerReady: false },
|
|
{ enabled: true, providerReady: true, modelSeen: true },
|
|
{ enabled: true, providerReady: true, modelSeen: false },
|
|
]) {
|
|
const readiness = visionReadiness(status);
|
|
const copy = `${readiness.manualLabel} ${readiness.message}`;
|
|
assert.doesNotMatch(copy, /diagnos|bleeding|cancer|symptom/i);
|
|
}
|
|
});
|