Ctrl K
ComunidadGuías…

__tests__/weakness.test.js

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

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

describe('the round trains what the model gets wrong', () => {
  const result = { agreement_pct: 34.55, per_tool: {
    read: { seen: 75, right: 46, pct: 61.3 },
    open: { seen: 75, right: 7, pct: 9.3 },
    finish: { seen: 22, right: 0, pct: 0 },
    dig: { seen: 42, right: 33, pct: 78.6 },
    rare: { seen: 2, right: 0, pct: 0 },
  } };

  it('weighs a failing tool up and a good one down, and says nothing about a tool it barely saw', () => {
    const w = weakness.fromExam(result);
    expect(w.finish).toBeCloseTo(3, 5);
    expect(w.open).toBeGreaterThan(2.7);
    expect(w.dig).toBeLessThan(1.5);
    expect(w.read).toBeGreaterThan(1);
    expect(w.rare).toBeUndefined();
    expect(weakness.fromExam(null)).toEqual({});
  });

  it('the shadow says the same about live jobs, and the worse of the two wins', () => {
    const shadow = { 'gb-base-1': { perTool: { read: { seen: 40, agree: 4 }, dig: { seen: 20, agree: 19 } } } };
    const live = weakness.fromShadow(shadow, 'gb-base-1');
    expect(live.read).toBeGreaterThan(2.7);
    const rounds = [{ id: 'r1', scope: { key: 'base' }, promoted: true, result }];
    const both = weakness.forScope({ rounds, shadow, model: 'gb-base-1', key: 'base' });
    expect(both.read).toBeGreaterThan(2.7);
    expect(both.finish).toBeCloseTo(3, 5);
    expect(both.dig).toBeLessThan(1.5);
  });

  it('a scope with no promoted round and no shadow weighs everything the same', () => {
    expect(weakness.forScope({ rounds: [], shadow: {}, key: 'platform:google' })).toEqual({});
  });

  it('a failing tool is handed over again when the round can hold more than the set has', () => {
    const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gb-weight-'));
    const prev = process.env.PROFILE_DIR; process.env.PROFILE_DIR = dir;
    delete require.cache[require.resolve('../src/slice')];
    delete require.cache[require.resolve('../src/learned')];
    const slice = require('../src/slice');
    try {
      const file = path.join(dir, 'train.jsonl');
      const rows = [];
      const line = (tool, n) => JSON.stringify({ messages: [{ role: 'system', content: 's' }, { role: 'user', content: `u${n}` }, { role: 'assistant', content: JSON.stringify({ tool, args: {} }) }], meta: { jobId: `j-${n}`, at: n % 5, tier: 'gold', role: 'general', platform: 'web', sighted: true } });
      let n = 0;
      for (let i = 0; i < 120; i++) rows.push(line('read', n++));
      for (let i = 0; i < 40; i++) rows.push(line('open', n++));
      fs.writeFileSync(file, rows.join('\n'));
      /* A round that can hold far more than the set has: everything is taken either way. */
      const plain = slice.draw({ file, builtAt: 'b1', want: 400, roundId: 'r-plain', scope: 'base' });
      expect(plain.count).toBe(160);
      expect(plain.tools.open).toBe(40);
      expect(plain.repeated).toBe(0);
      slice.release({ scope: 'base', marks: ['r-plain'] });
      const skewed = slice.draw({ file, builtAt: 'b1', want: 400, roundId: 'r-skew', scope: 'base', weights: { open: 3, read: 1 } });
      expect(skewed.fresh).toBe(160);
      expect(skewed.repeated).toBe(80);
      expect(skewed.tools.open).toBe(120);
      expect(skewed.tools.read).toBe(120);
      expect(skewed.count).toBe(240);
      /* The lines are the same turns, so nothing unlearned was spent twice. */
      const ids = new Set(skewed.jsonl.split('\n').map((l) => JSON.parse(l).meta.jobId));
      expect(ids.size).toBe(160);
    } finally {
      if (prev === undefined) delete process.env.PROFILE_DIR; else process.env.PROFILE_DIR = prev;
      delete require.cache[require.resolve('../src/slice')];
      fs.rmSync(dir, { recursive: true, force: true });
    }
  });
});

describe('a short answer is an answer', () => {
  const { shortReport, REPORT_MAX } = require('../src/traceset');
  const long = 'A'.repeat(900);

  it('keeps the first four hundred characters of a report and marks it', () => {
    const out = shortReport('finish', { summary: long, ok: true });
    expect(out.summary.length).toBeLessThan(long.length);
    expect(out.summary.startsWith('A'.repeat(REPORT_MAX))).toBe(true);
    expect(out.summary.endsWith(' …')).toBe(true);
    expect(out.ok).toBe(true);
    expect(shortReport('note', { text: long }).text.endsWith(' …')).toBe(true);
  });

  it('leaves a short report, another tool, and code alone', () => {
    expect(shortReport('finish', { summary: 'done' }).summary).toBe('done');
    expect(shortReport('run_script', { script: long }).script).toBe(long);
    expect(shortReport('type', { text: long }).text).toBe(long);
    expect(shortReport('open', { url: 'https://x/' })).toEqual({ url: 'https://x/' });
    expect(shortReport('finish', null)).toEqual({});
  });

  it('the answer it produces is still JSON a model can close', () => {
    const content = JSON.stringify({ tool: 'finish', args: shortReport('finish', { summary: long }) });
    expect(() => JSON.parse(content)).not.toThrow();
    expect(content.length).toBeLessThan(600);
  });
});

describe('learned means learned', () => {
  it('a turn whose tool the model still fails is drawn again; one it gets right is not', () => {
    const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gb-again-'));
    const prev = process.env.PROFILE_DIR; process.env.PROFILE_DIR = dir;
    delete require.cache[require.resolve('../src/slice')];
    delete require.cache[require.resolve('../src/learned')];
    const slice = require('../src/slice');
    const learned = require('../src/learned');
    try {
      const file = path.join(dir, 'train.jsonl');
      const line = (tool, n) => JSON.stringify({ messages: [{ role: 'system', content: 's' }, { role: 'user', content: `u${n}` }, { role: 'assistant', content: JSON.stringify({ tool, args: {} }) }], meta: { jobId: `j-${n}`, at: 0, tier: 'gold', role: 'general', platform: 'web', sighted: true } });
      const rows = [];
      let n = 0;
      for (let i = 0; i < 30; i++) rows.push(line('read', n++));
      for (let i = 0; i < 30; i++) rows.push(line('open', n++));
      fs.writeFileSync(file, rows.join('\n'));
      /* Everything is learned. */
      learned.record({ key: 'base', roundId: 'r-1', lines: rows });
      expect(slice.draw({ file, builtAt: 'b1', want: 100, roundId: 'r-2', scope: 'base' }).count).toBe(0);
      /* The model still fails `open`: those turns come back, `read` does not. */
      const again = slice.draw({ file, builtAt: 'b2', want: 100, roundId: 'r-3', scope: 'base', weights: { open: 3, read: 1.2 } });
      expect(again.tools.open).toBeGreaterThan(0);
      expect(again.tools.read).toBeUndefined();
    } finally {
      if (prev === undefined) delete process.env.PROFILE_DIR; else process.env.PROFILE_DIR = prev;
      delete require.cache[require.resolve('../src/slice')];
      fs.rmSync(dir, { recursive: true, force: true });
    }
  });
});