91 lines
3.4 KiB
TypeScript
91 lines
3.4 KiB
TypeScript
/**
|
|
* Unit tests for SystemSpecs + HeuristicModelMemoryEstimator.
|
|
*
|
|
* Strategy:
|
|
* - HeuristicModelMemoryEstimator is pure — directly drive it with model ids.
|
|
* - NodeSystemSpecsProvider depends on `os.*` so we test:
|
|
* a) caching (same instance returned twice),
|
|
* b) shape (all required fields present, sane numbers).
|
|
* We don't pin platform-specific values since CI hardware varies.
|
|
*/
|
|
|
|
import {
|
|
NodeSystemSpecsProvider,
|
|
HeuristicModelMemoryEstimator,
|
|
} from '../src/system/specs';
|
|
|
|
describe('HeuristicModelMemoryEstimator', () => {
|
|
const est = new HeuristicModelMemoryEstimator();
|
|
|
|
test('extracts parameter count from "7B" suffix', () => {
|
|
// 7B q4 default: 7 * 0.6 + 1 = 5.2
|
|
expect(est.estimate('llama-3.2-7b-q4_K_M')).toBeCloseTo(5.2, 1);
|
|
});
|
|
|
|
test('extracts parameter count from "70B"', () => {
|
|
// 70B q4 default: 70 * 0.6 + 1 = 43
|
|
expect(est.estimate('llama-3-70b-instruct-q4_0')).toBeCloseTo(43, 0);
|
|
});
|
|
|
|
test('q8 quantization uses higher byte/param', () => {
|
|
// 7B q8: 7 * 1.0 + 1 = 8
|
|
expect(est.estimate('mistral-7b-q8_0')).toBeCloseTo(8, 1);
|
|
});
|
|
|
|
test('fp16 uses 2 bytes/param', () => {
|
|
// 7B fp16: 7 * 2.0 + 1 = 15
|
|
expect(est.estimate('mistral-7b-fp16')).toBeCloseTo(15, 1);
|
|
});
|
|
|
|
test('q5 sits between q4 and q6', () => {
|
|
const q4 = est.estimate('foo-7b-q4');
|
|
const q5 = est.estimate('foo-7b-q5');
|
|
const q6 = est.estimate('foo-7b-q6');
|
|
expect(q4).toBeLessThan(q5);
|
|
expect(q5).toBeLessThan(q6);
|
|
});
|
|
|
|
test('falls back to 7B when parameter count is absent', () => {
|
|
// unknown size → 7B q4 default → 5.2
|
|
expect(est.estimate('some-model-no-size')).toBeCloseTo(5.2, 1);
|
|
});
|
|
|
|
test('decimal parameter counts like "3.8b"', () => {
|
|
// 3.8B q4: 3.8 * 0.6 + 1 = 3.28
|
|
expect(est.estimate('phi-3.8b-q4')).toBeCloseTo(3.28, 1);
|
|
});
|
|
|
|
test('handles empty / undefined input gracefully', () => {
|
|
expect(est.estimate('')).toBeCloseTo(5.2, 1); // defaults
|
|
expect(est.estimate(undefined as any)).toBeCloseTo(5.2, 1);
|
|
});
|
|
});
|
|
|
|
describe('NodeSystemSpecsProvider', () => {
|
|
test('returns the same cached object on repeated calls', () => {
|
|
const provider = new NodeSystemSpecsProvider();
|
|
const a = provider.get();
|
|
const b = provider.get();
|
|
expect(a).toBe(b);
|
|
});
|
|
|
|
test('produces a sane shape', () => {
|
|
const specs = new NodeSystemSpecsProvider().get();
|
|
expect(specs.totalRamGB).toBeGreaterThan(0);
|
|
expect(specs.cpuCount).toBeGreaterThanOrEqual(1);
|
|
expect(specs.platform).toMatch(/^(darwin|linux|win32|freebsd|openbsd|sunos|aix)$/);
|
|
expect(specs.arch.length).toBeGreaterThan(0);
|
|
expect(typeof specs.isAppleSilicon).toBe('boolean');
|
|
expect(specs.safeModelBudgetGB).toBeGreaterThanOrEqual(2);
|
|
expect(specs.safeModelBudgetGB).toBeLessThanOrEqual(specs.totalRamGB);
|
|
expect(specs.summary).toMatch(/RAM/);
|
|
});
|
|
|
|
test('safe budget is at most ~65% of total RAM (Apple Silicon ceiling)', () => {
|
|
const specs = new NodeSystemSpecsProvider().get();
|
|
// Even on Apple Silicon (most generous ratio) the budget is capped at
|
|
// 0.65 of total. Use 0.7 as a soft upper bound for any platform.
|
|
expect(specs.safeModelBudgetGB).toBeLessThanOrEqual(specs.totalRamGB * 0.7 + 1);
|
|
});
|
|
});
|