Ctrl K
ComunidadGuías…

__tests__/merge-one-model.test.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
const fs = require('fs');
const os = require('os');
const path = require('path');

/*
 * Averaging two adapters adds their matrices, which means something only when both were fitted to
 * the same network. Two students training side by side on two cards are not shares of one batch
 * and must never be treated as any: across models it is a shape error at load, after the hours
 * are spent and with both shares consumed.
 */
describe('a batch merges one model only', () => {
  let dir, training;
  const write = (rows) => {
    fs.mkdirSync(path.join(dir, 'training'), { recursive: true });
    fs.writeFileSync(path.join(dir, 'training', 'rounds.json'), JSON.stringify(rows));
  };
  const share = (id, batch, model, extra = {}) => ({
    id, batch, share: 2, merge: false, status: 'done', discarded: false,
    device: id, scope: { level: 'base', name: '', key: 'base' },
    recipe: { base: model }, adapterHub: `hub:${id}`,
    result: { agreement_pct: 30, collapse: { ratio: 1.2, tool: 'read' } },
    baseline: { agreement_pct: 20 },
    ...extra,
  });

  beforeEach(() => {
    dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gb-merge-'));
    process.env.PROFILE_DIR = dir;
    delete require.cache[require.resolve('../src/training')];
    training = require('../src/training');
  });
  afterEach(() => {
    delete process.env.PROFILE_DIR;
    delete require.cache[require.resolve('../src/training')];
    fs.rmSync(dir, { recursive: true, force: true });
  });

  it('two shares of one model are ready to merge', () => {
    write([
      share('r-1', 'b-1', 'Qwen/Qwen2.5-0.5B-Instruct'),
      share('r-2', 'b-1', 'Qwen/Qwen2.5-0.5B-Instruct'),
    ]);
    const out = training.batchReadyToMerge('r-1');
    expect(out).toBeTruthy();
    expect(out.abandoned).toBeFalsy();
  });

  it('two models in one batch are abandoned, with the reason', () => {
    write([
      share('r-1', 'b-1', 'Qwen/Qwen2.5-0.5B-Instruct'),
      share('r-2', 'b-1', 'Qwen/Qwen3-0.6B'),
    ]);
    const out = training.batchReadyToMerge('r-1');
    expect(out.abandoned).toBe(true);
    expect(out.why).toMatch(/cannot be averaged/);
    expect(out.why).toContain('Qwen/Qwen3-0.6B');
  });

  it('two rounds with no batch at all are simply two rounds', () => {
    write([
      { ...share('r-1', '', 'Qwen/Qwen2.5-0.5B-Instruct'), share: 1 },
      { ...share('r-2', '', 'Qwen/Qwen3-0.6B'), share: 1 },
    ]);
    /* No batch id: nothing to merge, and nothing claimed. */
    expect(training.batchReadyToMerge('r-1')).toBe(null);
    expect(training.batchReadyToMerge('r-2')).toBe(null);
  });
});