@matchbox-ai/train 0.1.0 → 0.2.1
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/CHANGELOG.md +36 -0
- package/README.md +1 -1
- package/THIRD-PARTY-NOTICES.md +1 -1
- package/dist/define-pipeline-D6I-jAE_.js +20 -0
- package/dist/define-pipeline-D6I-jAE_.js.map +1 -0
- package/dist/encoders/index.d.ts +0 -1
- package/dist/encoders/index.d.ts.map +1 -1
- package/dist/evaluate-sequence-ZCwyomzQ.js.map +1 -1
- package/dist/evaluation/evaluate-sequence.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -11
- package/dist/index.js.map +1 -1
- package/dist/load-project.d.ts +0 -4
- package/dist/load-project.d.ts.map +1 -1
- package/dist/models/presets.d.ts +6 -6
- package/dist/models/presets.d.ts.map +1 -1
- package/dist/models/record/fit.d.ts +6 -99
- package/dist/models/record/fit.d.ts.map +1 -1
- package/dist/models/record/run.d.ts +1 -57
- package/dist/models/record/run.d.ts.map +1 -1
- package/dist/models/record/verify-export.d.ts +1 -2
- package/dist/models/record/verify-export.d.ts.map +1 -1
- package/dist/models/sequence/fit-sequence.d.ts +9 -116
- package/dist/models/sequence/fit-sequence.d.ts.map +1 -1
- package/dist/models/sequence/prepare-supervision.d.ts +10 -0
- package/dist/models/sequence/prepare-supervision.d.ts.map +1 -0
- package/dist/models/sequence/run-sequence.d.ts +3 -71
- package/dist/models/sequence/run-sequence.d.ts.map +1 -1
- package/dist/models/sequence/types.d.ts +4 -0
- package/dist/models/sequence/types.d.ts.map +1 -1
- package/dist/native/fit.d.ts +15 -0
- package/dist/native/fit.d.ts.map +1 -0
- package/dist/native/index.d.ts +3 -0
- package/dist/native/index.d.ts.map +1 -0
- package/dist/native/record.d.ts +12 -0
- package/dist/native/record.d.ts.map +1 -0
- package/dist/package-model-CPj0qnQs.js.map +1 -1
- package/dist/packaging/package-model.d.ts.map +1 -1
- package/dist/pipeline/define-pipeline.d.ts +2 -5
- package/dist/pipeline/define-pipeline.d.ts.map +1 -1
- package/dist/project/artifact.d.ts +21 -67
- package/dist/project/artifact.d.ts.map +1 -1
- package/dist/project/config.d.ts +1 -4
- package/dist/project/config.d.ts.map +1 -1
- package/dist/project/discover.d.ts +2 -0
- package/dist/project/discover.d.ts.map +1 -1
- package/dist/project/entry.d.ts +5 -0
- package/dist/project/entry.d.ts.map +1 -0
- package/dist/project/index.d.ts +1 -1
- package/dist/project/index.d.ts.map +1 -1
- package/dist/project/index.js +2 -2
- package/dist/{project-BP331m20.js → project-BR13wXHr.js} +51 -18
- package/dist/project-BR13wXHr.js.map +1 -0
- package/dist/{record-BTZ5LWHF.js → record-MCYVi9O6.js} +60 -114
- package/dist/record-MCYVi9O6.js.map +1 -0
- package/dist/{run-Cwa7vD_T.js → run-PB_jiKnw.js} +4 -6
- package/dist/run-PB_jiKnw.js.map +1 -0
- package/dist/run.d.ts +4 -128
- package/dist/run.d.ts.map +1 -1
- package/dist/sequence-DGoGU7kG.js +219 -0
- package/dist/sequence-DGoGU7kG.js.map +1 -0
- package/dist/train.d.ts +4 -128
- package/dist/train.d.ts.map +1 -1
- package/dist/types.d.ts +0 -2
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +12 -7
- package/docs/agent-skill.md +8 -4
- package/docs/cli.md +85 -28
- package/docs/contributing.md +43 -0
- package/docs/dataset-format.md +4 -2
- package/docs/evaluation.md +66 -5
- package/docs/example-evaluation.md +47 -0
- package/docs/example-results.json +280 -0
- package/docs/examples/money.md +19 -0
- package/docs/examples/time.md +43 -0
- package/docs/getting-started.md +48 -54
- package/docs/native-packages.md +31 -0
- package/docs/neural-training.md +1 -1
- package/docs/parser-api.md +16 -16
- package/docs/pipelines.md +5 -6
- package/docs/primitives/README.md +12 -11
- package/docs/project-structure.md +17 -3
- package/docs/react.md +49 -7
- package/docs/reference/README.md +25 -0
- package/docs/reference/configuration.md +43 -0
- package/docs/reference/evaluation.md +21 -0
- package/docs/reference/pipeline.md +43 -0
- package/docs/reference/runtime.md +56 -0
- package/docs/reference/supervision.md +87 -0
- package/docs/reference/training.md +26 -0
- package/docs/reference/vite.md +20 -0
- package/docs/releases.md +6 -16
- package/docs/repository-design.md +2 -2
- package/docs/runtime-backends.md +8 -11
- package/docs/training.md +73 -0
- package/docs/website-deployment.md +11 -0
- package/native.cjs +1 -0
- package/package.json +10 -5
- package/prebuilds/darwin-arm64/node.napi.node +0 -0
- package/prebuilds/darwin-x64/node.napi.node +0 -0
- package/prebuilds/linux-arm64/node.napi.glibc.node +0 -0
- package/prebuilds/linux-arm64/node.napi.musl.node +0 -0
- package/prebuilds/linux-x64/node.napi.glibc.node +0 -0
- package/prebuilds/linux-x64/node.napi.musl.node +0 -0
- package/prebuilds/win32-arm64/node.napi.node +0 -0
- package/prebuilds/win32-x64/node.napi.node +0 -0
- package/dist/define-pipeline-Colvwt7u.js +0 -32
- package/dist/define-pipeline-Colvwt7u.js.map +0 -1
- package/dist/encoders/word-tokens.d.ts +0 -5
- package/dist/encoders/word-tokens.d.ts.map +0 -1
- package/dist/models/sequence/create-network.d.ts +0 -3
- package/dist/models/sequence/create-network.d.ts.map +0 -1
- package/dist/project-BP331m20.js.map +0 -1
- package/dist/record-BTZ5LWHF.js.map +0 -1
- package/dist/run-Cwa7vD_T.js.map +0 -1
- package/dist/sequence-C_WfVGO1.js +0 -251
- package/dist/sequence-C_WfVGO1.js.map +0 -1
- package/docs/filter-research.md +0 -63
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { n as sameOutput, t as evaluateSequence } from "./evaluate-sequence-ZCwyomzQ.js";
|
|
2
|
+
import { t as packageModel } from "./package-model-CPj0qnQs.js";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { readSequenceArtifact, tensorPredictor, tokenize, windows } from "@matchbox-ai/core/internal";
|
|
6
|
+
import { readFile } from "node:fs/promises";
|
|
7
|
+
import { dirname, relative, resolve } from "node:path";
|
|
8
|
+
import { pathToFileURL } from "node:url";
|
|
9
|
+
import { createParser } from "@matchbox-ai/core/runtime";
|
|
10
|
+
import { createHash } from "node:crypto";
|
|
11
|
+
//#region src/native/fit.ts
|
|
12
|
+
const require = createRequire(import.meta.url);
|
|
13
|
+
function predict(config, weights, inputs) {
|
|
14
|
+
return require("#native").predict(JSON.stringify(config), Buffer.from(weights), Int32Array.from(inputs.flat()));
|
|
15
|
+
}
|
|
16
|
+
function fit(config, inputs, labels, progress) {
|
|
17
|
+
return require("#native").fit(JSON.stringify(config), Int32Array.from(inputs.flat()), Int32Array.from(labels), (error, value) => {
|
|
18
|
+
if (!error) progress?.(value[0], value[1]);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/models/sequence/prepare-supervision.ts
|
|
23
|
+
function prepareSupervision(examples, recipe) {
|
|
24
|
+
const vocabulary = [...new Set(examples.flatMap((row) => tokenize(row.input, recipe.tokenizer).map((token) => token.key)))].sort();
|
|
25
|
+
const radius = 1;
|
|
26
|
+
const dropout = recipe.tokenDropout ?? 0;
|
|
27
|
+
if (!Number.isFinite(dropout) || dropout < 0 || dropout > .5) throw new Error("tokenDropout must be between 0 and 0.5.");
|
|
28
|
+
let maskingSeed = 7043;
|
|
29
|
+
const random = () => {
|
|
30
|
+
maskingSeed = Math.imul(maskingSeed, 1664525) + 1013904223 >>> 0;
|
|
31
|
+
return maskingSeed / 4294967296;
|
|
32
|
+
};
|
|
33
|
+
const inputs = [];
|
|
34
|
+
const labels = [];
|
|
35
|
+
for (const example of examples) {
|
|
36
|
+
const tokens = tokenize(example.input, recipe.tokenizer);
|
|
37
|
+
const annotations = recipe.annotate(example, tokens);
|
|
38
|
+
if (annotations.length !== tokens.length) throw new Error(`Annotation length mismatch: ${example.input}`);
|
|
39
|
+
windows(tokens, vocabulary, radius).forEach((window, position) => {
|
|
40
|
+
const label = annotations[position];
|
|
41
|
+
if (label === null) return;
|
|
42
|
+
const id = recipe.labels.indexOf(label);
|
|
43
|
+
if (id < 0) throw new Error(`Unknown annotation ${label}: ${example.input}`);
|
|
44
|
+
inputs.push(window);
|
|
45
|
+
labels.push(id);
|
|
46
|
+
if (dropout > 0) {
|
|
47
|
+
inputs.push(window.map((id) => id > 1 && random() < dropout ? 1 : id));
|
|
48
|
+
labels.push(id);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
if (recipe.labels.some((_, id) => !labels.includes(id))) throw new Error("Every label needs supervised training examples.");
|
|
53
|
+
maskingSeed = 9187;
|
|
54
|
+
for (let index = inputs.length - 1; index > 0; index--) {
|
|
55
|
+
const other = Math.floor(random() * (index + 1));
|
|
56
|
+
[inputs[index], inputs[other]] = [inputs[other], inputs[index]];
|
|
57
|
+
[labels[index], labels[other]] = [labels[other], labels[index]];
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
vocabulary,
|
|
61
|
+
radius,
|
|
62
|
+
dropout,
|
|
63
|
+
inputs,
|
|
64
|
+
labels
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
//#endregion
|
|
68
|
+
//#region src/models/sequence/fit-sequence.ts
|
|
69
|
+
async function fitSequence(examples, recipe, metadata, probes = [], progress) {
|
|
70
|
+
const { vocabulary, radius, dropout, inputs, labels } = prepareSupervision(examples, recipe);
|
|
71
|
+
const result = await fit({
|
|
72
|
+
vocabularySize: vocabulary.length + 2,
|
|
73
|
+
labelCount: recipe.labels.length
|
|
74
|
+
}, inputs, labels, progress);
|
|
75
|
+
const artifact = (weights) => ({
|
|
76
|
+
formatVersion: 3,
|
|
77
|
+
engine: "burn-0.21",
|
|
78
|
+
kind: "sequence-parser",
|
|
79
|
+
architecture: "embedding-window-mlp",
|
|
80
|
+
...metadata,
|
|
81
|
+
tokenizer: recipe.tokenizer,
|
|
82
|
+
readout: recipe.readout,
|
|
83
|
+
vocabulary,
|
|
84
|
+
labels: [...recipe.labels],
|
|
85
|
+
radius,
|
|
86
|
+
unknownTokens: dropout > 0 ? "predict" : "abstain",
|
|
87
|
+
threshold: .75,
|
|
88
|
+
precision: "float32",
|
|
89
|
+
weights: Buffer.from(weights).toString("base64")
|
|
90
|
+
});
|
|
91
|
+
const model = artifact(result.weights);
|
|
92
|
+
const checked = [...examples.slice(0, 16).map((row) => row.input), ...probes];
|
|
93
|
+
const portable = await tensorPredictor(model);
|
|
94
|
+
let maxConfidenceError = 0;
|
|
95
|
+
let labelDisagreements = 0;
|
|
96
|
+
try {
|
|
97
|
+
for (const input of checked) {
|
|
98
|
+
const tokens = tokenize(input, recipe.tokenizer);
|
|
99
|
+
if (!tokens.length) continue;
|
|
100
|
+
const scores = predict({
|
|
101
|
+
vocabularySize: vocabulary.length + 2,
|
|
102
|
+
labelCount: recipe.labels.length
|
|
103
|
+
}, result.weights, windows(tokens, vocabulary, radius));
|
|
104
|
+
portable.sequence(input).forEach((token, index) => {
|
|
105
|
+
const row = scores.slice(index * recipe.labels.length, (index + 1) * recipe.labels.length);
|
|
106
|
+
const confidence = Math.max(...row);
|
|
107
|
+
maxConfidenceError = Math.max(maxConfidenceError, Math.abs(confidence - token.confidence));
|
|
108
|
+
if (recipe.labels[row.indexOf(confidence)] !== token.label) labelDisagreements++;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
} finally {
|
|
112
|
+
portable.dispose();
|
|
113
|
+
}
|
|
114
|
+
if (labelDisagreements || maxConfidenceError > 1e-5) throw new Error("Burn native and WASM predictions disagree.");
|
|
115
|
+
return {
|
|
116
|
+
model,
|
|
117
|
+
parameters: result.parameters,
|
|
118
|
+
history: result.loss,
|
|
119
|
+
parity: {
|
|
120
|
+
examples: checked.length,
|
|
121
|
+
maxConfidenceError,
|
|
122
|
+
labelDisagreements
|
|
123
|
+
},
|
|
124
|
+
supervisedTokens: inputs.length
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
//#endregion
|
|
128
|
+
//#region src/models/sequence/run-sequence.ts
|
|
129
|
+
const hash = (value) => createHash("sha256").update(value).digest("hex");
|
|
130
|
+
async function runSequence(command, project, progress) {
|
|
131
|
+
const { task, config } = project;
|
|
132
|
+
const root = project.root;
|
|
133
|
+
const sequence = config.sequence;
|
|
134
|
+
if (!sequence) throw new Error("A sequence pipeline needs both recipe and decoder modules.");
|
|
135
|
+
const savedArtifact = command === "eval" ? readSequenceArtifact(JSON.parse(await readFile(project.output, "utf8"))) : null;
|
|
136
|
+
const decoderPath = savedArtifact ? resolve(dirname(project.output), savedArtifact.decoderModule) : resolve(root, sequence.decoder);
|
|
137
|
+
const decode = (await import(pathToFileURL(decoderPath).href)).default;
|
|
138
|
+
const evaluateSequence$1 = (parser, examples) => evaluateSequence(parser, examples, (value) => task.validateOutput(value).success).finally(() => {
|
|
139
|
+
if ("dispose" in parser && typeof parser.dispose === "function") parser.dispose();
|
|
140
|
+
});
|
|
141
|
+
const parser = (artifact) => createParser(artifact, task, decode);
|
|
142
|
+
if (savedArtifact) return { evaluation: await evaluateSequence$1(parser(savedArtifact), project.evaluation) };
|
|
143
|
+
const recipe = (await import(pathToFileURL(resolve(root, sequence.recipe)).href)).default;
|
|
144
|
+
const modulePath = (path) => {
|
|
145
|
+
const value = relative(dirname(project.output), path).replaceAll("\\", "/");
|
|
146
|
+
return value.startsWith(".") ? value : `./${value}`;
|
|
147
|
+
};
|
|
148
|
+
const rejections = z.array(z.strictObject({
|
|
149
|
+
input: z.string(),
|
|
150
|
+
output: z.null()
|
|
151
|
+
})).parse(recipe.rejections ?? []);
|
|
152
|
+
const training = [...project.train, ...rejections];
|
|
153
|
+
const heldOut = new Set([...project.validation, ...project.evaluation].map((row) => row.input.trim().toLowerCase()));
|
|
154
|
+
for (const example of training) {
|
|
155
|
+
if (!task.validateInput(example.input).success) throw new Error(`Invalid training input: ${example.input}`);
|
|
156
|
+
if (heldOut.has(example.input.trim().toLowerCase())) throw new Error(`Training input overlaps evaluation: ${example.input}`);
|
|
157
|
+
const tokens = tokenize(example.input, recipe.tokenizer);
|
|
158
|
+
const labels = recipe.annotate(example, tokens);
|
|
159
|
+
const value = decode(tokens.map((token, index) => ({
|
|
160
|
+
...token,
|
|
161
|
+
label: labels[index] ?? "O",
|
|
162
|
+
confidence: 1
|
|
163
|
+
})), example.input);
|
|
164
|
+
if (!sameOutput(value, example.output)) throw new Error(`Training annotations do not decode to the supplied output: ${example.input}`);
|
|
165
|
+
}
|
|
166
|
+
const started = performance.now();
|
|
167
|
+
const fit = await fitSequence(training, recipe, {
|
|
168
|
+
taskMetadata: task.toJSON(),
|
|
169
|
+
taskModule: modulePath(project.taskPath),
|
|
170
|
+
decoderModule: modulePath(resolve(root, sequence.decoder))
|
|
171
|
+
}, project.validation.map((row) => row.input), progress);
|
|
172
|
+
const validation = await evaluateSequence$1(parser(fit.model), project.validation);
|
|
173
|
+
const bytes = Buffer.byteLength(JSON.stringify(fit.model));
|
|
174
|
+
if (validation.exactAccuracy < config.minAccuracy || bytes > config.maxBytes) throw new Error(`Sequence model failed validation/size requirements (${validation.exactAccuracy}, ${bytes} bytes). ${JSON.stringify(validation.failures.slice(0, 10))}`);
|
|
175
|
+
const challenges = config.challenges ? z.array(z.strictObject({
|
|
176
|
+
input: z.string(),
|
|
177
|
+
output: z.null()
|
|
178
|
+
})).min(1).parse(JSON.parse(await readFile(resolve(root, config.challenges), "utf8"))) : null;
|
|
179
|
+
const report = {
|
|
180
|
+
formatVersion: 2,
|
|
181
|
+
architecture: fit.model.architecture,
|
|
182
|
+
backend: "Burn native CPU",
|
|
183
|
+
seed: 42,
|
|
184
|
+
artifactSha256: hash(JSON.stringify(fit.model)),
|
|
185
|
+
bytes,
|
|
186
|
+
parameters: fit.parameters,
|
|
187
|
+
datasetSha256: project.sources.map((source) => ({
|
|
188
|
+
source: source.source,
|
|
189
|
+
sha256: hash(source.text)
|
|
190
|
+
})),
|
|
191
|
+
examples: {
|
|
192
|
+
train: project.train.length,
|
|
193
|
+
rejections: recipe.rejections?.length ?? 0,
|
|
194
|
+
validation: project.validation.length,
|
|
195
|
+
eval: project.evaluation.length
|
|
196
|
+
},
|
|
197
|
+
supervisionSha256: hash(JSON.stringify(training.map((row) => ({
|
|
198
|
+
...row,
|
|
199
|
+
labels: recipe.annotate(row, tokenize(row.input, recipe.tokenizer))
|
|
200
|
+
})))),
|
|
201
|
+
supervisedTokens: fit.supervisedTokens,
|
|
202
|
+
loss: fit.history,
|
|
203
|
+
exportParity: fit.parity,
|
|
204
|
+
validation,
|
|
205
|
+
evaluation: await evaluateSequence$1(parser(fit.model), project.evaluation),
|
|
206
|
+
challenges: challenges ? await evaluateSequence$1(parser(fit.model), challenges) : null,
|
|
207
|
+
trainingMs: performance.now() - started,
|
|
208
|
+
notes: "Float32 weights in a Burn binary record, base64-encoded in the artifact. Validation gates export. Eval labels do not influence selection. Scores are uncalibrated."
|
|
209
|
+
};
|
|
210
|
+
await packageModel(project.output, fit.model, report);
|
|
211
|
+
return {
|
|
212
|
+
report,
|
|
213
|
+
output: project.output
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
//#endregion
|
|
217
|
+
export { runSequence };
|
|
218
|
+
|
|
219
|
+
//# sourceMappingURL=sequence-DGoGU7kG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sequence-DGoGU7kG.js","names":["evaluateSequence","evaluate"],"sources":["../src/native/fit.ts","../src/models/sequence/prepare-supervision.ts","../src/models/sequence/fit-sequence.ts","../src/models/sequence/run-sequence.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\ninterface NativeResult {\n parameters: number;\n weights: Uint8Array;\n loss: number[];\n}\nconst require = createRequire(import.meta.url);\nexport function predict(\n config: { vocabularySize: number; labelCount: number },\n weights: Uint8Array,\n inputs: number[][],\n): number[] {\n const native = require(\"#native\") as {\n predict(config: string, weights: Buffer, inputs: Int32Array): number[];\n };\n return native.predict(\n JSON.stringify(config),\n Buffer.from(weights),\n Int32Array.from(inputs.flat()),\n );\n}\nexport function fit(\n config: { vocabularySize: number; labelCount: number },\n inputs: number[][],\n labels: number[],\n progress?: (epoch: number, loss: number) => void,\n): Promise<NativeResult> {\n const native = require(\"#native\") as {\n fit(\n config: string,\n inputs: Int32Array,\n labels: Int32Array,\n progress: (error: Error | null, value: number[]) => void,\n ): Promise<NativeResult>;\n };\n return native.fit(\n JSON.stringify(config),\n Int32Array.from(inputs.flat()),\n Int32Array.from(labels),\n (error, value) => {\n if (!error) {\n progress?.(value[0]!, value[1]!);\n }\n },\n );\n}\n","import { tokenize, windows } from \"@matchbox-ai/core/internal\";\nimport type { DatasetExample } from \"@matchbox-ai/core\";\nimport type { SequenceRecipe } from \"./types.js\";\nexport function prepareSupervision(\n examples: readonly DatasetExample<unknown>[],\n recipe: SequenceRecipe,\n) {\n const vocabulary = [\n ...new Set(\n examples.flatMap((row) => tokenize(row.input, recipe.tokenizer).map((token) => token.key)),\n ),\n ].sort();\n const radius = 1;\n const dropout = recipe.tokenDropout ?? 0;\n if (!Number.isFinite(dropout) || dropout < 0 || dropout > 0.5) {\n throw new Error(\"tokenDropout must be between 0 and 0.5.\");\n }\n let maskingSeed = 7043;\n const random = () => {\n maskingSeed = (Math.imul(maskingSeed, 1664525) + 1013904223) >>> 0;\n return maskingSeed / 4294967296;\n };\n const inputs: number[][] = [];\n const labels: number[] = [];\n for (const example of examples) {\n const tokens = tokenize(example.input, recipe.tokenizer);\n const annotations = recipe.annotate(example, tokens);\n if (annotations.length !== tokens.length) {\n throw new Error(`Annotation length mismatch: ${example.input}`);\n }\n windows(tokens, vocabulary, radius).forEach((window, position) => {\n const label = annotations[position];\n if (label === null) {\n return;\n }\n const id = recipe.labels.indexOf(label!);\n if (id < 0) {\n throw new Error(`Unknown annotation ${label}: ${example.input}`);\n }\n inputs.push(window);\n labels.push(id);\n if (dropout > 0) {\n inputs.push(window.map((id) => (id > 1 && random() < dropout ? 1 : id)));\n labels.push(id);\n }\n });\n }\n if (recipe.labels.some((_, id) => !labels.includes(id))) {\n throw new Error(\"Every label needs supervised training examples.\");\n }\n // Mix currency/template blocks deterministically before minibatch optimization.\n maskingSeed = 9187;\n for (let index = inputs.length - 1; index > 0; index--) {\n const other = Math.floor(random() * (index + 1));\n [inputs[index], inputs[other]] = [inputs[other]!, inputs[index]!];\n [labels[index], labels[other]] = [labels[other]!, labels[index]!];\n }\n return { vocabulary, radius, dropout, inputs, labels };\n}\n","import { prepareSupervision } from \"./prepare-supervision.js\";\nimport { fit, predict } from \"../../native/index.js\";\nimport { tensorPredictor, tokenize, windows } from \"@matchbox-ai/core/internal\";\nimport type { SequenceArtifact } from \"@matchbox-ai/core/internal\";\nimport type { DatasetExample } from \"@matchbox-ai/core\";\nimport type { SequenceRecipe } from \"./types.js\";\nexport async function fitSequence(\n examples: readonly DatasetExample<unknown>[],\n recipe: SequenceRecipe,\n metadata: Pick<SequenceArtifact, \"taskModule\" | \"taskMetadata\" | \"decoderModule\">,\n probes: readonly string[] = [],\n progress?: (epoch: number, loss: number) => void,\n) {\n const { vocabulary, radius, dropout, inputs, labels } = prepareSupervision(examples, recipe);\n const result = await fit(\n { vocabularySize: vocabulary.length + 2, labelCount: recipe.labels.length },\n inputs,\n labels,\n progress,\n );\n const artifact = (weights: Uint8Array): SequenceArtifact => ({\n formatVersion: 3,\n engine: \"burn-0.21\",\n kind: \"sequence-parser\",\n architecture: \"embedding-window-mlp\",\n ...metadata,\n tokenizer: recipe.tokenizer,\n readout: recipe.readout,\n vocabulary,\n labels: [...recipe.labels],\n radius: radius as 1,\n unknownTokens: dropout > 0 ? \"predict\" : \"abstain\",\n threshold: 0.75,\n precision: \"float32\",\n weights: Buffer.from(weights).toString(\"base64\"),\n });\n const model = artifact(result.weights);\n const checked = [...examples.slice(0, 16).map((row) => row.input), ...probes];\n const portable = await tensorPredictor(model);\n let maxConfidenceError = 0;\n let labelDisagreements = 0;\n try {\n for (const input of checked) {\n const tokens = tokenize(input, recipe.tokenizer);\n if (!tokens.length) {\n continue;\n }\n const scores = predict(\n { vocabularySize: vocabulary.length + 2, labelCount: recipe.labels.length },\n result.weights,\n windows(tokens, vocabulary, radius),\n );\n portable.sequence(input).forEach((token, index) => {\n const row = scores.slice(index * recipe.labels.length, (index + 1) * recipe.labels.length);\n const confidence = Math.max(...row);\n maxConfidenceError = Math.max(maxConfidenceError, Math.abs(confidence - token.confidence));\n if (recipe.labels[row.indexOf(confidence)] !== token.label) {\n labelDisagreements++;\n }\n });\n }\n } finally {\n portable.dispose();\n }\n if (labelDisagreements || maxConfidenceError > 1e-5) {\n throw new Error(\"Burn native and WASM predictions disagree.\");\n }\n return {\n model,\n parameters: result.parameters,\n history: result.loss,\n parity: { examples: checked.length, maxConfidenceError, labelDisagreements },\n supervisedTokens: inputs.length,\n };\n}\n","import { sameOutput } from \"../../evaluation/same-output.js\";\nimport { z } from \"zod\";\nimport { createHash } from \"node:crypto\";\nimport { readFile } from \"node:fs/promises\";\nimport { dirname, relative, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { createParser } from \"@matchbox-ai/core/runtime\";\nimport { readSequenceArtifact, tokenize } from \"@matchbox-ai/core/internal\";\nimport type { DatasetExample } from \"@matchbox-ai/core\";\nimport type { MatchboxParser, SequenceDecoder } from \"@matchbox-ai/core/runtime\";\nimport type { loadProject } from \"../../load-project.js\";\nimport { packageModel } from \"../../packaging/package-model.js\";\nimport { fitSequence } from \"./fit-sequence.js\";\nimport { evaluateSequence as evaluate } from \"../../evaluation/evaluate-sequence.js\";\nimport type { SequenceRecipe } from \"./types.js\";\nconst hash = (value: string) => createHash(\"sha256\").update(value).digest(\"hex\");\nexport async function runSequence(\n command: \"train\" | \"eval\",\n project: Awaited<ReturnType<typeof loadProject>>,\n progress?: (epoch: number, loss: number) => void,\n) {\n const { task, config } = project;\n const root = project.root;\n const sequence = config.sequence;\n if (!sequence) {\n throw new Error(\"A sequence pipeline needs both recipe and decoder modules.\");\n }\n const savedArtifact =\n command === \"eval\"\n ? readSequenceArtifact(JSON.parse(await readFile(project.output, \"utf8\")))\n : null;\n const decoderPath = savedArtifact\n ? resolve(dirname(project.output), savedArtifact.decoderModule)\n : resolve(root, sequence.decoder);\n const decode: SequenceDecoder = (await import(pathToFileURL(decoderPath).href)).default;\n const evaluateSequence = (\n parser: MatchboxParser<unknown>,\n examples: readonly DatasetExample<unknown>[],\n ) =>\n evaluate(parser, examples, (value) => task.validateOutput(value).success).finally(() => {\n if (\"dispose\" in parser && typeof parser.dispose === \"function\") {\n parser.dispose();\n }\n });\n const parser = (artifact: unknown) => createParser(artifact, task, decode);\n if (savedArtifact) {\n return { evaluation: await evaluateSequence(parser(savedArtifact), project.evaluation) };\n }\n const recipe: SequenceRecipe = (await import(pathToFileURL(resolve(root, sequence.recipe)).href))\n .default;\n const modulePath = (path: string) => {\n const value = relative(dirname(project.output), path).replaceAll(\"\\\\\", \"/\");\n return value.startsWith(\".\") ? value : `./${value}`;\n };\n const rejections = z\n .array(z.strictObject({ input: z.string(), output: z.null() }))\n .parse(recipe.rejections ?? []);\n const training = [...project.train, ...rejections];\n const heldOut = new Set(\n [...project.validation, ...project.evaluation].map((row) => row.input.trim().toLowerCase()),\n );\n for (const example of training) {\n if (!task.validateInput(example.input).success) {\n throw new Error(`Invalid training input: ${example.input}`);\n }\n if (heldOut.has(example.input.trim().toLowerCase())) {\n throw new Error(`Training input overlaps evaluation: ${example.input}`);\n }\n const tokens = tokenize(example.input, recipe.tokenizer);\n const labels = recipe.annotate(example, tokens);\n const value = decode(\n tokens.map((token, index) => ({ ...token, label: labels[index] ?? \"O\", confidence: 1 })),\n example.input,\n );\n if (!sameOutput(value, example.output)) {\n throw new Error(\n `Training annotations do not decode to the supplied output: ${example.input}`,\n );\n }\n }\n const started = performance.now();\n const fit = await fitSequence(\n training,\n recipe,\n {\n taskMetadata: task.toJSON(),\n taskModule: modulePath(project.taskPath),\n decoderModule: modulePath(resolve(root, sequence.decoder)),\n },\n project.validation.map((row) => row.input),\n progress,\n );\n const validation = await evaluateSequence(parser(fit.model), project.validation);\n const bytes = Buffer.byteLength(JSON.stringify(fit.model));\n if (validation.exactAccuracy < config.minAccuracy || bytes > config.maxBytes) {\n throw new Error(\n `Sequence model failed validation/size requirements (${validation.exactAccuracy}, ${bytes} bytes). ${JSON.stringify(validation.failures.slice(0, 10))}`,\n );\n }\n const challenges = config.challenges\n ? z\n .array(z.strictObject({ input: z.string(), output: z.null() }))\n .min(1)\n .parse(JSON.parse(await readFile(resolve(root, config.challenges), \"utf8\")))\n : null;\n const report = {\n formatVersion: 2,\n architecture: fit.model.architecture,\n backend: \"Burn native CPU\",\n seed: 42,\n artifactSha256: hash(JSON.stringify(fit.model)),\n bytes,\n parameters: fit.parameters,\n datasetSha256: project.sources.map((source) => ({\n source: source.source,\n sha256: hash(source.text),\n })),\n examples: {\n train: project.train.length,\n rejections: recipe.rejections?.length ?? 0,\n validation: project.validation.length,\n eval: project.evaluation.length,\n },\n supervisionSha256: hash(\n JSON.stringify(\n training.map((row) => ({\n ...row,\n labels: recipe.annotate(row, tokenize(row.input, recipe.tokenizer)),\n })),\n ),\n ),\n supervisedTokens: fit.supervisedTokens,\n loss: fit.history,\n exportParity: fit.parity,\n validation,\n evaluation: await evaluateSequence(parser(fit.model), project.evaluation),\n challenges: challenges ? await evaluateSequence(parser(fit.model), challenges) : null,\n trainingMs: performance.now() - started,\n notes:\n \"Float32 weights in a Burn binary record, base64-encoded in the artifact. Validation gates export. Eval labels do not influence selection. Scores are uncalibrated.\",\n };\n await packageModel(project.output, fit.model, report);\n return { report, output: project.output };\n}\n"],"mappings":"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"}
|
package/dist/train.d.ts
CHANGED
|
@@ -34,23 +34,11 @@ export declare function train(target: string, options?: {
|
|
|
34
34
|
}[];
|
|
35
35
|
examples: {
|
|
36
36
|
train: number;
|
|
37
|
+
rejections: number;
|
|
37
38
|
validation: number;
|
|
38
39
|
eval: number;
|
|
39
40
|
};
|
|
40
|
-
|
|
41
|
-
examples: number;
|
|
42
|
-
invalidOutputRate: number;
|
|
43
|
-
exactAccuracy: number;
|
|
44
|
-
accepted: number;
|
|
45
|
-
acceptedAccuracy: number | null;
|
|
46
|
-
abstentionRate: number;
|
|
47
|
-
correctAbstentions: number;
|
|
48
|
-
failures: {
|
|
49
|
-
input: string;
|
|
50
|
-
expected: unknown;
|
|
51
|
-
actual: unknown;
|
|
52
|
-
}[];
|
|
53
|
-
} | null;
|
|
41
|
+
supervisionSha256: string;
|
|
54
42
|
supervisedTokens: number;
|
|
55
43
|
loss: number[];
|
|
56
44
|
exportParity: {
|
|
@@ -72,49 +60,7 @@ export declare function train(target: string, options?: {
|
|
|
72
60
|
actual: unknown;
|
|
73
61
|
}[];
|
|
74
62
|
};
|
|
75
|
-
|
|
76
|
-
examples: number;
|
|
77
|
-
invalidOutputRate: number;
|
|
78
|
-
exactAccuracy: number;
|
|
79
|
-
accepted: number;
|
|
80
|
-
acceptedAccuracy: number | null;
|
|
81
|
-
abstentionRate: number;
|
|
82
|
-
correctAbstentions: number;
|
|
83
|
-
failures: {
|
|
84
|
-
input: string;
|
|
85
|
-
expected: unknown;
|
|
86
|
-
actual: unknown;
|
|
87
|
-
}[];
|
|
88
|
-
};
|
|
89
|
-
untrained: {
|
|
90
|
-
examples: number;
|
|
91
|
-
invalidOutputRate: number;
|
|
92
|
-
exactAccuracy: number;
|
|
93
|
-
accepted: number;
|
|
94
|
-
acceptedAccuracy: number | null;
|
|
95
|
-
abstentionRate: number;
|
|
96
|
-
correctAbstentions: number;
|
|
97
|
-
failures: {
|
|
98
|
-
input: string;
|
|
99
|
-
expected: unknown;
|
|
100
|
-
actual: unknown;
|
|
101
|
-
}[];
|
|
102
|
-
};
|
|
103
|
-
float: {
|
|
104
|
-
examples: number;
|
|
105
|
-
invalidOutputRate: number;
|
|
106
|
-
exactAccuracy: number;
|
|
107
|
-
accepted: number;
|
|
108
|
-
acceptedAccuracy: number | null;
|
|
109
|
-
abstentionRate: number;
|
|
110
|
-
correctAbstentions: number;
|
|
111
|
-
failures: {
|
|
112
|
-
input: string;
|
|
113
|
-
expected: unknown;
|
|
114
|
-
actual: unknown;
|
|
115
|
-
}[];
|
|
116
|
-
};
|
|
117
|
-
quantized: {
|
|
63
|
+
evaluation: {
|
|
118
64
|
examples: number;
|
|
119
65
|
invalidOutputRate: number;
|
|
120
66
|
exactAccuracy: number;
|
|
@@ -142,20 +88,6 @@ export declare function train(target: string, options?: {
|
|
|
142
88
|
actual: unknown;
|
|
143
89
|
}[];
|
|
144
90
|
} | null;
|
|
145
|
-
baseline: {
|
|
146
|
-
examples: number;
|
|
147
|
-
invalidOutputRate: number;
|
|
148
|
-
exactAccuracy: number;
|
|
149
|
-
accepted: number;
|
|
150
|
-
acceptedAccuracy: number | null;
|
|
151
|
-
abstentionRate: number;
|
|
152
|
-
correctAbstentions: number;
|
|
153
|
-
failures: {
|
|
154
|
-
input: string;
|
|
155
|
-
expected: unknown;
|
|
156
|
-
actual: unknown;
|
|
157
|
-
}[];
|
|
158
|
-
} | null;
|
|
159
91
|
trainingMs: number;
|
|
160
92
|
notes: string;
|
|
161
93
|
};
|
|
@@ -198,35 +130,7 @@ export declare function train(target: string, options?: {
|
|
|
198
130
|
actual: unknown;
|
|
199
131
|
}[];
|
|
200
132
|
};
|
|
201
|
-
|
|
202
|
-
examples: number;
|
|
203
|
-
invalidOutputRate: number;
|
|
204
|
-
exactAccuracy: number;
|
|
205
|
-
accepted: number;
|
|
206
|
-
acceptedAccuracy: number | null;
|
|
207
|
-
abstentionRate: number;
|
|
208
|
-
correctAbstentions: number;
|
|
209
|
-
failures: {
|
|
210
|
-
input: string;
|
|
211
|
-
expected: unknown;
|
|
212
|
-
actual: unknown;
|
|
213
|
-
}[];
|
|
214
|
-
};
|
|
215
|
-
float: {
|
|
216
|
-
examples: number;
|
|
217
|
-
invalidOutputRate: number;
|
|
218
|
-
exactAccuracy: number;
|
|
219
|
-
accepted: number;
|
|
220
|
-
acceptedAccuracy: number | null;
|
|
221
|
-
abstentionRate: number;
|
|
222
|
-
correctAbstentions: number;
|
|
223
|
-
failures: {
|
|
224
|
-
input: string;
|
|
225
|
-
expected: unknown;
|
|
226
|
-
actual: unknown;
|
|
227
|
-
}[];
|
|
228
|
-
};
|
|
229
|
-
untrained: {
|
|
133
|
+
evaluation: {
|
|
230
134
|
examples: number;
|
|
231
135
|
invalidOutputRate: number;
|
|
232
136
|
exactAccuracy: number;
|
|
@@ -240,34 +144,6 @@ export declare function train(target: string, options?: {
|
|
|
240
144
|
actual: unknown;
|
|
241
145
|
}[];
|
|
242
146
|
};
|
|
243
|
-
untrainedUngated: {
|
|
244
|
-
examples: number;
|
|
245
|
-
invalidOutputRate: number;
|
|
246
|
-
exactAccuracy: number;
|
|
247
|
-
accepted: number;
|
|
248
|
-
acceptedAccuracy: number | null;
|
|
249
|
-
abstentionRate: number;
|
|
250
|
-
correctAbstentions: number;
|
|
251
|
-
failures: {
|
|
252
|
-
input: string;
|
|
253
|
-
expected: unknown;
|
|
254
|
-
actual: unknown;
|
|
255
|
-
}[];
|
|
256
|
-
};
|
|
257
|
-
baseline: {
|
|
258
|
-
examples: number;
|
|
259
|
-
invalidOutputRate: number;
|
|
260
|
-
exactAccuracy: number;
|
|
261
|
-
accepted: number;
|
|
262
|
-
acceptedAccuracy: number | null;
|
|
263
|
-
abstentionRate: number;
|
|
264
|
-
correctAbstentions: number;
|
|
265
|
-
failures: {
|
|
266
|
-
input: string;
|
|
267
|
-
expected: unknown;
|
|
268
|
-
actual: unknown;
|
|
269
|
-
}[];
|
|
270
|
-
} | null;
|
|
271
147
|
trainingMs: number;
|
|
272
148
|
notes: string;
|
|
273
149
|
};
|
package/dist/train.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"train.d.ts","sourceRoot":"","sources":["../src/train.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,wBAAsB,KAAK,CACzB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAAO
|
|
1
|
+
{"version":3,"file":"train.d.ts","sourceRoot":"","sources":["../src/train.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,wBAAsB,KAAK,CACzB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAKrE"}
|
package/dist/types.d.ts
CHANGED
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB"}
|
package/docs/README.md
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
# Matchbox documentation
|
|
2
2
|
|
|
3
|
-
Start with [
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
Start with [Getting started](getting-started.md) to add a task, train a model, and import it into an existing app.
|
|
4
|
+
|
|
5
|
+
- [CLI reference](cli.md) covers every command and option.
|
|
6
|
+
- [API reference](reference/README.md) indexes the public contracts by package.
|
|
7
|
+
- [Training](training.md) walks through fitting and exporting a model.
|
|
8
|
+
- [Training pipelines](pipelines.md) explains the two learning strategies and their limits.
|
|
9
|
+
- [Project structure](project-structure.md) maps authored files and generated artifacts.
|
|
10
|
+
- [Evaluation](evaluation.md) explains validation gates, test data, and uncertainty.
|
|
11
|
+
- [Example results](example-evaluation.md) reports harder cases, coverage, and remaining failures.
|
|
12
|
+
- [Money](examples/money.md) and [time](examples/time.md) show token recognition with explicit application-owned decoding.
|
|
13
|
+
|
|
14
|
+
Matchbox currently accepts string inputs. Core owns validation and browser integration; train owns build-time orchestration; Burn owns training and execution.
|
package/docs/agent-skill.md
CHANGED
|
@@ -7,16 +7,20 @@ description: Author, train, evaluate, and integrate tiny browser-local Matchbox
|
|
|
7
7
|
|
|
8
8
|
Read the installed package docs/README.md first, then project-structure.md, pipelines.md, evaluation.md, and primitives/README.md. In the framework repository, use root docs/. Match examples to the installed version.
|
|
9
9
|
|
|
10
|
-
1. Inspect parser
|
|
10
|
+
1. Inspect parser, pipeline, recipe, and decode entry points (`X.ts` or `X/X.ts`), data/train.jsonl, and independent evals before changing behavior.
|
|
11
11
|
2. Keep the output schema focused on valid application values. Make representation and supervision choices explicit in pipeline.ts or imported helpers.
|
|
12
12
|
3. Use fieldClassifier for finite output domains and tokenClassifier for explicitly supervised token recognition. Explain their limits; do not promise unseen numeric outputs from a finite classifier.
|
|
13
13
|
4. Keep domain dictionaries and normalizers application-owned and visible. Never insert one silently to make an evaluation pass.
|
|
14
14
|
5. Fit preprocessing only on training data. Put data generators in project-level scripts/ and preserve validation/test fixtures. Add meaningful held-out compositions and negative cases independently of model fitting.
|
|
15
15
|
6. Use the CLI from matchbox-ai or programmatic train from @matchbox-ai/train to apply validation gates, measure results, and package weights. Report failures honestly. Never fabricate benchmark figures.
|
|
16
16
|
7. Distinguish uncertain interpretation from invalid user input. Confidence is currently uncalibrated; inspect diagnostics before attributing uncertainty to one cause.
|
|
17
|
-
8. Import generated artifacts in the app and use @matchbox-ai/core/react when needed. Keep @matchbox-ai/train and native
|
|
18
|
-
9. Let
|
|
17
|
+
8. Import generated artifacts in the app and use @matchbox-ai/core/react when needed. Keep @matchbox-ai/train and native Burn outside browser code.
|
|
18
|
+
9. Let Burn serialize, load, and execute models. Do not implement custom inference kernels or a backend-selection framework. Verify native-to-browser export parity and benchmark the shipped Burn runtime. Label emulated mobile measurements accurately.
|
|
19
19
|
|
|
20
20
|
Scope changes to existing, documented primitives. Propose a new primitive separately with its contract, limitations, and evaluation evidence. Preserve authored code and tests when reorganizing folders.
|
|
21
21
|
|
|
22
|
-
Matchbox owns authoring, workflows, evals, packaging, validation, and typed results.
|
|
22
|
+
Matchbox owns authoring, workflows, evals, packaging, validation, and typed results. Burn owns model execution. A schema must never silently select numeric encodings or domain normalizers.
|
|
23
|
+
|
|
24
|
+
## Task layout
|
|
25
|
+
|
|
26
|
+
Use `X.ts` or `X/X.ts` for parser, pipeline, recipe, and decode. Keep helper files with their owning module. Shared domain code belongs in a named task-level folder. No task or helper index barrel is required. `tokenClassifier()` discovers recipe and decode; explicit path overrides are relative to the task root. Never keep both entry forms. Retrain after relocating authored modules because artifacts reference concrete source paths.
|