@matchbox-ai/train 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/LICENSE +21 -0
- package/README.md +3 -0
- package/THIRD-PARTY-NOTICES.md +17 -0
- package/dist/codecs/index.d.ts +2 -0
- package/dist/codecs/index.d.ts.map +1 -0
- package/dist/define-pipeline-Colvwt7u.js +32 -0
- package/dist/define-pipeline-Colvwt7u.js.map +1 -0
- package/dist/encoders/index.d.ts +3 -0
- package/dist/encoders/index.d.ts.map +1 -0
- package/dist/encoders/word-tokens.d.ts +5 -0
- package/dist/encoders/word-tokens.d.ts.map +1 -0
- package/dist/evaluate-sequence-ZCwyomzQ.js +42 -0
- package/dist/evaluate-sequence-ZCwyomzQ.js.map +1 -0
- package/dist/evaluation/evaluate-sequence.d.ts +17 -0
- package/dist/evaluation/evaluate-sequence.d.ts.map +1 -0
- package/dist/evaluation/same-output.d.ts +2 -0
- package/dist/evaluation/same-output.d.ts.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/load-project.d.ts +34 -0
- package/dist/load-project.d.ts.map +1 -0
- package/dist/models/index.d.ts +2 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/presets.d.ts +14 -0
- package/dist/models/presets.d.ts.map +1 -0
- package/dist/models/record/fit.d.ts +124 -0
- package/dist/models/record/fit.d.ts.map +1 -0
- package/dist/models/record/index.d.ts +2 -0
- package/dist/models/record/index.d.ts.map +1 -0
- package/dist/models/record/run.d.ts +115 -0
- package/dist/models/record/run.d.ts.map +1 -0
- package/dist/models/record/verify-export.d.ts +8 -0
- package/dist/models/record/verify-export.d.ts.map +1 -0
- package/dist/models/sequence/create-network.d.ts +3 -0
- package/dist/models/sequence/create-network.d.ts.map +1 -0
- package/dist/models/sequence/fit-sequence.d.ts +139 -0
- package/dist/models/sequence/fit-sequence.d.ts.map +1 -0
- package/dist/models/sequence/index.d.ts +3 -0
- package/dist/models/sequence/index.d.ts.map +1 -0
- package/dist/models/sequence/run-sequence.d.ts +162 -0
- package/dist/models/sequence/run-sequence.d.ts.map +1 -0
- package/dist/models/sequence/types.d.ts +10 -0
- package/dist/models/sequence/types.d.ts.map +1 -0
- package/dist/package-model-CPj0qnQs.js +18 -0
- package/dist/package-model-CPj0qnQs.js.map +1 -0
- package/dist/packaging/package-model.d.ts +3 -0
- package/dist/packaging/package-model.d.ts.map +1 -0
- package/dist/pipeline/define-pipeline.d.ts +20 -0
- package/dist/pipeline/define-pipeline.d.ts.map +1 -0
- package/dist/pipeline/index.d.ts +3 -0
- package/dist/pipeline/index.d.ts.map +1 -0
- package/dist/project/artifact.d.ts +129 -0
- package/dist/project/artifact.d.ts.map +1 -0
- package/dist/project/config.d.ts +37 -0
- package/dist/project/config.d.ts.map +1 -0
- package/dist/project/discover.d.ts +2 -0
- package/dist/project/discover.d.ts.map +1 -0
- package/dist/project/index.d.ts +4 -0
- package/dist/project/index.d.ts.map +1 -0
- package/dist/project/index.js +2 -0
- package/dist/project-BP331m20.js +125 -0
- package/dist/project-BP331m20.js.map +1 -0
- package/dist/record-BTZ5LWHF.js +205 -0
- package/dist/record-BTZ5LWHF.js.map +1 -0
- package/dist/run-Cwa7vD_T.js +72 -0
- package/dist/run-Cwa7vD_T.js.map +1 -0
- package/dist/run.d.ts +273 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/sequence-C_WfVGO1.js +251 -0
- package/dist/sequence-C_WfVGO1.js.map +1 -0
- package/dist/train.d.ts +276 -0
- package/dist/train.d.ts.map +1 -0
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -0
- package/docs/README.md +9 -0
- package/docs/agent-skill.md +22 -0
- package/docs/cli.md +54 -0
- package/docs/dataset-format.md +58 -0
- package/docs/end-to-end.md +3 -0
- package/docs/evaluation.md +9 -0
- package/docs/filter-research.md +63 -0
- package/docs/getting-started.md +88 -0
- package/docs/neural-training.md +3 -0
- package/docs/parser-api.md +86 -0
- package/docs/pipelines.md +30 -0
- package/docs/primitives/README.md +19 -0
- package/docs/project-structure.md +41 -0
- package/docs/react.md +16 -0
- package/docs/releases.md +50 -0
- package/docs/repository-design.md +11 -0
- package/docs/runtime-backends.md +18 -0
- package/package.json +57 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { n as pipelineSchema } from "./define-pipeline-Colvwt7u.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { readArtifact, tensorPredictor } from "@matchbox-ai/core/internal";
|
|
4
|
+
import { access, readFile, readdir, stat } from "node:fs/promises";
|
|
5
|
+
import { basename, dirname, resolve } from "node:path";
|
|
6
|
+
import { pathToFileURL } from "node:url";
|
|
7
|
+
import { createParser } from "@matchbox-ai/core/runtime";
|
|
8
|
+
//#region src/project/config.ts
|
|
9
|
+
const configSchema = z.strictObject({
|
|
10
|
+
formatVersion: z.literal(1).default(1),
|
|
11
|
+
task: z.string().default("./parser.ts"),
|
|
12
|
+
train: z.string().default("./data/train.jsonl"),
|
|
13
|
+
validation: z.string().default("./evals/validation.jsonl"),
|
|
14
|
+
eval: z.string().default("./evals/test.jsonl"),
|
|
15
|
+
output: z.string().endsWith(".matchbox"),
|
|
16
|
+
minAccuracy: z.number().min(0).max(1).default(.95),
|
|
17
|
+
maxBytes: z.number().positive().default(64e3),
|
|
18
|
+
sequence: z.strictObject({
|
|
19
|
+
recipe: z.string(),
|
|
20
|
+
decoder: z.string()
|
|
21
|
+
}).optional(),
|
|
22
|
+
baseline: z.string().optional(),
|
|
23
|
+
challenges: z.string().optional()
|
|
24
|
+
});
|
|
25
|
+
async function loadConfig(path) {
|
|
26
|
+
const directory = (await stat(path)).isDirectory();
|
|
27
|
+
const root = directory ? resolve(path) : dirname(resolve(path));
|
|
28
|
+
const exists = (file) => access(resolve(root, file)).then(() => true, () => false);
|
|
29
|
+
const configPath = directory ? resolve(root, "matchbox.config.ts") : resolve(path);
|
|
30
|
+
const authored = await access(configPath).then(async () => (await import(pathToFileURL(configPath).href)).default, () => ({}));
|
|
31
|
+
let defaults = {};
|
|
32
|
+
if (await exists("pipeline.ts")) {
|
|
33
|
+
const pipeline = pipelineSchema.parse((await import(pathToFileURL(resolve(root, "pipeline.ts")).href)).default);
|
|
34
|
+
defaults = { ...pipeline.acceptance };
|
|
35
|
+
if (pipeline.prediction.kind === "token-classifier") defaults.sequence = {
|
|
36
|
+
recipe: pipeline.prediction.recipe,
|
|
37
|
+
decoder: pipeline.prediction.decode
|
|
38
|
+
};
|
|
39
|
+
} else if (!authored.sequence) throw new Error(`Missing ${resolve(root, "pipeline.ts")}. Author an explicit pipeline before training.`);
|
|
40
|
+
const config = configSchema.parse({
|
|
41
|
+
output: resolve(root, "../../.matchbox", basename(root), "model.matchbox"),
|
|
42
|
+
...defaults,
|
|
43
|
+
...authored
|
|
44
|
+
});
|
|
45
|
+
if (!config.baseline && await exists("evals/baseline.ts")) config.baseline = "./evals/baseline.ts";
|
|
46
|
+
if (!config.challenges && await exists("evals/challenges.json")) config.challenges = "./evals/challenges.json";
|
|
47
|
+
return {
|
|
48
|
+
config,
|
|
49
|
+
root
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region src/project/discover.ts
|
|
54
|
+
const exists = (path) => access(path).then(() => true, () => false);
|
|
55
|
+
async function select(root) {
|
|
56
|
+
if (await exists(resolve(root, "parser.ts"))) return root;
|
|
57
|
+
if (await exists(resolve(root, "matchbox.config.ts"))) return resolve(root, "matchbox.config.ts");
|
|
58
|
+
const directory = resolve(root, "matchbox");
|
|
59
|
+
const entries = await readdir(directory, { withFileTypes: true }).catch(() => []);
|
|
60
|
+
const tasks = [];
|
|
61
|
+
for (const entry of entries) if (entry.isDirectory() && await exists(resolve(directory, entry.name, "parser.ts"))) tasks.push(entry.name);
|
|
62
|
+
tasks.sort();
|
|
63
|
+
if (tasks.length > 1) throw new Error(`Choose a task: ${tasks.join(", ")}. For example, matchbox-ai train ${tasks[0]}.`);
|
|
64
|
+
return tasks.length ? resolve(directory, tasks[0]) : void 0;
|
|
65
|
+
}
|
|
66
|
+
async function discover(target, cwd = process.cwd()) {
|
|
67
|
+
if (target && await exists(resolve(cwd, target))) {
|
|
68
|
+
const path = resolve(cwd, target);
|
|
69
|
+
if ((await stat(path)).isFile()) return path;
|
|
70
|
+
const selected = await select(path);
|
|
71
|
+
if (selected) return selected;
|
|
72
|
+
throw new Error(`No Matchbox task found in ${path}.`);
|
|
73
|
+
}
|
|
74
|
+
let root = resolve(cwd);
|
|
75
|
+
for (;;) {
|
|
76
|
+
if (target) {
|
|
77
|
+
const task = resolve(root, "matchbox", target);
|
|
78
|
+
if (await exists(resolve(task, "parser.ts"))) return task;
|
|
79
|
+
} else {
|
|
80
|
+
const selected = await select(root);
|
|
81
|
+
if (selected) return selected;
|
|
82
|
+
}
|
|
83
|
+
const parent = dirname(root);
|
|
84
|
+
if (parent === root) throw new Error(target ? `Task not found: ${target}.` : "No Matchbox project found. Run matchbox-ai init <name>.");
|
|
85
|
+
root = parent;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region src/project/artifact.ts
|
|
90
|
+
async function loadArtifact(configPath) {
|
|
91
|
+
const { config, root } = await loadConfig(configPath);
|
|
92
|
+
const output = resolve(root, config.output);
|
|
93
|
+
const text = await readFile(output, "utf8").catch(() => {
|
|
94
|
+
throw new Error(`No readable model at ${output}. Run matchbox-ai train first.`);
|
|
95
|
+
});
|
|
96
|
+
const artifact = readArtifact(JSON.parse(text));
|
|
97
|
+
const task = (await import(pathToFileURL(resolve(dirname(output), artifact.taskModule)).href)).default;
|
|
98
|
+
const decode = artifact.decoderModule === null ? void 0 : (await import(pathToFileURL(resolve(dirname(output), artifact.decoderModule)).href)).default;
|
|
99
|
+
return {
|
|
100
|
+
artifact,
|
|
101
|
+
task,
|
|
102
|
+
decode,
|
|
103
|
+
parser: createParser(artifact, task, decode),
|
|
104
|
+
inspect: async (input) => {
|
|
105
|
+
const predictor = await tensorPredictor(artifact);
|
|
106
|
+
try {
|
|
107
|
+
if (artifact.kind === "record-parser") return predictor.record(input);
|
|
108
|
+
const tokens = predictor.sequence(input);
|
|
109
|
+
return {
|
|
110
|
+
tokens,
|
|
111
|
+
candidate: decode(tokens, input)
|
|
112
|
+
};
|
|
113
|
+
} finally {
|
|
114
|
+
predictor.dispose();
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
output,
|
|
118
|
+
config,
|
|
119
|
+
root
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
//#endregion
|
|
123
|
+
export { discover as n, loadConfig as r, loadArtifact as t };
|
|
124
|
+
|
|
125
|
+
//# sourceMappingURL=project-BP331m20.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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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"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { t as evaluateSequence } from "./evaluate-sequence-ZCwyomzQ.js";
|
|
2
|
+
import { t as packageModel } from "./package-model-CPj0qnQs.js";
|
|
3
|
+
import { readRecordArtifact, recordFeatures, recordTokens, tensorPredictor } from "@matchbox-ai/core/internal";
|
|
4
|
+
import { dirname, relative } from "node:path";
|
|
5
|
+
import { createParser } from "@matchbox-ai/core/runtime";
|
|
6
|
+
import { createHash } from "node:crypto";
|
|
7
|
+
import * as tf from "@tensorflow/tfjs-node";
|
|
8
|
+
//#region src/models/record/verify-export.ts
|
|
9
|
+
async function verifyExport(model, artifact, probes) {
|
|
10
|
+
const native = probes.map((input) => tf.tidy(() => {
|
|
11
|
+
const logits = model.predict(tf.tensor2d([recordFeatures(input, artifact.vocabulary)]));
|
|
12
|
+
let offset = 0;
|
|
13
|
+
return artifact.fields.map((field) => {
|
|
14
|
+
const probabilities = Array.from(tf.softmax(tf.slice(logits, [0, offset], [1, field.values.length])).dataSync());
|
|
15
|
+
offset += field.values.length;
|
|
16
|
+
return probabilities;
|
|
17
|
+
});
|
|
18
|
+
}));
|
|
19
|
+
const predictor = await tensorPredictor(artifact);
|
|
20
|
+
let maxConfidenceError = 0;
|
|
21
|
+
let labelDisagreements = 0;
|
|
22
|
+
try {
|
|
23
|
+
probes.forEach((input, probeIndex) => {
|
|
24
|
+
predictor.record(input).fields.forEach((prediction, index) => {
|
|
25
|
+
const scores = native[probeIndex][index];
|
|
26
|
+
const maximum = Math.max(...scores);
|
|
27
|
+
maxConfidenceError = Math.max(maxConfidenceError, Math.abs(maximum - prediction.confidence));
|
|
28
|
+
if (prediction.value !== artifact.fields[index].values[scores.indexOf(maximum)]) labelDisagreements++;
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
} finally {
|
|
32
|
+
predictor.dispose();
|
|
33
|
+
await tf.setBackend("tensorflow");
|
|
34
|
+
}
|
|
35
|
+
if (labelDisagreements || maxConfidenceError > 1e-5) throw new Error("Serialized TensorFlow model disagrees with native predictions.");
|
|
36
|
+
return {
|
|
37
|
+
examples: probes.length,
|
|
38
|
+
labelDisagreements,
|
|
39
|
+
maxConfidenceError
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region src/models/record/fit.ts
|
|
44
|
+
async function fitRecord(examples, metadata, probes, progress) {
|
|
45
|
+
const schema = metadata.taskMetadata.output;
|
|
46
|
+
if (schema.type !== "object" || !schema.properties || schema.additionalProperties !== false) throw new Error("The default trainer currently supports strict flat objects with primitive field values. Use an explicit sequence pipeline for other shapes.");
|
|
47
|
+
const fields = Object.keys(schema.properties).map((name) => {
|
|
48
|
+
return {
|
|
49
|
+
name,
|
|
50
|
+
values: [...new Map(examples.map((row) => {
|
|
51
|
+
const value = row.output[name];
|
|
52
|
+
if (value !== null && ![
|
|
53
|
+
"number",
|
|
54
|
+
"string",
|
|
55
|
+
"boolean"
|
|
56
|
+
].includes(typeof value)) throw new Error(`output.${name}: the default trainer requires primitive field values.`);
|
|
57
|
+
return [JSON.stringify(value), value];
|
|
58
|
+
})).entries()].sort(([a], [b]) => a.localeCompare(b)).map(([, value]) => value)
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
const vocabulary = [...new Set(examples.flatMap((row) => recordTokens(row.input)))].sort();
|
|
62
|
+
const outputs = fields.reduce((sum, field) => sum + field.values.length, 0);
|
|
63
|
+
if (fields.length > 32 || fields.some((field) => field.values.length > 256) || vocabulary.length > 1e4) throw new Error("Default trainer capacity exceeded. Use a custom pipeline for larger output domains.");
|
|
64
|
+
await tf.setBackend("tensorflow");
|
|
65
|
+
await tf.ready();
|
|
66
|
+
const model = tf.sequential({ layers: [tf.layers.dense({
|
|
67
|
+
inputShape: [vocabulary.length],
|
|
68
|
+
units: 32,
|
|
69
|
+
activation: "tanh",
|
|
70
|
+
kernelInitializer: tf.initializers.glorotUniform({ seed: 42 })
|
|
71
|
+
}), tf.layers.dense({
|
|
72
|
+
units: outputs,
|
|
73
|
+
kernelInitializer: tf.initializers.glorotUniform({ seed: 43 })
|
|
74
|
+
})] });
|
|
75
|
+
const optimizer = tf.train.adam(.02);
|
|
76
|
+
model.compile({
|
|
77
|
+
optimizer,
|
|
78
|
+
loss: (gold, logits) => tf.tidy(() => {
|
|
79
|
+
let offset = 0;
|
|
80
|
+
const losses = fields.map((field) => {
|
|
81
|
+
const count = field.values.length;
|
|
82
|
+
const target = tf.slice(gold, [0, offset], [-1, count]);
|
|
83
|
+
const scores = tf.slice(logits, [0, offset], [-1, count]);
|
|
84
|
+
offset += count;
|
|
85
|
+
return tf.neg(tf.sum(tf.mul(target, tf.logSoftmax(scores)), 1));
|
|
86
|
+
});
|
|
87
|
+
return tf.mean(tf.addN(losses));
|
|
88
|
+
})
|
|
89
|
+
});
|
|
90
|
+
const x = tf.tensor2d(examples.map((row) => recordFeatures(row.input, vocabulary)));
|
|
91
|
+
const y = tf.tensor2d(examples.map((row) => fields.flatMap((field) => field.values.map((value) => value === row.output[field.name] ? 1 : 0))));
|
|
92
|
+
function exported(precision) {
|
|
93
|
+
const weights = model.getWeights().map((tensor, index) => {
|
|
94
|
+
const values = Array.from(tensor.dataSync());
|
|
95
|
+
const scale = precision === "int8" ? values.reduce((max, value) => Math.max(max, Math.abs(value)), 0) / 127 || 1 : 1;
|
|
96
|
+
return {
|
|
97
|
+
name: model.weights[index].originalName,
|
|
98
|
+
shape: tensor.shape,
|
|
99
|
+
values: precision === "int8" ? values.map((value) => Math.round(value / scale)) : values,
|
|
100
|
+
scale
|
|
101
|
+
};
|
|
102
|
+
});
|
|
103
|
+
return readRecordArtifact({
|
|
104
|
+
formatVersion: 2,
|
|
105
|
+
kind: "record-parser",
|
|
106
|
+
architecture: "bag-of-words-mlp",
|
|
107
|
+
...metadata,
|
|
108
|
+
modelTopology: JSON.parse(model.toJSON()),
|
|
109
|
+
decoderModule: null,
|
|
110
|
+
fields,
|
|
111
|
+
vocabulary,
|
|
112
|
+
threshold: .75,
|
|
113
|
+
precision,
|
|
114
|
+
weights
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
const untrained = exported("float32");
|
|
118
|
+
const history = [];
|
|
119
|
+
try {
|
|
120
|
+
await model.fit(x, y, {
|
|
121
|
+
epochs: 100,
|
|
122
|
+
batchSize: 128,
|
|
123
|
+
shuffle: false,
|
|
124
|
+
verbose: 0,
|
|
125
|
+
callbacks: { onEpochEnd(epoch, logs) {
|
|
126
|
+
history.push(Number(logs?.loss));
|
|
127
|
+
progress?.(epoch + 1, Number(logs?.loss));
|
|
128
|
+
} }
|
|
129
|
+
});
|
|
130
|
+
const float = exported("float32");
|
|
131
|
+
return {
|
|
132
|
+
float,
|
|
133
|
+
quantized: exported("int8"),
|
|
134
|
+
untrained,
|
|
135
|
+
history,
|
|
136
|
+
parity: await verifyExport(model, float, probes)
|
|
137
|
+
};
|
|
138
|
+
} finally {
|
|
139
|
+
x.dispose();
|
|
140
|
+
y.dispose();
|
|
141
|
+
model.dispose();
|
|
142
|
+
optimizer.dispose();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
//#endregion
|
|
146
|
+
//#region src/models/record/run.ts
|
|
147
|
+
async function runRecord(project, progress) {
|
|
148
|
+
const start = performance.now();
|
|
149
|
+
const taskModule = relative(dirname(project.output), project.taskPath).replaceAll("\\", "/");
|
|
150
|
+
const fit = await fitRecord(project.train, {
|
|
151
|
+
taskModule: taskModule.startsWith(".") ? taskModule : `./${taskModule}`,
|
|
152
|
+
taskMetadata: project.task.toJSON()
|
|
153
|
+
}, project.validation.map((row) => row.input), progress);
|
|
154
|
+
const evaluate = async (artifact, rows = project.evaluation) => {
|
|
155
|
+
const parser = createParser(artifact, project.task);
|
|
156
|
+
try {
|
|
157
|
+
return await evaluateSequence(parser, rows, (value) => project.task.validateOutput(value).success);
|
|
158
|
+
} finally {
|
|
159
|
+
parser.dispose();
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
const validation = await evaluate(fit.quantized, project.validation);
|
|
163
|
+
const bytes = Buffer.byteLength(JSON.stringify(fit.quantized));
|
|
164
|
+
if (validation.exactAccuracy < project.config.minAccuracy || bytes > project.config.maxBytes) throw new Error(`Record model failed validation/size requirements (${validation.exactAccuracy}, ${bytes} bytes). ${JSON.stringify(validation.failures.slice(0, 10))}`);
|
|
165
|
+
const report = {
|
|
166
|
+
formatVersion: 1,
|
|
167
|
+
architecture: fit.quantized.architecture,
|
|
168
|
+
backend: "TensorFlow native CPU",
|
|
169
|
+
seed: 42,
|
|
170
|
+
artifactSha256: createHash("sha256").update(JSON.stringify(fit.quantized)).digest("hex"),
|
|
171
|
+
bytes,
|
|
172
|
+
parameters: fit.quantized.weights.reduce((sum, weight) => sum + weight.values.length, 0),
|
|
173
|
+
datasetSha256: project.sources.map((source) => ({
|
|
174
|
+
source: source.source,
|
|
175
|
+
sha256: createHash("sha256").update(source.text).digest("hex")
|
|
176
|
+
})),
|
|
177
|
+
examples: {
|
|
178
|
+
train: project.train.length,
|
|
179
|
+
validation: project.validation.length,
|
|
180
|
+
eval: project.evaluation.length
|
|
181
|
+
},
|
|
182
|
+
loss: fit.history,
|
|
183
|
+
exportParity: fit.parity,
|
|
184
|
+
validation,
|
|
185
|
+
quantized: await evaluate(fit.quantized),
|
|
186
|
+
float: await evaluate(fit.float),
|
|
187
|
+
untrained: await evaluate(fit.untrained),
|
|
188
|
+
untrainedUngated: await evaluate({
|
|
189
|
+
...fit.untrained,
|
|
190
|
+
threshold: 0
|
|
191
|
+
}),
|
|
192
|
+
baseline: project.baseline ? await evaluateSequence(project.baseline, project.evaluation, (value) => project.task.validateOutput(value).success) : null,
|
|
193
|
+
trainingMs: performance.now() - start,
|
|
194
|
+
notes: "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."
|
|
195
|
+
};
|
|
196
|
+
await packageModel(project.output, fit.quantized, report);
|
|
197
|
+
return {
|
|
198
|
+
report,
|
|
199
|
+
output: project.output
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
//#endregion
|
|
203
|
+
export { runRecord };
|
|
204
|
+
|
|
205
|
+
//# sourceMappingURL=record-BTZ5LWHF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { r as loadConfig } from "./project-BP331m20.js";
|
|
2
|
+
import "zod";
|
|
3
|
+
import { readFile } from "node:fs/promises";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
import { parseDatasets } from "@matchbox-ai/core";
|
|
7
|
+
//#region src/load-project.ts
|
|
8
|
+
async function loadProject(path) {
|
|
9
|
+
const { config, root } = await loadConfig(path);
|
|
10
|
+
const taskPath = resolve(root, config.task);
|
|
11
|
+
const task = (await import(pathToFileURL(taskPath).href)).default;
|
|
12
|
+
const baseline = config.baseline ? (await import(pathToFileURL(resolve(root, config.baseline)).href)).default : null;
|
|
13
|
+
const sources = await Promise.all([
|
|
14
|
+
config.train,
|
|
15
|
+
config.validation,
|
|
16
|
+
config.eval
|
|
17
|
+
].map(async (source) => ({
|
|
18
|
+
source,
|
|
19
|
+
text: await readFile(resolve(root, source), "utf8")
|
|
20
|
+
})));
|
|
21
|
+
const training = parseDatasets(task, {
|
|
22
|
+
formatVersion: 1,
|
|
23
|
+
train: sources[0],
|
|
24
|
+
eval: sources[1]
|
|
25
|
+
});
|
|
26
|
+
const evaluation = parseDatasets(task, {
|
|
27
|
+
formatVersion: 1,
|
|
28
|
+
train: sources[0],
|
|
29
|
+
eval: sources[2]
|
|
30
|
+
});
|
|
31
|
+
for (const result of [training, evaluation]) if (!result.success) throw new Error(result.issues.map((issue) => `${issue.source}:${issue.line} ${issue.path.join(".")}: ${issue.message}`).join("\n"));
|
|
32
|
+
if (!training.success || !evaluation.success) throw new Error("Invalid datasets.");
|
|
33
|
+
const groups = [
|
|
34
|
+
training.data.train,
|
|
35
|
+
training.data.eval,
|
|
36
|
+
evaluation.data.eval
|
|
37
|
+
];
|
|
38
|
+
const seen = /* @__PURE__ */ new Map();
|
|
39
|
+
groups.forEach((group, split) => group.forEach((row) => {
|
|
40
|
+
const key = row.input.trim().toLowerCase();
|
|
41
|
+
if (seen.has(key) && seen.get(key) !== split) throw new Error(`Input overlaps dataset splits: ${row.input}`);
|
|
42
|
+
seen.set(key, split);
|
|
43
|
+
}));
|
|
44
|
+
return {
|
|
45
|
+
root,
|
|
46
|
+
config,
|
|
47
|
+
task,
|
|
48
|
+
baseline,
|
|
49
|
+
taskPath,
|
|
50
|
+
output: resolve(root, config.output),
|
|
51
|
+
sources,
|
|
52
|
+
train: training.data.train,
|
|
53
|
+
validation: training.data.eval,
|
|
54
|
+
evaluation: evaluation.data.eval
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/run.ts
|
|
59
|
+
async function run(command, path, progress) {
|
|
60
|
+
const project = await loadProject(path);
|
|
61
|
+
if (project.config.sequence) {
|
|
62
|
+
const { runSequence } = await import("./sequence-C_WfVGO1.js");
|
|
63
|
+
return runSequence(command, project, progress);
|
|
64
|
+
}
|
|
65
|
+
if (command !== "train") throw new Error("Use the eval CLI command to evaluate a saved artifact.");
|
|
66
|
+
const { runRecord } = await import("./record-BTZ5LWHF.js");
|
|
67
|
+
return runRecord(project, progress);
|
|
68
|
+
}
|
|
69
|
+
//#endregion
|
|
70
|
+
export { run };
|
|
71
|
+
|
|
72
|
+
//# sourceMappingURL=run-Cwa7vD_T.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|