@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,56 @@
|
|
|
1
|
+
# Runtime API
|
|
2
|
+
|
|
3
|
+
## MatchboxParser and ParseResult
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import type { MatchboxParser, ParseResult } from "@matchbox-ai/core/runtime";
|
|
7
|
+
|
|
8
|
+
type ParseResult<T> =
|
|
9
|
+
| { status: "ok"; value: T; confidence: number }
|
|
10
|
+
| { status: "uncertain"; value: null; confidence: number; reason: string };
|
|
11
|
+
|
|
12
|
+
interface MatchboxParser<T> {
|
|
13
|
+
load?(): Promise<void>;
|
|
14
|
+
parse(input: string): Promise<ParseResult<T>>;
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The generated module exports a parser. `parse` initializes it when necessary and returns schema-validated output or uncertainty. Confidence is an uncalibrated score, not a probability of correctness. Unknown vocabulary, low scores, or an undecodable prediction can cause uncertainty. A string that fails the task's input constraints also returns uncertainty; load failures reject the promise.
|
|
19
|
+
|
|
20
|
+
## createParser
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { createParser } from "@matchbox-ai/core/runtime";
|
|
24
|
+
const parser = createParser(artifact, task, decode);
|
|
25
|
+
await parser.load();
|
|
26
|
+
const result = await parser.parse("for 90 minutes");
|
|
27
|
+
parser.dispose();
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
| Argument | Type | Behavior |
|
|
31
|
+
| ---------- | ---------------------------- | -------------------------------------------------------- |
|
|
32
|
+
| `artifact` | `unknown` | Parsed Matchbox model artifact, validated by the loader. |
|
|
33
|
+
| `task` | `ParserDefinition<Output>` | Must match the artifact's serialized task metadata. |
|
|
34
|
+
| `decode` | `SequenceDecoder`, optional. | Required for token models; omit for field models. |
|
|
35
|
+
|
|
36
|
+
Returns `MatchboxParser<z.output<Output>>` plus required `load(): Promise<void>` and `dispose(): void`. Most applications should import the generated module instead of calling this factory.
|
|
37
|
+
|
|
38
|
+
`load` caches initialization and uses Burn WASM CPU. Failed initialization can be retried. `dispose` releases weights; calls after disposal reject. The React hook does not dispose shared module instances on unmount.
|
|
39
|
+
|
|
40
|
+
Malformed artifacts, schema mismatches, and a missing token decoder throw during parser creation. No runtime network API or API key is required. Your bundler may fetch the model's static chunks during loading.
|
|
41
|
+
|
|
42
|
+
## compileClauses
|
|
43
|
+
|
|
44
|
+
`compileClauses(input, recognize)` is a filter-specific helper. `recognize(clause)` returns `{ value: Predicate | null, confidence: number }`. It returns `{ value: FilterExpression | null, confidence: number }` with the minimum clause confidence.
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
interface Predicate {
|
|
48
|
+
field: string;
|
|
49
|
+
operator: string;
|
|
50
|
+
value: string | number | boolean;
|
|
51
|
+
}
|
|
52
|
+
type FilterExpression =
|
|
53
|
+
Predicate | { and: Predicate[] } | { or: (Predicate | { and: Predicate[] })[] };
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
It splits explicit `and`/`or` clauses with AND precedence. Empty input, input over 500 characters, more than eight clauses, parentheses, semicolons, and newlines return `null` with confidence zero. Implicit conjunctions and general recursive ASTs are unsupported. It never generates SQL.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Token supervision and decoding
|
|
2
|
+
|
|
3
|
+
Use a token pipeline when the model should identify parts of the input and application code should assemble the result.
|
|
4
|
+
|
|
5
|
+
## Why separate modules?
|
|
6
|
+
|
|
7
|
+
The token pipeline discovers default-exported recipe and decoder modules by convention. Use recipe.ts or recipe/recipe.ts, and decode.ts or decode/decode.ts. The training process loads the recipe; the generated browser wrapper imports the decoder directly. This keeps training annotations and training dependencies out of the application bundle.
|
|
8
|
+
|
|
9
|
+
The recipe file is an authoring convention of the current API, not a requirement of machine learning. The decoder is an application-owned runtime dependency. Keep its imports browser-safe, including any number-conversion helpers. The [money example](../examples/money.md) exposes these files separately so you can inspect both learned recognition and authored arithmetic.
|
|
10
|
+
|
|
11
|
+
## SequenceRecipe
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import type { SequenceRecipe } from "@matchbox-ai/train";
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
| Property | Type | Meaning |
|
|
18
|
+
| ----------- | -------------------------------------------------- | --------------------------------------------------- |
|
|
19
|
+
| `tokenizer` | `"words"` or `"characters"` | Chooses token boundaries. |
|
|
20
|
+
| `readout` | `"all"` or `"last"` | Predicts at every position or the final position. |
|
|
21
|
+
| `labels` | `readonly string[]` | The label vocabulary. |
|
|
22
|
+
| `annotate` | `(example, tokens) => readonly (string or null)[]` | Returns one label or unsupervised `null` per token. |
|
|
23
|
+
|
|
24
|
+
`example` is `DatasetExample<unknown>`; `tokens` is `readonly Token[]`. The recipe is a default export. Annotation length must equal token count, and labels must belong to the vocabulary. It runs on training examples only. For every training row, Matchbox checks that the supplied labels decode to its expected output.
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
const recipe: SequenceRecipe = {
|
|
28
|
+
tokenizer: "words",
|
|
29
|
+
readout: "all",
|
|
30
|
+
labels: ["O", "AMOUNT", "MINUTE"],
|
|
31
|
+
annotate(example) {
|
|
32
|
+
const labels = annotations[example.input];
|
|
33
|
+
if (!labels) throw new Error(`Missing annotation: ${example.input}`);
|
|
34
|
+
return labels;
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
export default recipe;
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Here `annotations` is an application-owned map aligned to the tokens in each input. `O` is an ordinary label used by examples for neutral tokens, not a framework normalizer.
|
|
41
|
+
|
|
42
|
+
## tokenize
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { tokenize } from "@matchbox-ai/train";
|
|
46
|
+
const tokens = tokenize("for 90 minutes", "words");
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`tokenize(input: string, mode: "words" | "characters"): Token[]` is shared with annotation generators. Word mode separates numbers, letter runs, and punctuation; numeric token keys become `<number>`. Character mode iterates Unicode code points. Original text and offsets are preserved.
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
interface Token {
|
|
53
|
+
text: string;
|
|
54
|
+
key: string;
|
|
55
|
+
start: number;
|
|
56
|
+
end: number;
|
|
57
|
+
}
|
|
58
|
+
interface TaggedToken extends Token {
|
|
59
|
+
label: string;
|
|
60
|
+
confidence: number;
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Offsets are UTF-16 indices, with an exclusive end, suitable for `input.slice(start, end)`. Token types are exported from `@matchbox-ai/core/runtime`.
|
|
65
|
+
|
|
66
|
+
## OutputDecoder / SequenceDecoder
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import type { OutputDecoder } from "@matchbox-ai/train";
|
|
70
|
+
// The equivalent runtime type is SequenceDecoder from @matchbox-ai/core/runtime.
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The signature is `(tokens: readonly TaggedToken[], input: string) => unknown`. Return a candidate JSON value, or `null` when the recognition cannot be assembled. Matchbox validates the candidate with the task schema before returning it to the app.
|
|
74
|
+
|
|
75
|
+
Keep decoding synchronous and browser-safe. It owns numeric conversion, unit arithmetic, and structural consistency checks. Model recognition remains learned. Avoid consulting a hidden clock, network, or mutable application state; pass relative structures back to the application when context is needed.
|
|
76
|
+
|
|
77
|
+
## Rejection supervision and unknown tokens
|
|
78
|
+
|
|
79
|
+
`rejections?: readonly DatasetExample<null>[]` supplies training inputs for which the application cannot produce an answer. These are training rows, separate from evaluation challenges. `annotate` must label every supervised position, and the decoder must return `null` for the supplied labels. The money and time recipes use an application-owned `REJECT` label. Matchbox does not invent rejection rules or labels.
|
|
80
|
+
|
|
81
|
+
`tokenDropout?: number` accepts a probability from zero through 0.5 and defaults to zero. With a positive value, training adds one copy of each supervised window with token IDs independently replaced by the unknown ID at that probability. Padding is preserved. Masking and minibatch order use fixed seeds, and vocabulary construction only reads training data.
|
|
82
|
+
|
|
83
|
+
This explicitly trains an unknown embedding. Its exported model attempts recognition on unfamiliar tokens instead of automatically abstaining. The normal recognition threshold, decoder, and schema still apply. With zero dropout, unfamiliar vocabulary causes uncertainty with score zero, as before.
|
|
84
|
+
|
|
85
|
+
Masking loses information and can introduce contradictory supervision. It can reduce accuracy or cause confident wrong answers. Evaluate familiar regressions, unfamiliar contexts, and negative inputs separately before enabling it. A model that tolerates an unknown name may also overlook an unknown negation. Scores remain uncalibrated token recognition scores, not probabilities that the final answer is correct.
|
|
86
|
+
|
|
87
|
+
The generated report records rejection counts and hashes the actual token supervision as well as source datasets. These controls make training inspectable; they do not guarantee semantic correctness.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Training API
|
|
2
|
+
|
|
3
|
+
Import these APIs in Node/Bun tooling, outside browser entry points.
|
|
4
|
+
|
|
5
|
+
## train
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { train } from "@matchbox-ai/train";
|
|
9
|
+
const result = await train("money", {
|
|
10
|
+
onProgress(epoch, loss) {
|
|
11
|
+
console.log(epoch, loss);
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`train(target: string, options?: { onProgress?: (epoch: number, loss: number) => void })` resolves a task and returns a promise for `{ report, output }`. `output` is the packaged artifact path.
|
|
17
|
+
|
|
18
|
+
It validates the datasets and supervision, trains with native Burn, checks validation accuracy and artifact size, then packages. Configuration, annotation, schema, validation-gate, or size failures reject the promise. A low independent test score is reported after selection and does not undo a model that passed validation. Run CLI `eval` as a separate test gate.
|
|
19
|
+
|
|
20
|
+
Report format 2 contains `architecture`, `backend`, `seed`, `bytes`, `parameters`, `artifactSha256`, `datasetSha256`, split counts in `examples`, `loss`, `exportParity`, `trainingMs`, and evaluation results for `validation` and `evaluation`. Sequence reports additionally include `supervisedTokens` and `challenges`. These are measured build results, not browser latency.
|
|
21
|
+
|
|
22
|
+
## TrainingConfig
|
|
23
|
+
|
|
24
|
+
`TrainingConfig` is the optional task configuration type. See [every field and default](configuration.md).
|
|
25
|
+
|
|
26
|
+
Use [evaluate](evaluation.md) to score an existing parser without training it.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Vite integration
|
|
2
|
+
|
|
3
|
+
The generated `model.ts` wrapper works without a plugin. Use this optional plugin to import `.matchbox` files directly.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
// vite.config.ts
|
|
7
|
+
import { defineConfig } from "vite";
|
|
8
|
+
import { matchbox } from "@matchbox-ai/core/vite";
|
|
9
|
+
|
|
10
|
+
export default defineConfig({ plugins: [matchbox()] });
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`matchbox()` takes no arguments and returns a Vite plugin. It converts `.matchbox` artifact imports into typed runtime modules.
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import model from "./.matchbox/money/model.matchbox";
|
|
17
|
+
const result = await model.parse("twenty dollars");
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Train before starting Vite or building the app. The generated declaration accompanies the artifact; set `allowArbitraryExtensions: true` in your TypeScript configuration for these imports. Keep the schema and decoder at their expected relative paths.
|
package/docs/releases.md
CHANGED
|
@@ -5,19 +5,19 @@ Changesets versions and publishes three packages together:
|
|
|
5
5
|
| Package | Purpose |
|
|
6
6
|
| -------------------- | -------------------------------------------- |
|
|
7
7
|
| `@matchbox-ai/core` | Task contracts and browser runtime. |
|
|
8
|
-
| `@matchbox-ai/train` | Pipeline authoring and
|
|
8
|
+
| `@matchbox-ai/train` | Pipeline authoring and Burn training. |
|
|
9
9
|
| `matchbox-ai` | The CLI, scaffolding, and browser workbench. |
|
|
10
10
|
|
|
11
|
-
The repository root, apps, and examples remain private. Packages use the MIT license.
|
|
11
|
+
The repository root, apps, and examples remain private. Packages use the MIT license. Changesets maintains versions and internal dependency ranges.
|
|
12
12
|
|
|
13
13
|
## Normal workflow
|
|
14
14
|
|
|
15
15
|
1. Run `bun run changeset` alongside a package change and commit its Markdown file.
|
|
16
16
|
2. Merge the feature PR. GitHub Actions creates or updates a Version Packages PR with versions, changelogs, dependency ranges, and the Bun lockfile.
|
|
17
|
-
3. Merge the version PR when the release is ready. The release workflow runs the full checks and browser tests, packs
|
|
17
|
+
3. Merge the version PR when the release is ready. The release workflow builds every native target, tests installed package archives on each target, runs the full checks and browser tests, then packs and publishes to npm. Changesets creates package tags and GitHub releases.
|
|
18
18
|
4. Retry a failed publication with Run workflow on `release.yml` from main. Changesets checks the registry and skips already published versions.
|
|
19
19
|
|
|
20
|
-
The packages use a fixed version group while their APIs evolve. Internal dependency ranges are updated by Changesets. Run `bun run changeset status` to inspect the next release. Run `bun run release:pack` after
|
|
20
|
+
The packages use a fixed version group while their APIs evolve. Internal dependency ranges are updated by Changesets. Run `bun run changeset status` to inspect the next release. Run `bun run release:pack` after collecting all native artifacts to inspect publishable archives without publishing. A local build contains only the current platform; the release guard rejects incomplete distributions.
|
|
21
21
|
|
|
22
22
|
## npm and GitHub setup
|
|
23
23
|
|
|
@@ -31,19 +31,9 @@ For each npm package, configure a GitHub trusted publisher with:
|
|
|
31
31
|
- Environment: leave blank.
|
|
32
32
|
- Allowed action: enable direct `npm publish`.
|
|
33
33
|
|
|
34
|
-
The workflow uses Node 24 and npm OIDC authentication. No npm token is stored in GitHub. Only the publish job receives `id-token: write`; build and validation run separately. The
|
|
34
|
+
The workflow uses Node 24 and npm OIDC authentication. No npm token is stored in GitHub. Only the publish job receives `id-token: write`; build and validation run separately. The workflow enables provenance for public repositories.
|
|
35
35
|
|
|
36
|
-
Trusted publisher settings are configured
|
|
37
|
-
|
|
38
|
-
```sh
|
|
39
|
-
bun install --frozen-lockfile
|
|
40
|
-
bun run check
|
|
41
|
-
bun run test:browser
|
|
42
|
-
npm login
|
|
43
|
-
bun run release
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
Complete npm's authentication prompts, then configure the three trusted publishers for subsequent releases. Do not run `release` from a feature branch or publish version 0.0.0. Setting up this workflow does not itself claim npm names or configure their trusted publishers.
|
|
36
|
+
Trusted publisher settings are configured for the three existing npm packages. Bundled native binaries do not introduce additional package names or trusted publishers. Ordinary feature work does not publish packages.
|
|
47
37
|
|
|
48
38
|
The CLI declares its training and runtime dependencies so `bunx matchbox-ai init` can work before the app has any Matchbox packages installed. Scaffolding writes registry versions from the running release, preserving dependencies already present in the app.
|
|
49
39
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Repository design
|
|
2
2
|
|
|
3
|
-
Bun workspaces contain three framework packages. packages/core owns parser definitions, datasets,
|
|
3
|
+
Bun workspaces contain three framework packages. packages/core owns parser definitions, datasets, Burn model loading, React, and Vite integration. packages/train owns explicit pipeline primitives, native training, evaluation, and packaging. packages/cli owns the command-line workflow, scaffolding templates, and browser workbench. It calls the train and evaluate APIs from @matchbox-ai/train and project loading from @matchbox-ai/train/project. Cross-package imports use exports. Core/internal is reserved for framework implementation, while runtime exports stay application-facing.
|
|
4
4
|
|
|
5
5
|
Each examples/<example>/matchbox/<task> is independently trainable through the same CLI conventions. apps/playground hosts the React/Vite demo and imports generated artifacts from examples. apps/benchmarks measures browser inference separately. No training dependencies enter the playground browser graph.
|
|
6
6
|
|
|
7
|
-
Core source is organized under parser/, runtime/
|
|
7
|
+
Core source is organized under parser/, runtime/burn/, react/, vite/, dataset/, and internal/. Train source separates pipeline/, encoders/, models/, codecs/, evaluation/, packaging/, and project/. CLI source separates commands/ and workbench/, with the browser UI in packages/cli/workbench/. Public index.ts files only re-export. Existing token codecs remain application-owned.
|
|
8
8
|
|
|
9
9
|
Rolldown builds ESM and TypeScript emits declarations. Public docs are copied into each package during builds for coding-agent access; root docs/ remains the source of truth. The skill lives in skills/matchbox/SKILL.md. Generated weights and copied package docs remain ignored.
|
|
10
10
|
|
package/docs/runtime-backends.md
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Runtime execution
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
import { matchbox } from "@matchbox-ai/core/vite";
|
|
5
|
-
export default { plugins: [matchbox()] };
|
|
6
|
-
```
|
|
3
|
+
Matchbox uses Burn for native training and browser inference. The Rust workspace contains the shared model implementation, a Node-API adapter for training, and a WebAssembly adapter for inference. There is no public backend selector or handwritten JavaScript evaluator.
|
|
7
4
|
|
|
8
|
-
|
|
5
|
+
Both sequence parsers and structured-record classifiers export float32 Burn records. TypeScript handles input features, authored output transformations, validation and abstention. Native/WASM prediction checks verify serialization fidelity before export completes.
|
|
9
6
|
|
|
10
|
-
|
|
7
|
+
The runtime loads lazily on the first parse. A parser retains its loaded model and exposes dispose(). Shared generated modules live for the application lifetime; the Vite plugin disposes them on HMR. Explicitly created instances should be disposed by their owner. React hooks do not dispose shared model modules when one component unmounts.
|
|
11
8
|
|
|
12
|
-
|
|
9
|
+
Direct creation is available through @matchbox-ai/core/runtime. Imports are SSR-safe; browser loading uses a separate WASM asset. Node inference reads that asset from the installed package. Training dependencies stay out of browser entry points.
|
|
13
10
|
|
|
14
|
-
|
|
11
|
+
Artifact format 3 contains a base64-encoded Burn record. Regenerate earlier artifacts with matchbox-ai train. The report evaluates the trained artifact once under `evaluation`. Quantization is not implemented.
|
|
15
12
|
|
|
16
|
-
|
|
13
|
+
The training package bundles platform-specific native binaries. See [native package distribution](native-packages.md) for the verified support matrix. Repository contributors should follow [contributor setup](contributing.md).
|
|
17
14
|
|
|
18
|
-
|
|
15
|
+
apps/benchmarks measures loading and warm inference of the shipped runtime. Cold timing includes lazy WASM loading after the benchmark page itself has loaded. Mobile browser-test profiles emulate device settings on desktop hardware, not physical-phone performance.
|
package/docs/training.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Train a model
|
|
2
|
+
|
|
3
|
+
Training learns from your examples and writes a model you can import into your application. Run these commands from a project where you have already [added a task](getting-started.md).
|
|
4
|
+
|
|
5
|
+
## Prepare the task
|
|
6
|
+
|
|
7
|
+
A conventional task needs:
|
|
8
|
+
|
|
9
|
+
| File | Purpose |
|
|
10
|
+
| ------------------------ | ---------------------------------------------------------- |
|
|
11
|
+
| `parser.ts` | Defines valid input and output. |
|
|
12
|
+
| `pipeline.ts` | Chooses a learning strategy and export requirements. |
|
|
13
|
+
| `data/train.jsonl` | Supplies the examples used to fit the model. |
|
|
14
|
+
| `evals/validation.jsonl` | Checks whether the fitted model meets export requirements. |
|
|
15
|
+
| `evals/test.jsonl` | Measures the selected model independently. |
|
|
16
|
+
|
|
17
|
+
[Choose a pipeline](pipelines.md) based on the task. A field classifier learns finite output values. A token classifier also needs a [recipe and decoder](reference/supervision.md) with aligned token annotations.
|
|
18
|
+
|
|
19
|
+
Write validation and test examples independently of training data. Keep them out of data generators. The loader rejects inputs shared across splits after trimming whitespace and folding case.
|
|
20
|
+
|
|
21
|
+
## Run training
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
bunx matchbox-ai train money
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
For epoch loss, add `--verbose`. For a machine-readable build report, add `--json`.
|
|
28
|
+
|
|
29
|
+
You can also open `bunx matchbox-ai dev money` and choose **Train model**. Both use the same local training workflow. Your app keeps its own dev server.
|
|
30
|
+
|
|
31
|
+
## What happens during a run
|
|
32
|
+
|
|
33
|
+
1. Matchbox validates task configuration, datasets, and any token annotations.
|
|
34
|
+
2. The selected strategy fits vocabulary, output domains, and weights from training data.
|
|
35
|
+
3. Burn trains the network locally. Matchbox checks the exported model against the native model.
|
|
36
|
+
4. Validation accuracy and model size gate packaging.
|
|
37
|
+
5. The independent test split is scored and included in the report.
|
|
38
|
+
|
|
39
|
+
A schema describes valid output. It does not generate training data or choose a numeric representation. Token decoders own explicit conversion and arithmetic.
|
|
40
|
+
|
|
41
|
+
## Set export requirements
|
|
42
|
+
|
|
43
|
+
Set acceptance values in `pipeline.ts`:
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
acceptance: { minAccuracy: 0.9, maxBytes: 30000 }
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`minAccuracy` is the required fraction of exact validation matches. `maxBytes` limits serialized model bytes, excluding the runtime and authored decoder. If either gate fails, training exits nonzero without exporting a replacement.
|
|
50
|
+
|
|
51
|
+
A model may pass validation and perform poorly on the independent test split. That test score is reported after packaging; run [`eval`](evaluation.md) as the separate test gate before shipping.
|
|
52
|
+
|
|
53
|
+
## Use the artifacts
|
|
54
|
+
|
|
55
|
+
A successful run writes:
|
|
56
|
+
|
|
57
|
+
```text
|
|
58
|
+
.matchbox/money/
|
|
59
|
+
model.matchbox
|
|
60
|
+
model.ts
|
|
61
|
+
model.d.matchbox.ts
|
|
62
|
+
report.json
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Import `model.ts` in your app. The wrapper references the matching task and decoder, so keep them together when deploying. Generated artifacts are ignored by Git; train before your app build or restore an evaluated artifact and its matching authored modules.
|
|
66
|
+
|
|
67
|
+
The report records accuracy, size, loss, export parity, and dataset hashes. It does not measure browser latency. Continue with [Evaluate a model](evaluation.md) to assess the result, uncertainty, and speed.
|
|
68
|
+
|
|
69
|
+
## Retrain after changes
|
|
70
|
+
|
|
71
|
+
Edit training examples and, for token models, their annotations. Run training again, then evaluate. Preserve test inputs even when the model gets them wrong.
|
|
72
|
+
|
|
73
|
+
For programmatic training, see [`train`](reference/training.md). For path overrides, see [configuration](reference/configuration.md).
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Website deployment
|
|
2
|
+
|
|
3
|
+
The `matchbox` project in the Boolean Industries Vercel team is connected to `alexpatow/matchbox`. Pull requests receive preview deployments, and `main` is the production branch.
|
|
4
|
+
|
|
5
|
+
The project root is the repository root. `vercel.json` installs dependencies with the frozen Bun lockfile, builds the packages, trains the example models, and builds the React/Vite website in `apps/playground`. Vercel serves `apps/playground/dist` as static files. Training runs only during the build; inference runs in the browser.
|
|
6
|
+
|
|
7
|
+
Installation and builds pin Bun 1.4.2 because Vercel's bundled Bun may not support the repository's lockfile version. Keep this pin aligned with `packageManager` in the root `package.json`. The Vercel project uses Node.js 24.
|
|
8
|
+
|
|
9
|
+
The SPA rewrite supports direct links to documentation routes. Generated model artifacts stay out of Git and are recreated for each build. No application secrets are required.
|
|
10
|
+
|
|
11
|
+
To link a local checkout, run `vercel link --project matchbox --scope boolean-industries`. The generated `.vercel/` directory is ignored. Use `vercel deploy --target preview --scope boolean-industries` for a manual preview.
|
package/native.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require("node-gyp-build")(__dirname);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@matchbox-ai/train",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -13,10 +13,15 @@
|
|
|
13
13
|
"docs",
|
|
14
14
|
"LICENSE",
|
|
15
15
|
"THIRD-PARTY-NOTICES.md",
|
|
16
|
-
"CHANGELOG.md"
|
|
16
|
+
"CHANGELOG.md",
|
|
17
|
+
"native.cjs",
|
|
18
|
+
"prebuilds"
|
|
17
19
|
],
|
|
18
20
|
"type": "module",
|
|
19
21
|
"sideEffects": false,
|
|
22
|
+
"imports": {
|
|
23
|
+
"#native": "./native.cjs"
|
|
24
|
+
},
|
|
20
25
|
"exports": {
|
|
21
26
|
".": {
|
|
22
27
|
"types": "./dist/index.d.ts",
|
|
@@ -38,17 +43,17 @@
|
|
|
38
43
|
"dev:types": "tsc -p tsconfig.build.json --watch"
|
|
39
44
|
},
|
|
40
45
|
"dependencies": {
|
|
41
|
-
"
|
|
46
|
+
"node-gyp-build": "4.8.4",
|
|
42
47
|
"zod": "4.6.3"
|
|
43
48
|
},
|
|
44
49
|
"devDependencies": {
|
|
45
|
-
"@matchbox-ai/core": "0.1
|
|
50
|
+
"@matchbox-ai/core": "0.2.1",
|
|
46
51
|
"@types/node": "22.20.2",
|
|
47
52
|
"rolldown": "1.2.8",
|
|
48
53
|
"typescript": "7.0.2"
|
|
49
54
|
},
|
|
50
55
|
"peerDependencies": {
|
|
51
|
-
"@matchbox-ai/core": "0.1
|
|
56
|
+
"@matchbox-ai/core": "0.2.1"
|
|
52
57
|
},
|
|
53
58
|
"engines": {
|
|
54
59
|
"bun": ">=1.4.2",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
//#region src/pipeline/define-pipeline.ts
|
|
3
|
-
const pipelineSchema = z.strictObject({
|
|
4
|
-
input: z.strictObject({ kind: z.literal("words") }).optional(),
|
|
5
|
-
prediction: z.discriminatedUnion("kind", [z.strictObject({ kind: z.literal("field-classifier") }), z.strictObject({
|
|
6
|
-
kind: z.literal("token-classifier"),
|
|
7
|
-
recipe: z.string().min(1),
|
|
8
|
-
decode: z.string().min(1)
|
|
9
|
-
})]),
|
|
10
|
-
acceptance: z.strictObject({
|
|
11
|
-
minAccuracy: z.number().min(0).max(1).optional(),
|
|
12
|
-
maxBytes: z.number().positive().optional()
|
|
13
|
-
}).optional()
|
|
14
|
-
}).superRefine((value, context) => {
|
|
15
|
-
if (value.prediction.kind === "field-classifier" && !value.input) context.addIssue({
|
|
16
|
-
code: "custom",
|
|
17
|
-
message: "fieldClassifier requires an explicit input encoder.",
|
|
18
|
-
path: ["input"]
|
|
19
|
-
});
|
|
20
|
-
if (value.prediction.kind === "token-classifier" && value.input) context.addIssue({
|
|
21
|
-
code: "custom",
|
|
22
|
-
message: "The sequence recipe owns tokenization; omit the duplicate input encoder.",
|
|
23
|
-
path: ["input"]
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
function definePipeline(pipeline) {
|
|
27
|
-
return pipelineSchema.parse(pipeline);
|
|
28
|
-
}
|
|
29
|
-
//#endregion
|
|
30
|
-
export { pipelineSchema as n, definePipeline as t };
|
|
31
|
-
|
|
32
|
-
//# sourceMappingURL=define-pipeline-Colvwt7u.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"define-pipeline-Colvwt7u.js","names":[],"sources":["../src/pipeline/define-pipeline.ts"],"sourcesContent":["import { z } from \"zod\";\nexport const pipelineSchema = z\n .strictObject({\n input: z.strictObject({ kind: z.literal(\"words\") }).optional(),\n prediction: z.discriminatedUnion(\"kind\", [\n z.strictObject({ kind: z.literal(\"field-classifier\") }),\n z.strictObject({\n kind: z.literal(\"token-classifier\"),\n recipe: z.string().min(1),\n decode: z.string().min(1),\n }),\n ]),\n acceptance: z\n .strictObject({\n minAccuracy: z.number().min(0).max(1).optional(),\n maxBytes: z.number().positive().optional(),\n })\n .optional(),\n })\n .superRefine((value, context) => {\n if (value.prediction.kind === \"field-classifier\" && !value.input)\n context.addIssue({\n code: \"custom\",\n message: \"fieldClassifier requires an explicit input encoder.\",\n path: [\"input\"],\n });\n if (value.prediction.kind === \"token-classifier\" && value.input)\n context.addIssue({\n code: \"custom\",\n message: \"The sequence recipe owns tokenization; omit the duplicate input encoder.\",\n path: [\"input\"],\n });\n });\nexport type Pipeline = z.infer<typeof pipelineSchema>;\nexport function definePipeline(pipeline: Pipeline): Pipeline {\n return pipelineSchema.parse(pipeline);\n}\n"],"mappings":";;AACA,MAAa,iBAAiB,EAC3B,aAAa;CACZ,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS;CAC7D,YAAY,EAAE,mBAAmB,QAAQ,CACvC,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,kBAAkB,EAAE,CAAC,GACtD,EAAE,aAAa;EACb,MAAM,EAAE,QAAQ,kBAAkB;EAClC,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;EACxB,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;CAC1B,CAAC,CACH,CAAC;CACD,YAAY,EACT,aAAa;EACZ,aAAa,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;EAC/C,UAAU,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS;CAC3C,CAAC,CAAC,CACD,SAAS;AACd,CAAC,CAAC,CACD,aAAa,OAAO,YAAY;CAC/B,IAAI,MAAM,WAAW,SAAS,sBAAsB,CAAC,MAAM,OACzD,QAAQ,SAAS;EACf,MAAM;EACN,SAAS;EACT,MAAM,CAAC,OAAO;CAChB,CAAC;CACH,IAAI,MAAM,WAAW,SAAS,sBAAsB,MAAM,OACxD,QAAQ,SAAS;EACf,MAAM;EACN,SAAS;EACT,MAAM,CAAC,OAAO;CAChB,CAAC;AACL,CAAC;AAEH,SAAgB,eAAe,UAA8B;CAC3D,OAAO,eAAe,MAAM,QAAQ;AACtC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"word-tokens.d.ts","sourceRoot":"","sources":["../../src/encoders/word-tokens.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,wBAAgB,UAAU;aACf,IAAI,EAAE,OAAO;EACvB"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"create-network.d.ts","sourceRoot":"","sources":["../../../src/models/sequence/create-network.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC5C,wBAAgB,aAAa,CAAC,cAAc,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,iBA0BvE"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"project-BP331m20.js","names":[],"sources":["../src/project/config.ts","../src/project/discover.ts","../src/project/artifact.ts"],"sourcesContent":["import { access, stat } from \"node:fs/promises\";\nimport { basename, dirname, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { z } from \"zod\";\nimport { pipelineSchema } from \"../pipeline/index.js\";\nexport const configSchema = z.strictObject({\n formatVersion: z.literal(1).default(1),\n task: z.string().default(\"./parser.ts\"),\n train: z.string().default(\"./data/train.jsonl\"),\n validation: z.string().default(\"./evals/validation.jsonl\"),\n eval: z.string().default(\"./evals/test.jsonl\"),\n output: z.string().endsWith(\".matchbox\"),\n minAccuracy: z.number().min(0).max(1).default(0.95),\n maxBytes: z.number().positive().default(64000),\n sequence: z.strictObject({ recipe: z.string(), decoder: z.string() }).optional(),\n baseline: z.string().optional(),\n challenges: z.string().optional(),\n});\nexport async function loadConfig(path: string) {\n const directory = (await stat(path)).isDirectory();\n const root = directory ? resolve(path) : dirname(resolve(path));\n const exists = (file: string) =>\n access(resolve(root, file)).then(\n () => true,\n () => false,\n );\n const configPath = directory ? resolve(root, \"matchbox.config.ts\") : resolve(path);\n const authored = await access(configPath).then(\n async () => (await import(pathToFileURL(configPath).href)).default,\n () => ({}),\n );\n let defaults: Record<string, unknown> = {};\n if (await exists(\"pipeline.ts\")) {\n const pipeline = pipelineSchema.parse(\n (await import(pathToFileURL(resolve(root, \"pipeline.ts\")).href)).default,\n );\n defaults = { ...pipeline.acceptance };\n if (pipeline.prediction.kind === \"token-classifier\")\n defaults.sequence = {\n recipe: pipeline.prediction.recipe,\n decoder: pipeline.prediction.decode,\n };\n } else if (!authored.sequence)\n throw new Error(\n `Missing ${resolve(root, \"pipeline.ts\")}. Author an explicit pipeline before training.`,\n );\n const config = configSchema.parse({\n output: resolve(root, \"../../.matchbox\", basename(root), \"model.matchbox\"),\n ...defaults,\n ...authored,\n });\n if (!config.baseline && (await exists(\"evals/baseline.ts\")))\n config.baseline = \"./evals/baseline.ts\";\n if (!config.challenges && (await exists(\"evals/challenges.json\")))\n config.challenges = \"./evals/challenges.json\";\n return { config, root };\n}\n","import { access, readdir, stat } from \"node:fs/promises\";\nimport { dirname, resolve } from \"node:path\";\nconst exists = (path: string) =>\n access(path).then(\n () => true,\n () => false,\n );\nasync function select(root: string): Promise<string | undefined> {\n if (await exists(resolve(root, \"parser.ts\"))) return root;\n if (await exists(resolve(root, \"matchbox.config.ts\"))) return resolve(root, \"matchbox.config.ts\");\n const directory = resolve(root, \"matchbox\");\n const entries = await readdir(directory, { withFileTypes: true }).catch(() => []);\n const tasks: string[] = [];\n for (const entry of entries)\n if (entry.isDirectory() && (await exists(resolve(directory, entry.name, \"parser.ts\"))))\n tasks.push(entry.name);\n tasks.sort();\n if (tasks.length > 1)\n throw new Error(\n `Choose a task: ${tasks.join(\", \")}. For example, matchbox-ai train ${tasks[0]}.`,\n );\n return tasks.length ? resolve(directory, tasks[0]!) : undefined;\n}\nexport async function discover(target?: string, cwd = process.cwd()): Promise<string> {\n if (target && (await exists(resolve(cwd, target)))) {\n const path = resolve(cwd, target);\n if ((await stat(path)).isFile()) return path;\n const selected = await select(path);\n if (selected) return selected;\n throw new Error(`No Matchbox task found in ${path}.`);\n }\n let root = resolve(cwd);\n for (;;) {\n if (target) {\n const task = resolve(root, \"matchbox\", target);\n if (await exists(resolve(task, \"parser.ts\"))) return task;\n } else {\n const selected = await select(root);\n if (selected) return selected;\n }\n const parent = dirname(root);\n if (parent === root)\n throw new Error(\n target\n ? `Task not found: ${target}.`\n : \"No Matchbox project found. Run matchbox-ai init <name>.\",\n );\n root = parent;\n }\n}\n","import { readFile } from \"node:fs/promises\";\nimport { dirname, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { createParser } from \"@matchbox-ai/core/runtime\";\nimport { readArtifact, tensorPredictor } from \"@matchbox-ai/core/internal\";\nimport type { SequenceDecoder } from \"@matchbox-ai/core/runtime\";\nimport type { ParserDefinition } from \"@matchbox-ai/core\";\nimport type { z } from \"zod\";\nimport { loadConfig } from \"./config.js\";\nexport async function loadArtifact(configPath: string) {\n const { config, root } = await loadConfig(configPath);\n const output = resolve(root, config.output);\n const text = await readFile(output, \"utf8\").catch(() => {\n throw new Error(`No readable model at ${output}. Run matchbox-ai train first.`);\n });\n const artifact = readArtifact(JSON.parse(text));\n const task: ParserDefinition<z.ZodType> = (\n await import(pathToFileURL(resolve(dirname(output), artifact.taskModule)).href)\n ).default;\n const decode: SequenceDecoder | undefined =\n artifact.decoderModule === null\n ? undefined\n : (await import(pathToFileURL(resolve(dirname(output), artifact.decoderModule)).href))\n .default;\n return {\n artifact,\n task,\n decode,\n parser: createParser(artifact, task, decode),\n inspect: async (input: string) => {\n const predictor = await tensorPredictor(artifact);\n try {\n if (artifact.kind === \"record-parser\") return predictor.record(input);\n const tokens = predictor.sequence(input);\n return { tokens, candidate: decode!(tokens, input) };\n } finally {\n predictor.dispose();\n }\n },\n output,\n config,\n root,\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"record-BTZ5LWHF.js","names":[],"sources":["../src/models/record/verify-export.ts","../src/models/record/fit.ts","../src/models/record/run.ts"],"sourcesContent":["import * as tf from \"@tensorflow/tfjs-node\";\nimport { recordFeatures, tensorPredictor } from \"@matchbox-ai/core/internal\";\nimport type { RecordArtifact } from \"@matchbox-ai/core/internal\";\n\nexport async function verifyExport(\n model: tf.LayersModel,\n artifact: RecordArtifact,\n probes: readonly string[],\n) {\n // Snapshot predictions on the native backend before loading the browser model.\n const native = probes.map((input) =>\n tf.tidy(() => {\n const logits = model.predict(\n tf.tensor2d([recordFeatures(input, artifact.vocabulary)]),\n ) as tf.Tensor;\n let offset = 0;\n return artifact.fields.map((field) => {\n const probabilities = Array.from(\n tf.softmax(tf.slice(logits, [0, offset], [1, field.values.length])).dataSync(),\n );\n offset += field.values.length;\n return probabilities;\n });\n }),\n );\n const predictor = await tensorPredictor(artifact);\n let maxConfidenceError = 0;\n let labelDisagreements = 0;\n try {\n probes.forEach((input, probeIndex) => {\n predictor.record(input).fields.forEach((prediction, index) => {\n const scores = native[probeIndex]![index]!;\n const maximum = Math.max(...scores);\n maxConfidenceError = Math.max(\n maxConfidenceError,\n Math.abs(maximum - prediction.confidence),\n );\n if (prediction.value !== artifact.fields[index]!.values[scores.indexOf(maximum)])\n labelDisagreements++;\n });\n });\n } finally {\n predictor.dispose();\n await tf.setBackend(\"tensorflow\");\n }\n if (labelDisagreements || maxConfidenceError > 1e-5)\n throw new Error(\"Serialized TensorFlow model disagrees with native predictions.\");\n return { examples: probes.length, labelDisagreements, maxConfidenceError };\n}\n","import { verifyExport } from \"./verify-export.js\";\nimport * as tf from \"@tensorflow/tfjs-node\";\nimport { recordFeatures, recordTokens, readRecordArtifact } from \"@matchbox-ai/core/internal\";\nimport type { RecordArtifact } from \"@matchbox-ai/core/internal\";\nimport type { DatasetExample, ParserMetadata } from \"@matchbox-ai/core\";\nexport async function fitRecord(\n examples: readonly DatasetExample<unknown>[],\n metadata: { taskModule: string; taskMetadata: ParserMetadata },\n probes: readonly string[],\n progress?: (epoch: number, loss: number) => void,\n) {\n const schema = metadata.taskMetadata.output;\n if (schema.type !== \"object\" || !schema.properties || schema.additionalProperties !== false)\n throw new Error(\n \"The default trainer currently supports strict flat objects with primitive field values. Use an explicit sequence pipeline for other shapes.\",\n );\n const fields = Object.keys(schema.properties).map((name) => {\n const values = [\n ...new Map(\n examples.map((row) => {\n const value = (row.output as Record<string, unknown>)[name];\n if (value !== null && ![\"number\", \"string\", \"boolean\"].includes(typeof value))\n throw new Error(`output.${name}: the default trainer requires primitive field values.`);\n return [JSON.stringify(value), value as string | number | boolean | null] as const;\n }),\n ).entries(),\n ]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([, value]) => value);\n return { name, values };\n });\n const vocabulary = [...new Set(examples.flatMap((row) => recordTokens(row.input)))].sort();\n const outputs = fields.reduce((sum, field) => sum + field.values.length, 0);\n if (\n fields.length > 32 ||\n fields.some((field) => field.values.length > 256) ||\n vocabulary.length > 10000\n )\n throw new Error(\n \"Default trainer capacity exceeded. Use a custom pipeline for larger output domains.\",\n );\n await tf.setBackend(\"tensorflow\");\n await tf.ready();\n const model = tf.sequential({\n layers: [\n tf.layers.dense({\n inputShape: [vocabulary.length],\n units: 32,\n activation: \"tanh\",\n kernelInitializer: tf.initializers.glorotUniform({ seed: 42 }),\n }),\n tf.layers.dense({\n units: outputs,\n kernelInitializer: tf.initializers.glorotUniform({ seed: 43 }),\n }),\n ],\n });\n const optimizer = tf.train.adam(0.02);\n model.compile({\n optimizer,\n loss: (gold, logits) =>\n tf.tidy(() => {\n let offset = 0;\n const losses = fields.map((field) => {\n const count = field.values.length;\n const target = tf.slice(gold, [0, offset], [-1, count]);\n const scores = tf.slice(logits, [0, offset], [-1, count]);\n offset += count;\n return tf.neg(tf.sum(tf.mul(target, tf.logSoftmax(scores)), 1));\n });\n return tf.mean(tf.addN(losses));\n }),\n });\n const x = tf.tensor2d(examples.map((row) => recordFeatures(row.input, vocabulary)));\n const y = tf.tensor2d(\n examples.map((row) =>\n fields.flatMap((field) =>\n field.values.map((value) =>\n value === (row.output as Record<string, unknown>)[field.name] ? 1 : 0,\n ),\n ),\n ),\n );\n function exported(precision: \"int8\" | \"float32\"): RecordArtifact {\n const weights = model.getWeights().map((tensor, index) => {\n const values = Array.from(tensor.dataSync());\n const scale =\n precision === \"int8\"\n ? values.reduce((max, value) => Math.max(max, Math.abs(value)), 0) / 127 || 1\n : 1;\n return {\n name: model.weights[index]!.originalName,\n shape: tensor.shape,\n values: precision === \"int8\" ? values.map((value) => Math.round(value / scale)) : values,\n scale,\n };\n }) as RecordArtifact[\"weights\"];\n return readRecordArtifact({\n formatVersion: 2,\n kind: \"record-parser\",\n architecture: \"bag-of-words-mlp\",\n ...metadata,\n modelTopology: JSON.parse(model.toJSON() as string),\n decoderModule: null,\n fields,\n vocabulary,\n threshold: 0.75,\n precision,\n weights,\n });\n }\n const untrained = exported(\"float32\");\n const history: number[] = [];\n try {\n await model.fit(x, y, {\n epochs: 100,\n batchSize: 128,\n shuffle: false,\n verbose: 0,\n callbacks: {\n onEpochEnd(epoch, logs) {\n history.push(Number(logs?.loss));\n progress?.(epoch + 1, Number(logs?.loss));\n },\n },\n });\n const float = exported(\"float32\"),\n quantized = exported(\"int8\");\n const parity = await verifyExport(model, float, probes);\n return {\n float,\n quantized,\n untrained,\n history,\n parity,\n };\n } finally {\n x.dispose();\n y.dispose();\n model.dispose();\n optimizer.dispose();\n }\n}\n","import { createHash } from \"node:crypto\";\nimport { dirname, relative } from \"node:path\";\nimport { createParser } from \"@matchbox-ai/core/runtime\";\nimport type { RecordArtifact } from \"@matchbox-ai/core/internal\";\nimport type { loadProject } from \"../../load-project.js\";\nimport { packageModel } from \"../../packaging/package-model.js\";\nimport { evaluateSequence } from \"../../evaluation/evaluate-sequence.js\";\nimport { fitRecord } from \"./fit.js\";\nexport async function runRecord(\n project: Awaited<ReturnType<typeof loadProject>>,\n progress?: (epoch: number, loss: number) => void,\n) {\n const start = performance.now();\n const taskModule = relative(dirname(project.output), project.taskPath).replaceAll(\"\\\\\", \"/\");\n const fit = await fitRecord(\n project.train,\n {\n taskModule: taskModule.startsWith(\".\") ? taskModule : `./${taskModule}`,\n taskMetadata: project.task.toJSON(),\n },\n project.validation.map((row) => row.input),\n progress,\n );\n const evaluate = async (artifact: RecordArtifact, rows = project.evaluation) => {\n const parser = createParser(artifact, project.task);\n try {\n return await evaluateSequence(\n parser,\n rows,\n (value) => project.task.validateOutput(value).success,\n );\n } finally {\n parser.dispose();\n }\n };\n const validation = await evaluate(fit.quantized, project.validation);\n const bytes = Buffer.byteLength(JSON.stringify(fit.quantized));\n if (validation.exactAccuracy < project.config.minAccuracy || bytes > project.config.maxBytes)\n throw new Error(\n `Record model failed validation/size requirements (${validation.exactAccuracy}, ${bytes} bytes). ${JSON.stringify(validation.failures.slice(0, 10))}`,\n );\n const report = {\n formatVersion: 1,\n architecture: fit.quantized.architecture,\n backend: \"TensorFlow native CPU\",\n seed: 42,\n artifactSha256: createHash(\"sha256\").update(JSON.stringify(fit.quantized)).digest(\"hex\"),\n bytes,\n parameters: fit.quantized.weights.reduce((sum, weight) => sum + weight.values.length, 0),\n datasetSha256: project.sources.map((source) => ({\n source: source.source,\n sha256: createHash(\"sha256\").update(source.text).digest(\"hex\"),\n })),\n examples: {\n train: project.train.length,\n validation: project.validation.length,\n eval: project.evaluation.length,\n },\n loss: fit.history,\n exportParity: fit.parity,\n validation,\n quantized: await evaluate(fit.quantized),\n float: await evaluate(fit.float),\n untrained: await evaluate(fit.untrained),\n untrainedUngated: await evaluate({ ...fit.untrained, threshold: 0 }),\n baseline: project.baseline\n ? await evaluateSequence(\n project.baseline,\n project.evaluation,\n (value) => project.task.validateOutput(value).success,\n )\n : null,\n trainingMs: performance.now() - start,\n notes:\n \"Each field classifies values present in training data. Vocabulary and value domains are fitted only on training. There are no number dictionaries, aliases, normalization rules, or span annotations. Unknown tokens abstain. Bag-of-words ignores order. Confidence is uncalibrated. This model cannot emit unseen numeric values.\",\n };\n await packageModel(project.output, fit.quantized, report);\n return { report, output: project.output };\n}\n"],"mappings":"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"}
|
package/dist/run-Cwa7vD_T.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"run-Cwa7vD_T.js","names":[],"sources":["../src/load-project.ts","../src/run.ts"],"sourcesContent":["import { loadConfig } from \"./project/index.js\";\nimport { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { z } from \"zod\";\nimport { parseDatasets } from \"@matchbox-ai/core\";\nimport type { ParserDefinition } from \"@matchbox-ai/core\";\nimport type { MatchboxParser } from \"@matchbox-ai/core/runtime\";\nexport async function loadProject(path: string) {\n const { config, root } = await loadConfig(path);\n const taskPath = resolve(root, config.task);\n const task: ParserDefinition<z.ZodType> = (await import(pathToFileURL(taskPath).href)).default;\n const baseline: MatchboxParser<unknown> | null = config.baseline\n ? (await import(pathToFileURL(resolve(root, config.baseline)).href)).default\n : null;\n const sources = await Promise.all(\n [config.train, config.validation, config.eval].map(async (source) => ({\n source,\n text: await readFile(resolve(root, source), \"utf8\"),\n })),\n );\n const training = parseDatasets(task, { formatVersion: 1, train: sources[0]!, eval: sources[1]! });\n const evaluation = parseDatasets(task, {\n formatVersion: 1,\n train: sources[0]!,\n eval: sources[2]!,\n });\n for (const result of [training, evaluation])\n if (!result.success)\n throw new Error(\n result.issues\n .map((issue) => `${issue.source}:${issue.line} ${issue.path.join(\".\")}: ${issue.message}`)\n .join(\"\\n\"),\n );\n if (!training.success || !evaluation.success) throw new Error(\"Invalid datasets.\");\n const groups = [training.data.train, training.data.eval, evaluation.data.eval];\n const seen = new Map<string, number>();\n groups.forEach((group, split) =>\n group.forEach((row) => {\n const key = row.input.trim().toLowerCase();\n if (seen.has(key) && seen.get(key) !== split)\n throw new Error(`Input overlaps dataset splits: ${row.input}`);\n seen.set(key, split);\n }),\n );\n return {\n root,\n config,\n task,\n baseline,\n taskPath,\n output: resolve(root, config.output),\n sources,\n train: training.data.train,\n validation: training.data.eval,\n evaluation: evaluation.data.eval,\n };\n}\n","import { loadProject } from \"./load-project.js\";\nexport async function run(\n command: \"train\" | \"eval\",\n path: string,\n progress?: (epoch: number, loss: number) => void,\n) {\n const project = await loadProject(path);\n if (project.config.sequence) {\n const { runSequence } = await import(\"./models/sequence/index.js\");\n return runSequence(command, project, progress);\n }\n if (command !== \"train\")\n throw new Error(\"Use the eval CLI command to evaluate a saved artifact.\");\n const { runRecord } = await import(\"./models/record/index.js\");\n return runRecord(project, progress);\n}\n"],"mappings":";;;;;;;AAQA,eAAsB,YAAY,MAAc;CAC9C,MAAM,EAAE,QAAQ,SAAS,MAAM,WAAW,IAAI;CAC9C,MAAM,WAAW,QAAQ,MAAM,OAAO,IAAI;CAC1C,MAAM,QAAqC,MAAM,OAAO,cAAc,QAAQ,CAAC,CAAC,MAAA,CAAO;CACvF,MAAM,WAA2C,OAAO,YACnD,MAAM,OAAO,cAAc,QAAQ,MAAM,OAAO,QAAQ,CAAC,CAAC,CAAC,MAAA,CAAO,UACnE;CACJ,MAAM,UAAU,MAAM,QAAQ,IAC5B;EAAC,OAAO;EAAO,OAAO;EAAY,OAAO;CAAI,CAAC,CAAC,IAAI,OAAO,YAAY;EACpE;EACA,MAAM,MAAM,SAAS,QAAQ,MAAM,MAAM,GAAG,MAAM;CACpD,EAAE,CACJ;CACA,MAAM,WAAW,cAAc,MAAM;EAAE,eAAe;EAAG,OAAO,QAAQ;EAAK,MAAM,QAAQ;CAAI,CAAC;CAChG,MAAM,aAAa,cAAc,MAAM;EACrC,eAAe;EACf,OAAO,QAAQ;EACf,MAAM,QAAQ;CAChB,CAAC;CACD,KAAK,MAAM,UAAU,CAAC,UAAU,UAAU,GACxC,IAAI,CAAC,OAAO,SACV,MAAM,IAAI,MACR,OAAO,OACJ,KAAK,UAAU,GAAG,MAAM,OAAO,GAAG,MAAM,KAAK,GAAG,MAAM,KAAK,KAAK,GAAG,EAAE,IAAI,MAAM,SAAS,CAAC,CACzF,KAAK,IAAI,CACd;CACJ,IAAI,CAAC,SAAS,WAAW,CAAC,WAAW,SAAS,MAAM,IAAI,MAAM,mBAAmB;CACjF,MAAM,SAAS;EAAC,SAAS,KAAK;EAAO,SAAS,KAAK;EAAM,WAAW,KAAK;CAAI;CAC7E,MAAM,uBAAO,IAAI,IAAoB;CACrC,OAAO,SAAS,OAAO,UACrB,MAAM,SAAS,QAAQ;EACrB,MAAM,MAAM,IAAI,MAAM,KAAK,CAAC,CAAC,YAAY;EACzC,IAAI,KAAK,IAAI,GAAG,KAAK,KAAK,IAAI,GAAG,MAAM,OACrC,MAAM,IAAI,MAAM,kCAAkC,IAAI,OAAO;EAC/D,KAAK,IAAI,KAAK,KAAK;CACrB,CAAC,CACH;CACA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA,QAAQ,QAAQ,MAAM,OAAO,MAAM;EACnC;EACA,OAAO,SAAS,KAAK;EACrB,YAAY,SAAS,KAAK;EAC1B,YAAY,WAAW,KAAK;CAC9B;AACF;;;ACxDA,eAAsB,IACpB,SACA,MACA,UACA;CACA,MAAM,UAAU,MAAM,YAAY,IAAI;CACtC,IAAI,QAAQ,OAAO,UAAU;EAC3B,MAAM,EAAE,gBAAgB,MAAM,OAAO;EACrC,OAAO,YAAY,SAAS,SAAS,QAAQ;CAC/C;CACA,IAAI,YAAY,SACd,MAAM,IAAI,MAAM,wDAAwD;CAC1E,MAAM,EAAE,cAAc,MAAM,OAAO;CACnC,OAAO,UAAU,SAAS,QAAQ;AACpC"}
|