harness-everything 0.29.0 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/AGENTS.md +1 -0
- package/CHANGELOG.md +35 -0
- package/PRIVACY.md +9 -0
- package/benchmarks/fixtures/system-one-intent-holdout-draft.json +2228 -0
- package/benchmarks/fixtures/system-one-intent-holdout.json +16509 -0
- package/benchmarks/fixtures/system-one-intent-reviews.json +2723 -0
- package/benchmarks/results/system-one/intent-ngram-v1-2026-09-24/README.md +64 -0
- package/benchmarks/results/system-one/intent-ngram-v1-2026-09-24/report.json +15177 -0
- package/benchmarks/results/system-one/labeler-holdout-check-intent-2026-09-24/report.json +2976 -0
- package/benchmarks/results/system-one/labeler-holdout-check-intent-scores-haiku-2026-09-24/report-005-steps.json +6125 -0
- package/benchmarks/results/system-one/labeler-holdout-check-intent-scores-haiku-2026-09-24/report-haiku-sonnet-escalation.json +6344 -0
- package/benchmarks/results/system-one/labeler-holdout-check-intent-scores-haiku-2026-09-24/report-scaled-margin-escalation.json +6206 -0
- package/benchmarks/results/system-one/labeler-holdout-check-intent-scores-haiku-2026-09-24/report.json +6103 -0
- package/benchmarks/results/system-one/laya-ft-2026-09-25/README.md +83 -0
- package/benchmarks/results/system-one/laya-ft-2026-09-25/report.json +71 -0
- package/benchmarks/results/system-one/laya-ft-2026-09-25/shadow-report.json +44 -0
- package/benchmarks/results/system-one/laya-zs-2026-09-25/README.md +68 -0
- package/benchmarks/results/system-one/laya-zs-2026-09-25/report.json +173 -0
- package/ci/mechanism-34-observations.test.js +550 -0
- package/ci/mechanism-system-one-collect.test.js +16 -0
- package/ci/mechanism-system-one-intent.test.js +406 -0
- package/ci/mechanism-system-one-laya.test.js +45 -0
- package/ci/mechanism-system-one-relevance.test.js +112 -0
- package/ci/mechanism-system-one-resident.test.js +16 -0
- package/ci/system-one-laya-calibrate-test.py +65 -0
- package/ci/system-one-laya-export-test.py +155 -0
- package/ci/system-one-laya-label-test.py +201 -0
- package/ci/system-one-laya-perintent-test.py +60 -0
- package/ci/system-one-laya-provider-test.py +115 -0
- package/ci/system-one-laya-train-test.py +93 -0
- package/docs/routing.md +25 -0
- package/docs/system-one-intent-gates.md +102 -0
- package/docs/system-one-intent.md +173 -0
- package/docs/system-one-laya-training.md +38 -0
- package/docs/system-one-observations.md +206 -0
- package/docs/system-one-routing.md +25 -10
- package/docs/system-one-suggestion-gates.md +292 -0
- package/docs/system-one-training.md +56 -6
- package/harness-everything/SKILL.md +1 -1
- package/harness-everything/scripts/kernel-router.js +10 -0
- package/harness-everything/scripts/system-one/catalogs.js +35 -0
- package/harness-everything/scripts/system-one/contract.js +45 -1
- package/harness-everything/scripts/system-one/evaluate.js +115 -19
- package/harness-everything/scripts/system-one/intent.js +22 -0
- package/harness-everything/scripts/system-one/laya_adapter.py +141 -0
- package/harness-everything/scripts/system-one/provider.js +10 -3
- package/harness-everything/scripts/system-one/resident.js +8 -3
- package/harness-everything/scripts/system-one/router.js +1 -1
- package/hooks/hooks.json +40 -0
- package/hooks/scripts/codex-user-prompt.js +39 -0
- package/hooks/scripts/lib/observations.js +581 -0
- package/hooks/scripts/observation-hook.js +25 -0
- package/opencode-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
- package/plugins/harness-everything/hooks/hooks.json +35 -2
- package/plugins/harness-everything/hooks/scripts/codex-user-prompt.js +39 -0
- package/plugins/harness-everything/hooks/scripts/lib/observations.js +581 -0
- package/plugins/harness-everything/hooks/scripts/observation-hook.js +25 -0
- package/plugins/harness-everything/plugin.json +1 -1
- package/plugins/harness-everything/skills/harness-everything/SKILL.md +1 -1
- package/plugins/harness-everything/skills/harness-everything/scripts/kernel-router.js +10 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/system-one/catalogs.js +35 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/system-one/contract.js +45 -1
- package/plugins/harness-everything/skills/harness-everything/scripts/system-one/evaluate.js +115 -19
- package/plugins/harness-everything/skills/harness-everything/scripts/system-one/intent.js +22 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/system-one/laya_adapter.py +141 -0
- package/plugins/harness-everything/skills/harness-everything/scripts/system-one/provider.js +10 -3
- package/plugins/harness-everything/skills/harness-everything/scripts/system-one/resident.js +8 -3
- package/plugins/harness-everything/skills/harness-everything/scripts/system-one/router.js +1 -1
- package/sbom.cdx.json +7 -7
- package/scripts/evaluate-system-one.js +17 -5
- package/scripts/system-one-calibrate-laya.py +165 -0
- package/scripts/system-one-calibrate.js +42 -13
- package/scripts/system-one-collect.js +7 -3
- package/scripts/system-one-compare-laya.py +156 -0
- package/scripts/system-one-corpus.js +22 -15
- package/scripts/system-one-eval-laya.js +112 -0
- package/scripts/system-one-export-laya.py +197 -0
- package/scripts/system-one-label-laya.py +243 -0
- package/scripts/system-one-label.js +163 -37
- package/scripts/system-one-observations-export.js +106 -0
- package/scripts/system-one-observations-review.js +86 -0
- package/scripts/system-one-perintent-laya.py +134 -0
- package/scripts/system-one-train-laya.py +344 -0
- package/scripts/system-one-train-ngram.py +66 -14
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Stdlib tests for the Laya zero-shot calibration (#255 Phase 3 prep).
|
|
2
|
+
|
|
3
|
+
No model dependency. Run with:
|
|
4
|
+
python3 ci/system-one-laya-calibrate-test.py
|
|
5
|
+
"""
|
|
6
|
+
import importlib.util
|
|
7
|
+
import sys
|
|
8
|
+
import unittest
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
sys.dont_write_bytecode = True # never leave __pycache__ in the source tree that packaging copies
|
|
12
|
+
path = Path(__file__).resolve().parents[1] / 'scripts/system-one-calibrate-laya.py'
|
|
13
|
+
spec = importlib.util.spec_from_file_location('laya_calibrate', path)
|
|
14
|
+
laya_calibrate = importlib.util.module_from_spec(spec)
|
|
15
|
+
spec.loader.exec_module(laya_calibrate)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class IsotonicTests(unittest.TestCase):
|
|
19
|
+
def test_exact_staircase_on_separable_data(self):
|
|
20
|
+
knots = laya_calibrate.isotonic_fit([0.1, 0.2, 0.8, 0.9], [0.0, 0.0, 1.0, 1.0])
|
|
21
|
+
self.assertEqual([laya_calibrate.isotonic_predict(knots, x) for x in (0.1, 0.2, 0.8, 0.9)],
|
|
22
|
+
[0.0, 0.0, 1.0, 1.0])
|
|
23
|
+
|
|
24
|
+
def test_predictions_are_monotone(self):
|
|
25
|
+
xs = [0.05, 0.4, 0.35, 0.7, 0.6, 0.9, 0.2, 0.5]
|
|
26
|
+
ys = [0.0, 1.0, 0.0, 0.6, 1.0, 0.8, 0.2, 0.4]
|
|
27
|
+
knots = laya_calibrate.isotonic_fit(xs, ys)
|
|
28
|
+
grid = [i / 20 for i in range(21)]
|
|
29
|
+
preds = [laya_calibrate.isotonic_predict(knots, x) for x in grid]
|
|
30
|
+
self.assertTrue(all(b >= a for a, b in zip(preds, preds[1:])), 'monotone non-decreasing')
|
|
31
|
+
|
|
32
|
+
def test_duplicate_x_averages_y(self):
|
|
33
|
+
knots = laya_calibrate.isotonic_fit([0.5, 0.5, 0.5], [0.0, 0.6, 0.3])
|
|
34
|
+
self.assertAlmostEqual(laya_calibrate.isotonic_predict(knots, 0.5), 0.3)
|
|
35
|
+
|
|
36
|
+
def test_clamps_outside_range(self):
|
|
37
|
+
knots = laya_calibrate.isotonic_fit([0.2, 0.8], [0.3, 0.9])
|
|
38
|
+
self.assertEqual(laya_calibrate.isotonic_predict(knots, 0.0), 0.3)
|
|
39
|
+
self.assertEqual(laya_calibrate.isotonic_predict(knots, 1.0), 0.9)
|
|
40
|
+
|
|
41
|
+
def test_knots_are_json_serializable(self):
|
|
42
|
+
import json
|
|
43
|
+
knots = laya_calibrate.isotonic_fit([0.1, 0.9, 0.4], [0.2, 0.8, 0.5])
|
|
44
|
+
self.assertEqual(json.loads(json.dumps(knots)), knots)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class DeriveCalibratedTests(unittest.TestCase):
|
|
48
|
+
def scores(self, **over):
|
|
49
|
+
base = {i: 0.0 for i in
|
|
50
|
+
['explain', 'discuss', 'git', 'fix', 'edit', 'feature', 'refactor',
|
|
51
|
+
'review', 'test', 'docs', 'plan', 'investigate']}
|
|
52
|
+
base.update(over)
|
|
53
|
+
return base
|
|
54
|
+
|
|
55
|
+
def test_null_cutoff_abstains_low_max(self):
|
|
56
|
+
out = laya_calibrate.derive_calibrated(self.scores(fix=0.5, test=0.3), {}, 0.6)
|
|
57
|
+
self.assertIsNone(out['gold'])
|
|
58
|
+
|
|
59
|
+
def test_identity_mapping_keeps_bands(self):
|
|
60
|
+
out = laya_calibrate.derive_calibrated(self.scores(fix=0.9, test=0.5), {}, 0.2)
|
|
61
|
+
self.assertEqual((out['gold'], out['secondary']), ('fix', ['test']))
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
if __name__ == '__main__':
|
|
65
|
+
unittest.main()
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"""Stdlib contract tests for the Laya soft-target exporter (#255 Phase 3).
|
|
2
|
+
|
|
3
|
+
The exporter converts teacher relevance scores to upstream RLCD training
|
|
4
|
+
items without loading any model: sequence building is injected. Run with:
|
|
5
|
+
python3 ci/system-one-laya-export-test.py
|
|
6
|
+
"""
|
|
7
|
+
import importlib.util
|
|
8
|
+
import json
|
|
9
|
+
import sys
|
|
10
|
+
import tempfile
|
|
11
|
+
import unittest
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
sys.dont_write_bytecode = True # never leave __pycache__ in the source tree that packaging copies
|
|
15
|
+
path = Path(__file__).resolve().parents[1] / 'scripts/system-one-export-laya.py'
|
|
16
|
+
spec = importlib.util.spec_from_file_location('laya_exporter', path)
|
|
17
|
+
laya_exporter = importlib.util.module_from_spec(spec)
|
|
18
|
+
spec.loader.exec_module(laya_exporter)
|
|
19
|
+
|
|
20
|
+
INTENTS = ['explain', 'discuss', 'git', 'fix', 'edit', 'feature', 'refactor',
|
|
21
|
+
'review', 'test', 'docs', 'plan', 'investigate']
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def fake_builder(state, instructions, probs):
|
|
25
|
+
"""Deterministic stub: token ids derive from the text, markers fixed."""
|
|
26
|
+
ids = [len(state) % 251 + 1, len(instructions) % 251 + 1, 7]
|
|
27
|
+
return {'ids': ids, 'markers': [0, 1], 'target': [probs['false'], probs['true']]}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ItemTests(unittest.TestCase):
|
|
31
|
+
def test_noul_target_is_false_true_pair(self):
|
|
32
|
+
item = laya_exporter.build_item('fix it', 'repair?', {'true': 0.8, 'false': 0.2}, fake_builder)
|
|
33
|
+
self.assertEqual(item['qtype'], 2, 'noul qtype follows laya.common.QTYPES')
|
|
34
|
+
self.assertEqual(item['target'], [0.2, 0.8])
|
|
35
|
+
self.assertEqual(item['label'], 1)
|
|
36
|
+
|
|
37
|
+
def test_relevance_p_maps_to_true_p(self):
|
|
38
|
+
item = laya_exporter.build_item('x', 'y', {'true': 0.0, 'false': 1.0}, fake_builder)
|
|
39
|
+
self.assertEqual((item['target'], item['label']), ([1.0, 0.0], 0))
|
|
40
|
+
|
|
41
|
+
def test_target_distribution_is_conserved(self):
|
|
42
|
+
for p in (0.0, 0.25, 0.6, 1.0):
|
|
43
|
+
item = laya_exporter.build_item('x', 'y', {'true': p, 'false': 1 - p}, fake_builder)
|
|
44
|
+
self.assertAlmostEqual(sum(item['target']), 1.0, msg=f'p={p}')
|
|
45
|
+
|
|
46
|
+
def test_bad_probabilities_fail(self):
|
|
47
|
+
with self.assertRaisesRegex(ValueError, 'probabilities'):
|
|
48
|
+
laya_exporter.build_item('x', 'y', {'true': 0.8, 'false': 0.8}, fake_builder)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class ExportTests(unittest.TestCase):
|
|
52
|
+
def write_data(self, data, prompts, teacher):
|
|
53
|
+
(data / 'prompts.jsonl').write_text(
|
|
54
|
+
'\n'.join(json.dumps(p) for p in prompts) + '\n', encoding='utf-8')
|
|
55
|
+
(data / 'labels-intent-scores.jsonl').write_text(
|
|
56
|
+
'\n'.join(json.dumps(t) for t in teacher) + '\n', encoding='utf-8')
|
|
57
|
+
|
|
58
|
+
def teacher_row(self, id, scores):
|
|
59
|
+
return {'id': id, 'gold': 'fix', 'secondary': [], 'scores': scores}
|
|
60
|
+
|
|
61
|
+
def test_exports_train_only_in_id_order(self):
|
|
62
|
+
prompts = [{'id': 'b', 'split': 'train', 'text': 'commit'},
|
|
63
|
+
{'id': 'a', 'split': 'train', 'text': 'fix it'},
|
|
64
|
+
{'id': 'c', 'split': 'validation', 'text': 'skip me'}]
|
|
65
|
+
scores = {i: (0.9 if i == 'fix' else 0.0) for i in INTENTS}
|
|
66
|
+
with tempfile.TemporaryDirectory() as d:
|
|
67
|
+
data = Path(d) / 'data'
|
|
68
|
+
data.mkdir()
|
|
69
|
+
self.write_data(data, prompts, [self.teacher_row('a', scores), self.teacher_row('b', scores)])
|
|
70
|
+
out = Path(d) / 'train_items.json'
|
|
71
|
+
manifest = Path(d) / 'manifest.json'
|
|
72
|
+
summary = laya_exporter.export(data, 'train', out, manifest, fake_builder, seed=0)
|
|
73
|
+
self.assertEqual(summary['items'], 24, '2 prompts x 12 intents')
|
|
74
|
+
rows = [json.loads(line) for line in out.read_text(encoding='utf-8').strip().split('\n')]
|
|
75
|
+
self.assertEqual([r['promptId'] for r in rows[::12]], ['a', 'b'], 'deterministic id order')
|
|
76
|
+
self.assertTrue(all(set(r) >= {'promptId', 'intent', 'ids', 'markers', 'qtype', 'target', 'label'} for r in rows))
|
|
77
|
+
self.assertEqual(summary['manifest']['seed'], 0)
|
|
78
|
+
|
|
79
|
+
def test_seed_controls_order(self):
|
|
80
|
+
prompts = [{'id': c, 'split': 'train', 'text': c * 20} for c in 'abcd']
|
|
81
|
+
scores = {i: 0.1 for i in INTENTS}
|
|
82
|
+
with tempfile.TemporaryDirectory() as d:
|
|
83
|
+
data = Path(d) / 'data'
|
|
84
|
+
data.mkdir()
|
|
85
|
+
self.write_data(data, prompts, [self.teacher_row(p['id'], scores) for p in prompts])
|
|
86
|
+
outs = []
|
|
87
|
+
for seed in (11, 11, 12):
|
|
88
|
+
out = Path(d) / f'o{seed}.json'
|
|
89
|
+
laya_exporter.export(data, 'train', out, Path(d) / f'm{seed}.json', fake_builder, seed=seed)
|
|
90
|
+
outs.append([json.loads(line)['promptId'] for line in out.read_text(encoding='utf-8').strip().split('\n')][::12])
|
|
91
|
+
self.assertEqual(outs[0], outs[1], 'same seed replays the order')
|
|
92
|
+
self.assertNotEqual(outs[0], outs[2], 'a different seed shuffles')
|
|
93
|
+
|
|
94
|
+
def test_overlap_with_forbidden_ids_fails(self):
|
|
95
|
+
prompts = [{'id': 'a', 'split': 'train', 'text': 'fix it'}]
|
|
96
|
+
scores = {i: 0.1 for i in INTENTS}
|
|
97
|
+
with tempfile.TemporaryDirectory() as d:
|
|
98
|
+
data = Path(d) / 'data'
|
|
99
|
+
data.mkdir()
|
|
100
|
+
self.write_data(data, prompts, [self.teacher_row('a', scores)])
|
|
101
|
+
forbid = Path(d) / 'forbid.json'
|
|
102
|
+
forbid.write_text(json.dumps(['a']), encoding='utf-8')
|
|
103
|
+
with self.assertRaisesRegex(ValueError, 'overlap'):
|
|
104
|
+
laya_exporter.export(data, 'train', Path(d) / 'o.json', Path(d) / 'm.json',
|
|
105
|
+
fake_builder, seed=7, forbid_path=forbid)
|
|
106
|
+
|
|
107
|
+
def test_missing_teacher_row_is_skipped_and_counted(self):
|
|
108
|
+
prompts = [{'id': 'a', 'split': 'train', 'text': 'fix it'},
|
|
109
|
+
{'id': 'b', 'split': 'train', 'text': 'no teacher'}]
|
|
110
|
+
scores = {i: 0.1 for i in INTENTS}
|
|
111
|
+
with tempfile.TemporaryDirectory() as d:
|
|
112
|
+
data = Path(d) / 'data'
|
|
113
|
+
data.mkdir()
|
|
114
|
+
self.write_data(data, prompts, [self.teacher_row('a', scores)])
|
|
115
|
+
out = Path(d) / 'o.json'
|
|
116
|
+
summary = laya_exporter.export(data, 'train', out, Path(d) / 'm.json', fake_builder, seed=7)
|
|
117
|
+
self.assertEqual((summary['items'], summary['skippedNoTeacher']), (12, 1))
|
|
118
|
+
|
|
119
|
+
def test_oversample_repeats_only_selected_positive_intent(self):
|
|
120
|
+
prompts = [{'id': 'a', 'split': 'train', 'text': 'restructure'},
|
|
121
|
+
{'id': 'b', 'split': 'train', 'text': 'fix it'}]
|
|
122
|
+
scores_a = {i: (0.9 if i == 'refactor' else 0.0) for i in INTENTS}
|
|
123
|
+
scores_b = {i: (0.9 if i == 'fix' else 0.0) for i in INTENTS}
|
|
124
|
+
with tempfile.TemporaryDirectory() as d:
|
|
125
|
+
data = Path(d) / 'data'
|
|
126
|
+
data.mkdir()
|
|
127
|
+
self.write_data(data, prompts, [self.teacher_row('a', scores_a), self.teacher_row('b', scores_b)])
|
|
128
|
+
out = Path(d) / 'o.json'
|
|
129
|
+
summary = laya_exporter.export(data, 'train', out, Path(d) / 'm.json', fake_builder,
|
|
130
|
+
seed=0, oversample={'refactor': 3})
|
|
131
|
+
rows = [json.loads(line) for line in out.read_text(encoding='utf-8').strip().split('\n')]
|
|
132
|
+
keys = [(r['promptId'], r['intent']) for r in rows]
|
|
133
|
+
self.assertEqual(summary['items'], 26, '24 base items plus two refactor repeats')
|
|
134
|
+
self.assertEqual(keys.count(('a', 'refactor')), 3)
|
|
135
|
+
self.assertTrue(all(keys.count(('a', intent)) == 1 for intent in INTENTS if intent != 'refactor'),
|
|
136
|
+
'sibling intents must not be reweighted')
|
|
137
|
+
self.assertTrue(all(keys.count(('b', intent)) == 1 for intent in INTENTS),
|
|
138
|
+
'non-positive prompt must remain at base multiplicity')
|
|
139
|
+
self.assertEqual(summary['manifest']['oversample'], {'refactor': 3})
|
|
140
|
+
|
|
141
|
+
def test_bad_oversample_spec_fails(self):
|
|
142
|
+
with tempfile.TemporaryDirectory() as d:
|
|
143
|
+
data = Path(d) / 'data'
|
|
144
|
+
data.mkdir()
|
|
145
|
+
self.write_data(data, [], [])
|
|
146
|
+
with self.assertRaisesRegex(ValueError, 'oversample'):
|
|
147
|
+
laya_exporter.export(data, 'train', Path(d) / 'o.json', Path(d) / 'm.json',
|
|
148
|
+
fake_builder, oversample={'nope': 2})
|
|
149
|
+
with self.assertRaisesRegex(ValueError, 'oversample'):
|
|
150
|
+
laya_exporter.export(data, 'train', Path(d) / 'o.json', Path(d) / 'm.json',
|
|
151
|
+
fake_builder, oversample={'fix': 1})
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
if __name__ == '__main__':
|
|
155
|
+
unittest.main()
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"""Stdlib contract tests for the Laya dense intent labeler (#255 Phase 1+2).
|
|
2
|
+
|
|
3
|
+
No model dependency: backends are injected as stubs. Run with:
|
|
4
|
+
python3 ci/system-one-laya-label-test.py
|
|
5
|
+
"""
|
|
6
|
+
import importlib.util
|
|
7
|
+
import json
|
|
8
|
+
import sys
|
|
9
|
+
import tempfile
|
|
10
|
+
import unittest
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
sys.dont_write_bytecode = True # never leave __pycache__ in the source tree that packaging copies
|
|
14
|
+
path = Path(__file__).resolve().parents[1] / 'scripts/system-one-label-laya.py'
|
|
15
|
+
spec = importlib.util.spec_from_file_location('laya_labeler', path)
|
|
16
|
+
laya_labeler = importlib.util.module_from_spec(spec)
|
|
17
|
+
spec.loader.exec_module(laya_labeler)
|
|
18
|
+
|
|
19
|
+
INTENTS = ['explain', 'discuss', 'git', 'fix', 'edit', 'feature', 'refactor',
|
|
20
|
+
'review', 'test', 'docs', 'plan', 'investigate']
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class MappingTests(unittest.TestCase):
|
|
24
|
+
def test_intent_order_matches_owner_catalog(self):
|
|
25
|
+
# catalogs.js INTENT_OPTIONS order is the scorer output order; it must not drift.
|
|
26
|
+
self.assertEqual(laya_labeler.INTENTS, INTENTS)
|
|
27
|
+
|
|
28
|
+
def test_every_intent_has_one_noul_question(self):
|
|
29
|
+
questions = laya_labeler.build_questions()
|
|
30
|
+
self.assertEqual(list(questions), INTENTS, 'question order follows the catalog order')
|
|
31
|
+
for intent, q in questions.items():
|
|
32
|
+
self.assertEqual(q['type'], 'noul')
|
|
33
|
+
self.assertTrue(q['instructions'], intent)
|
|
34
|
+
|
|
35
|
+
def test_question_order_change_keeps_semantic_mapping(self):
|
|
36
|
+
a = laya_labeler.build_questions()
|
|
37
|
+
b = laya_labeler.build_questions()
|
|
38
|
+
self.assertEqual(a, b)
|
|
39
|
+
self.assertEqual(list(a), INTENTS)
|
|
40
|
+
|
|
41
|
+
def test_mapping_version_is_pinned(self):
|
|
42
|
+
self.assertRegex(laya_labeler.QUESTIONS_VERSION, r'^v\d+$')
|
|
43
|
+
|
|
44
|
+
def test_v2_keeps_intents_and_order_but_sharpens_text(self):
|
|
45
|
+
v1 = laya_labeler.build_questions('v1')
|
|
46
|
+
v2 = laya_labeler.build_questions('v2')
|
|
47
|
+
self.assertEqual(list(v2), INTENTS)
|
|
48
|
+
for intent, q in v2.items():
|
|
49
|
+
self.assertEqual(q['type'], 'noul')
|
|
50
|
+
self.assertTrue(q['instructions'], intent)
|
|
51
|
+
self.assertTrue(any(v2[i]['instructions'] != v1[i]['instructions'] for i in INTENTS),
|
|
52
|
+
'v2 rewords at least one intent')
|
|
53
|
+
self.assertEqual(v2['git']['instructions'], v1['git']['instructions'],
|
|
54
|
+
'unconfused intents keep v1 text')
|
|
55
|
+
|
|
56
|
+
def test_unknown_questions_version_fails(self):
|
|
57
|
+
with self.assertRaisesRegex(ValueError, 'unknown questions version'):
|
|
58
|
+
laya_labeler.build_questions('v9')
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class ValidatorTests(unittest.TestCase):
|
|
62
|
+
def scores(self, **over):
|
|
63
|
+
base = {i: 0.0 for i in INTENTS}
|
|
64
|
+
base.update(over)
|
|
65
|
+
return base
|
|
66
|
+
|
|
67
|
+
def test_valid_scores_pass(self):
|
|
68
|
+
self.assertTrue(laya_labeler.validate_scores(self.scores(fix=0.9, test=0.4)))
|
|
69
|
+
|
|
70
|
+
def test_missing_or_unknown_key_fails(self):
|
|
71
|
+
bad = self.scores(fix=0.9)
|
|
72
|
+
del bad['fix']
|
|
73
|
+
with self.assertRaisesRegex(ValueError, 'keys'):
|
|
74
|
+
laya_labeler.validate_scores(bad)
|
|
75
|
+
bad2 = self.scores(fix=0.9)
|
|
76
|
+
bad2['nope'] = 0.1
|
|
77
|
+
with self.assertRaisesRegex(ValueError, 'keys'):
|
|
78
|
+
laya_labeler.validate_scores(bad2)
|
|
79
|
+
|
|
80
|
+
def test_out_of_range_or_nan_fails(self):
|
|
81
|
+
for v in (-0.1, 1.1, float('nan')):
|
|
82
|
+
with self.assertRaisesRegex(ValueError, 'range', msg=f'value={v}'):
|
|
83
|
+
laya_labeler.validate_scores(self.scores(fix=v))
|
|
84
|
+
|
|
85
|
+
def test_non_float_fails(self):
|
|
86
|
+
with self.assertRaisesRegex(ValueError, 'range'):
|
|
87
|
+
laya_labeler.validate_scores(self.scores(fix='0.9'))
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class DeriveTests(unittest.TestCase):
|
|
91
|
+
def scores(self, **over):
|
|
92
|
+
base = {i: 0.0 for i in INTENTS}
|
|
93
|
+
base.update(over)
|
|
94
|
+
return base
|
|
95
|
+
|
|
96
|
+
def test_primary_and_secondary_from_bands(self):
|
|
97
|
+
out = laya_labeler.derive_label(self.scores(fix=0.9, test=0.5, docs=0.3))
|
|
98
|
+
self.assertEqual(out['gold'], 'fix')
|
|
99
|
+
self.assertEqual(out['secondary'], ['test'])
|
|
100
|
+
|
|
101
|
+
def test_low_max_abstains(self):
|
|
102
|
+
out = laya_labeler.derive_label(self.scores(explain=0.2))
|
|
103
|
+
self.assertIsNone(out['gold'])
|
|
104
|
+
self.assertEqual(out['secondary'], [])
|
|
105
|
+
|
|
106
|
+
def test_tie_breaks_by_catalog_order(self):
|
|
107
|
+
out = laya_labeler.derive_label(self.scores(fix=0.7, test=0.7))
|
|
108
|
+
self.assertEqual(out['gold'], 'fix')
|
|
109
|
+
|
|
110
|
+
def test_derived_labels_are_marked_bootstrap(self):
|
|
111
|
+
out = laya_labeler.derive_label(self.scores(fix=0.9))
|
|
112
|
+
self.assertTrue(out['derived'])
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class GuardTests(unittest.TestCase):
|
|
116
|
+
def test_refuses_to_overwrite_sonnet_scores_file(self):
|
|
117
|
+
with self.assertRaisesRegex(ValueError, 'protected'):
|
|
118
|
+
laya_labeler.output_row({'id': 'x', 'text': 'fix it'}, {}, backend='stub',
|
|
119
|
+
out_name='labels-intent-scores.jsonl')
|
|
120
|
+
|
|
121
|
+
def test_output_row_shape_matches_trainer_contract(self):
|
|
122
|
+
scores = {i: (0.9 if i == 'fix' else 0.0) for i in INTENTS}
|
|
123
|
+
row = laya_labeler.output_row({'id': 'abc', 'text': 'fix it'}, scores,
|
|
124
|
+
backend='stub', out_name='labels-intent-laya-zs.jsonl',
|
|
125
|
+
routing='english')
|
|
126
|
+
self.assertEqual(row['id'], 'abc')
|
|
127
|
+
self.assertEqual(row['gold'], 'fix')
|
|
128
|
+
self.assertEqual(row['secondary'], [])
|
|
129
|
+
self.assertEqual(list(row['scores']), INTENTS)
|
|
130
|
+
self.assertEqual(row['labeler']['task'], 'intent')
|
|
131
|
+
self.assertEqual(row['labeler']['routing'], 'english')
|
|
132
|
+
self.assertEqual(row['labeler']['questions'], 'v1')
|
|
133
|
+
self.assertEqual(json.loads(json.dumps(row)), row)
|
|
134
|
+
|
|
135
|
+
def test_output_row_records_questions_version(self):
|
|
136
|
+
scores = {i: 0.1 for i in INTENTS}
|
|
137
|
+
row = laya_labeler.output_row({'id': 'abc', 'text': 'x'}, scores,
|
|
138
|
+
backend='stub', out_name='o.jsonl', questions_version='v2')
|
|
139
|
+
self.assertEqual(row['labeler']['questions'], 'v2')
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class EndToEndTests(unittest.TestCase):
|
|
143
|
+
def test_stub_backend_labels_a_split(self):
|
|
144
|
+
prompts = [{'id': 'p1', 'split': 'validation', 'text': 'fix the crash'},
|
|
145
|
+
{'id': 'p2', 'split': 'train', 'text': 'commit and push'}]
|
|
146
|
+
calls = []
|
|
147
|
+
|
|
148
|
+
def stub(text):
|
|
149
|
+
calls.append(text)
|
|
150
|
+
return ({i: (0.8 if i == 'fix' else 0.05) for i in INTENTS}, 'english', 1.0)
|
|
151
|
+
|
|
152
|
+
with tempfile.TemporaryDirectory() as d:
|
|
153
|
+
data = Path(d) / 'data'
|
|
154
|
+
data.mkdir()
|
|
155
|
+
(data / 'prompts.jsonl').write_text(
|
|
156
|
+
'\n'.join(json.dumps(p) for p in prompts) + '\n', encoding='utf-8')
|
|
157
|
+
out = Path(d) / 'labels-intent-laya-zs.jsonl'
|
|
158
|
+
manifest = Path(d) / 'manifest.json'
|
|
159
|
+
summary = laya_labeler.label_split(
|
|
160
|
+
data, 'validation', out, manifest, backend_fn=stub, backend_name='stub')
|
|
161
|
+
self.assertEqual(calls, ['fix the crash'], 'only the requested split is labeled')
|
|
162
|
+
rows = [json.loads(line) for line in out.read_text(encoding='utf-8').strip().split('\n')]
|
|
163
|
+
self.assertEqual([r['id'] for r in rows], ['p1'])
|
|
164
|
+
self.assertEqual(rows[0]['gold'], 'fix')
|
|
165
|
+
self.assertEqual(summary['labeled'], 1)
|
|
166
|
+
man = json.loads(manifest.read_text(encoding='utf-8'))
|
|
167
|
+
self.assertEqual(man['backend'], 'stub')
|
|
168
|
+
self.assertEqual(man['split'], 'validation')
|
|
169
|
+
|
|
170
|
+
def test_resume_skips_done_ids(self):
|
|
171
|
+
calls = []
|
|
172
|
+
|
|
173
|
+
def stub(text):
|
|
174
|
+
calls.append(text)
|
|
175
|
+
return ({i: (0.8 if i == 'fix' else 0.05) for i in INTENTS}, 'english', 1.0)
|
|
176
|
+
|
|
177
|
+
with tempfile.TemporaryDirectory() as d:
|
|
178
|
+
data = Path(d) / 'data'
|
|
179
|
+
data.mkdir()
|
|
180
|
+
prompts = [{'id': f'p{i}', 'split': 'validation', 'text': f'text {i}'} for i in range(3)]
|
|
181
|
+
(data / 'prompts.jsonl').write_text(
|
|
182
|
+
'\n'.join(json.dumps(p) for p in prompts) + '\n', encoding='utf-8')
|
|
183
|
+
out = Path(d) / 'labels-intent-laya-zs.jsonl'
|
|
184
|
+
manifest = Path(d) / 'manifest.json'
|
|
185
|
+
laya_labeler.label_split(data, 'validation', out, manifest,
|
|
186
|
+
backend_fn=stub, backend_name='stub')
|
|
187
|
+
self.assertEqual(len(calls), 3)
|
|
188
|
+
laya_labeler.label_rows(
|
|
189
|
+
prompts, 'validation', 'sha', out, manifest, backend_fn=stub,
|
|
190
|
+
backend_name='stub', resume=True)
|
|
191
|
+
self.assertEqual(len(calls), 3, 'resume makes no new backend calls')
|
|
192
|
+
self.assertEqual(
|
|
193
|
+
[json.loads(line)['id'] for line in out.read_text(encoding='utf-8').splitlines()],
|
|
194
|
+
['p0', 'p1', 'p2'], 'resume appends nothing when all ids are done')
|
|
195
|
+
man2 = json.loads(manifest.read_text(encoding='utf-8'))
|
|
196
|
+
self.assertEqual([man2['labeled'], man2['thisRun'], man2['resumed']], [3, 0, True])
|
|
197
|
+
self.assertEqual(man2['routing'], {'english': 3})
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
if __name__ == '__main__':
|
|
201
|
+
unittest.main()
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Stdlib tests for per-intent Bernoulli evaluation (#255 Phase 3).
|
|
2
|
+
|
|
3
|
+
Each intent is its own binary decision with its own threshold: no simplex,
|
|
4
|
+
no single winner. Run with:
|
|
5
|
+
python3 ci/system-one-laya-perintent-test.py
|
|
6
|
+
"""
|
|
7
|
+
import importlib.util
|
|
8
|
+
import sys
|
|
9
|
+
import unittest
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
sys.dont_write_bytecode = True # never leave __pycache__ in the source tree that packaging copies
|
|
13
|
+
path = Path(__file__).resolve().parents[1] / 'scripts/system-one-perintent-laya.py'
|
|
14
|
+
spec = importlib.util.spec_from_file_location('laya_perintent', path)
|
|
15
|
+
laya_perintent = importlib.util.module_from_spec(spec)
|
|
16
|
+
spec.loader.exec_module(laya_perintent)
|
|
17
|
+
|
|
18
|
+
INTENTS = ['explain', 'discuss', 'git', 'fix', 'edit', 'feature', 'refactor',
|
|
19
|
+
'review', 'test', 'docs', 'plan', 'investigate']
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class BinarizeTests(unittest.TestCase):
|
|
23
|
+
def test_band_boundary(self):
|
|
24
|
+
self.assertEqual(laya_perintent.binarize({'fix': 0.4, 'test': 0.39}), {'fix', 'test'} - {'test'})
|
|
25
|
+
self.assertEqual(laya_perintent.binarize({'fix': 0.4}), {'fix'})
|
|
26
|
+
|
|
27
|
+
def test_custom_positive_band(self):
|
|
28
|
+
self.assertEqual(laya_perintent.binarize({'fix': 0.5}, band=0.6), set())
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class SweepTests(unittest.TestCase):
|
|
32
|
+
def test_separable_intent_gets_perfect_threshold(self):
|
|
33
|
+
scores = [0.1, 0.2, 0.8, 0.9]
|
|
34
|
+
truth = [False, False, True, True]
|
|
35
|
+
tau, p, r, f = laya_perintent.sweep_threshold(scores, truth)
|
|
36
|
+
self.assertEqual((p, r, f), (1.0, 1.0, 1.0))
|
|
37
|
+
self.assertTrue(0.2 < tau <= 0.8)
|
|
38
|
+
|
|
39
|
+
def test_all_negative_stays_silent(self):
|
|
40
|
+
tau, p, r, f = laya_perintent.sweep_threshold([0.1, 0.2, 0.3], [False, False, False])
|
|
41
|
+
self.assertEqual(r, 0.0)
|
|
42
|
+
self.assertTrue(tau > 0.3)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class MicroTests(unittest.TestCase):
|
|
46
|
+
def test_hand_computed_micro(self):
|
|
47
|
+
# totals: tp=1, fp=2, fn=2.
|
|
48
|
+
per = {'a': (1, 1, 1), 'b': (0, 1, 1)} # (tp, fp, fn)
|
|
49
|
+
m = laya_perintent.micro(per)
|
|
50
|
+
self.assertAlmostEqual(m['precision'], 1 / 3)
|
|
51
|
+
self.assertAlmostEqual(m['recall'], 1 / 3)
|
|
52
|
+
self.assertAlmostEqual(m['f1'], 1 / 3)
|
|
53
|
+
|
|
54
|
+
def test_empty_predictions_score_zero_not_nan(self):
|
|
55
|
+
m = laya_perintent.micro({'a': (0, 0, 2)})
|
|
56
|
+
self.assertEqual((m['precision'], m['recall'], m['f1']), (0.0, 0.0, 0.0))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
if __name__ == '__main__':
|
|
60
|
+
unittest.main()
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""Contract tests for the Laya fine-tuned provider adapter (#255 Phase 3).
|
|
2
|
+
|
|
3
|
+
The 13-option evaluator vector, unclassified ramp, limits and artifact
|
|
4
|
+
rules are all pinned here with a stub predictor: no model weights needed.
|
|
5
|
+
Run with:
|
|
6
|
+
python3 ci/system-one-laya-provider-test.py
|
|
7
|
+
"""
|
|
8
|
+
import hashlib
|
|
9
|
+
import importlib.util
|
|
10
|
+
import json
|
|
11
|
+
import sys
|
|
12
|
+
import tempfile
|
|
13
|
+
import unittest
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
sys.dont_write_bytecode = True # never leave __pycache__ in the source tree that packaging copies
|
|
17
|
+
path = Path(__file__).resolve().parents[1] / 'harness-everything/scripts/system-one/laya_adapter.py'
|
|
18
|
+
spec = importlib.util.spec_from_file_location('laya_adapter', path)
|
|
19
|
+
laya_adapter = importlib.util.module_from_spec(spec)
|
|
20
|
+
spec.loader.exec_module(laya_adapter)
|
|
21
|
+
|
|
22
|
+
INTENTS = ['explain', 'discuss', 'git', 'fix', 'edit', 'feature', 'refactor',
|
|
23
|
+
'review', 'test', 'docs', 'plan', 'investigate']
|
|
24
|
+
OPTIONS = [{'id': i, 'text': f'{i} text'} for i in INTENTS] + [{'id': 'unclassified', 'text': 'unclear'}]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def request(options=None, task='intent', context='fix it'):
|
|
28
|
+
return {'schemaVersion': 1, 'task': task, 'context': context,
|
|
29
|
+
'options': options if options is not None else OPTIONS,
|
|
30
|
+
'catalogHash': 'b' * 64, 'requestHash': 'a' * 64}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def stub_predict(probs):
|
|
34
|
+
def run(state, questions):
|
|
35
|
+
assert state and set(questions) == set(INTENTS)
|
|
36
|
+
return dict(probs)
|
|
37
|
+
return run
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ArtifactTests(unittest.TestCase):
|
|
41
|
+
def write_ckpt(self, root, weights=b'fake-weights-1', config=b'{"cfg":1}'):
|
|
42
|
+
ckpt = Path(root) / 'model.safetensors'
|
|
43
|
+
ckpt.write_bytes(weights)
|
|
44
|
+
(Path(root) / 'rl_agent_config.json').write_bytes(config)
|
|
45
|
+
return {'checkpoint': str(ckpt),
|
|
46
|
+
'weightsSha256': hashlib.sha256(weights).hexdigest(),
|
|
47
|
+
'configSha256': hashlib.sha256(config).hexdigest()}
|
|
48
|
+
|
|
49
|
+
def test_verify_ok_and_config_sibling_rule(self):
|
|
50
|
+
with tempfile.TemporaryDirectory() as d:
|
|
51
|
+
m = self.write_ckpt(d)
|
|
52
|
+
self.assertEqual(laya_adapter.verify_artifacts(m), Path(m['checkpoint']))
|
|
53
|
+
|
|
54
|
+
def test_hash_mismatch_and_bad_format_fail(self):
|
|
55
|
+
with tempfile.TemporaryDirectory() as d:
|
|
56
|
+
m = self.write_ckpt(d)
|
|
57
|
+
bad = dict(m, weightsSha256='0' * 64)
|
|
58
|
+
with self.assertRaisesRegex(ValueError, 'artifact-hash'):
|
|
59
|
+
laya_adapter.verify_artifacts(bad)
|
|
60
|
+
bad2 = dict(m, checkpoint=str(Path(d) / 'model.pt'))
|
|
61
|
+
with self.assertRaisesRegex(ValueError, 'checkpoint-format'):
|
|
62
|
+
laya_adapter.verify_artifacts(bad2)
|
|
63
|
+
bad3 = dict(m, checkpoint='relative/model.safetensors')
|
|
64
|
+
with self.assertRaisesRegex(ValueError, 'checkpoint-format'):
|
|
65
|
+
laya_adapter.verify_artifacts(bad3)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class ScorerTests(unittest.TestCase):
|
|
69
|
+
def probs(self, **over):
|
|
70
|
+
base = {i: 0.05 for i in INTENTS}
|
|
71
|
+
base.update(over)
|
|
72
|
+
return base
|
|
73
|
+
|
|
74
|
+
def test_scores_follow_request_option_order_raw(self):
|
|
75
|
+
score, _ = laya_adapter.make_scorer(stub_predict(self.probs(fix=0.8)))
|
|
76
|
+
shuffled = [OPTIONS[5], OPTIONS[0], *[o for o in OPTIONS if o['id'] not in ('feature', 'explain')]]
|
|
77
|
+
out = score(request(options=shuffled))
|
|
78
|
+
self.assertEqual(len(out), 13)
|
|
79
|
+
by_id = {o['id']: p for o, p in zip(shuffled, out)}
|
|
80
|
+
self.assertGreater(by_id['fix'], by_id['test'])
|
|
81
|
+
self.assertTrue(all(0.0 <= p <= 1.0 for p in out))
|
|
82
|
+
self.assertGreater(sum(out), 1.0, 'independent scores sum past one: no simplex')
|
|
83
|
+
|
|
84
|
+
def test_confident_max_leaves_no_unclassified_mass(self):
|
|
85
|
+
score, _ = laya_adapter.make_scorer(stub_predict(self.probs(fix=0.9)))
|
|
86
|
+
out = score(request())
|
|
87
|
+
self.assertEqual(out[-1], 0.0, 'max above the cutoff closes the abstain ramp')
|
|
88
|
+
|
|
89
|
+
def test_flat_lows_abstain_through_unclassified(self):
|
|
90
|
+
score, _ = laya_adapter.make_scorer(stub_predict(self.probs()))
|
|
91
|
+
out = score(request())
|
|
92
|
+
self.assertGreater(out[-1], 0.5, 'nothing actionable concentrates on unclassified')
|
|
93
|
+
|
|
94
|
+
def test_non_intent_task_and_missing_intent_fail(self):
|
|
95
|
+
score, _ = laya_adapter.make_scorer(stub_predict(self.probs()))
|
|
96
|
+
with self.assertRaisesRegex(ValueError, 'task'):
|
|
97
|
+
score(request(task='tier'))
|
|
98
|
+
with self.assertRaisesRegex(ValueError, 'options'):
|
|
99
|
+
score(request(options=[{'id': 'fix', 'text': 'x'}, {'id': 'unclassified', 'text': 'y'}]))
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class LimitTests(unittest.TestCase):
|
|
103
|
+
def test_boundary_passes_and_overflow_fails(self):
|
|
104
|
+
score, config = laya_adapter.make_scorer(stub_predict({i: 0.1 for i in INTENTS}))
|
|
105
|
+
ok_ctx = 'x' * config['context_tokens']
|
|
106
|
+
laya_adapter.check_limits(request(context=ok_ctx), config)
|
|
107
|
+
with self.assertRaisesRegex(ValueError, 'input-too-long'):
|
|
108
|
+
laya_adapter.check_limits(request(context=ok_ctx + 'x'), config)
|
|
109
|
+
big_opt = [{'id': 'a', 'text': 'y' * (config['option_tokens'] + 1)}, {'id': 'b', 'text': 'z'}]
|
|
110
|
+
with self.assertRaisesRegex(ValueError, 'input-too-long'):
|
|
111
|
+
laya_adapter.check_limits(request(options=big_opt), config)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
if __name__ == '__main__':
|
|
115
|
+
unittest.main()
|