@tuned-tensor/cli 0.13.1-beta.5 → 0.13.1-beta.6
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/README.md +17 -6
- package/dist/{chunk-AVMRHDFY.js → chunk-W7MOBPCO.js} +61 -38
- package/dist/chunk-W7MOBPCO.js.map +1 -0
- package/dist/index.js +122 -17
- package/dist/index.js.map +1 -1
- package/dist/local-runtime.js +69 -6
- package/dist/local-runtime.js.map +1 -1
- package/docs/local-runtime/architecture.md +10 -6
- package/docs/local-runtime/foundation-long-runs.md +108 -0
- package/package.json +1 -1
- package/training/foundation/src/checkpoint.py +235 -0
- package/training/foundation/src/common.py +30 -2
- package/training/foundation/src/data.py +89 -3
- package/training/foundation/src/evaluate.py +10 -2
- package/training/foundation/src/pretrain.py +426 -27
- package/training/foundation/src/train_tokenizer.py +29 -5
- package/dist/chunk-AVMRHDFY.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -16,12 +16,13 @@ import {
|
|
|
16
16
|
hardenExistingLocalStore,
|
|
17
17
|
loadLocalRunInput,
|
|
18
18
|
loadLocalRunnerConfig,
|
|
19
|
+
localPathsOverlap,
|
|
19
20
|
readHardwareSnapshot,
|
|
20
21
|
runLocalPipeline,
|
|
21
22
|
runLoggedProcess,
|
|
22
23
|
warningsFromSnapshot,
|
|
23
24
|
withFoundationPythonEnvironment
|
|
24
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-W7MOBPCO.js";
|
|
25
26
|
|
|
26
27
|
// src/cli.ts
|
|
27
28
|
import { spawn as spawn2 } from "child_process";
|
|
@@ -1067,6 +1068,7 @@ var TRAINING_SOURCE_FILES = {
|
|
|
1067
1068
|
foundation: {
|
|
1068
1069
|
tokenizer: "training/foundation/src/train_tokenizer.py",
|
|
1069
1070
|
pretrain: "training/foundation/src/pretrain.py",
|
|
1071
|
+
checkpoint: "training/foundation/src/checkpoint.py",
|
|
1070
1072
|
model: "training/foundation/src/model.py",
|
|
1071
1073
|
data: "training/foundation/src/data.py",
|
|
1072
1074
|
finetune: "training/foundation/src/finetune.py",
|
|
@@ -1522,6 +1524,7 @@ function createTunedTensorTools(api, options = {}) {
|
|
|
1522
1524
|
component: Type2.Union([
|
|
1523
1525
|
Type2.Literal("tokenizer"),
|
|
1524
1526
|
Type2.Literal("pretrain"),
|
|
1527
|
+
Type2.Literal("checkpoint"),
|
|
1525
1528
|
Type2.Literal("model"),
|
|
1526
1529
|
Type2.Literal("data"),
|
|
1527
1530
|
Type2.Literal("finetune"),
|
|
@@ -5134,8 +5137,8 @@ import { resolve as resolve5 } from "path";
|
|
|
5134
5137
|
// src/local-runtime/foundation-runner.ts
|
|
5135
5138
|
import { createHash as createHash3, randomUUID as randomUUID5 } from "crypto";
|
|
5136
5139
|
import { createReadStream } from "fs";
|
|
5137
|
-
import { chmod as chmod2, lstat as lstat3, mkdir as mkdir3, open as open2, readdir as readdir3, readFile as readFile4 } from "fs/promises";
|
|
5138
|
-
import { dirname as dirname4, join as join7, resolve as resolve4 } from "path";
|
|
5140
|
+
import { chmod as chmod2, lstat as lstat3, mkdir as mkdir3, open as open2, readdir as readdir3, readFile as readFile4, rename as rename2, rm as rm2 } from "fs/promises";
|
|
5141
|
+
import { basename as basename4, dirname as dirname4, join as join7, resolve as resolve4 } from "path";
|
|
5139
5142
|
function isRef(value) {
|
|
5140
5143
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value) && typeof value.from === "string";
|
|
5141
5144
|
}
|
|
@@ -5204,13 +5207,20 @@ async function readRequiredJsonObject(path, description) {
|
|
|
5204
5207
|
}
|
|
5205
5208
|
return parsed;
|
|
5206
5209
|
}
|
|
5207
|
-
async function
|
|
5208
|
-
const
|
|
5210
|
+
async function writePrivateTextAtomic(path, content) {
|
|
5211
|
+
const temporary = join7(dirname4(path), `.${basename4(path)}.${randomUUID5()}.tmp`);
|
|
5212
|
+
const file = await open2(temporary, "wx", 384);
|
|
5213
|
+
let closed = false;
|
|
5209
5214
|
try {
|
|
5210
5215
|
await file.chmod(384);
|
|
5211
5216
|
await file.writeFile(content, "utf8");
|
|
5212
|
-
|
|
5217
|
+
await file.sync();
|
|
5213
5218
|
await file.close();
|
|
5219
|
+
closed = true;
|
|
5220
|
+
await rename2(temporary, path);
|
|
5221
|
+
} finally {
|
|
5222
|
+
if (!closed) await file.close().catch(() => void 0);
|
|
5223
|
+
await rm2(temporary, { force: true }).catch(() => void 0);
|
|
5214
5224
|
}
|
|
5215
5225
|
}
|
|
5216
5226
|
async function artifactFor(path) {
|
|
@@ -5247,6 +5257,11 @@ function assertFoundationPlanSupported(spec, plan) {
|
|
|
5247
5257
|
`Foundation runner does not implement ${unsupported.uses} (step ${unsupported.id}).`
|
|
5248
5258
|
);
|
|
5249
5259
|
}
|
|
5260
|
+
if (spec.foundation.corpus_path && spec.foundation.validation_path && localPathsOverlap(spec.foundation.corpus_path, spec.foundation.validation_path)) {
|
|
5261
|
+
throw new Error(
|
|
5262
|
+
"Foundation training and validation paths overlap; use disjoint corpora for held-out evaluation."
|
|
5263
|
+
);
|
|
5264
|
+
}
|
|
5250
5265
|
if (plan.steps.some((step) => step.uses === "rl")) {
|
|
5251
5266
|
const invalidReward = spec.examples.findIndex(
|
|
5252
5267
|
(example) => !/-?\d+(?:\.\d+)?/.test(example.output.replaceAll(",", ""))
|
|
@@ -5274,7 +5289,9 @@ async function defaultFoundationSpawn(args2) {
|
|
|
5274
5289
|
commandArgs: launched.commandArgs,
|
|
5275
5290
|
env: withFoundationPythonEnvironment(process.env),
|
|
5276
5291
|
logPath: args2.logPath,
|
|
5277
|
-
stage: args2.stepId
|
|
5292
|
+
stage: args2.stepId,
|
|
5293
|
+
shutdownGraceMs: 12e4,
|
|
5294
|
+
appendLog: true
|
|
5278
5295
|
});
|
|
5279
5296
|
if (result2.exitCode !== 0) {
|
|
5280
5297
|
throw new Error(
|
|
@@ -5288,8 +5305,26 @@ async function runFoundationPipeline(args2) {
|
|
|
5288
5305
|
const outputDir = resolve4(
|
|
5289
5306
|
args2.outputDir ?? join7(dirname4(resolve4(args2.specPath)), DEFAULT_FOUNDATION_RUNS_DIR, randomUUID5())
|
|
5290
5307
|
);
|
|
5291
|
-
|
|
5292
|
-
|
|
5308
|
+
let outputExists = false;
|
|
5309
|
+
try {
|
|
5310
|
+
const outputInfo = await lstat3(outputDir);
|
|
5311
|
+
if (!args2.resume) {
|
|
5312
|
+
throw new Error(`Foundation output directory already exists; pass --resume ${outputDir} to recover it.`);
|
|
5313
|
+
}
|
|
5314
|
+
if (outputInfo.isSymbolicLink()) {
|
|
5315
|
+
throw new Error(`Foundation resume directory must not be a symbolic link: ${outputDir}`);
|
|
5316
|
+
}
|
|
5317
|
+
if (!outputInfo.isDirectory()) {
|
|
5318
|
+
throw new Error(`Foundation resume path must be a directory: ${outputDir}`);
|
|
5319
|
+
}
|
|
5320
|
+
outputExists = true;
|
|
5321
|
+
} catch (error) {
|
|
5322
|
+
if (error.code !== "ENOENT") throw error;
|
|
5323
|
+
}
|
|
5324
|
+
if (!outputExists) {
|
|
5325
|
+
await mkdir3(outputDir, { recursive: true, mode: 448 });
|
|
5326
|
+
}
|
|
5327
|
+
await makePrivateTree(outputDir);
|
|
5293
5328
|
const spawnStep = args2.spawnStep ?? defaultFoundationSpawn;
|
|
5294
5329
|
const produced = /* @__PURE__ */ new Map();
|
|
5295
5330
|
const steps = [];
|
|
@@ -5297,6 +5332,7 @@ async function runFoundationPipeline(args2) {
|
|
|
5297
5332
|
const hp = args2.spec.foundation;
|
|
5298
5333
|
let tokenizerDir;
|
|
5299
5334
|
let modelDir;
|
|
5335
|
+
let upstreamExecuted = false;
|
|
5300
5336
|
const resolveKind = (stepId, kind) => {
|
|
5301
5337
|
const path = produced.get(stepId)?.[kind];
|
|
5302
5338
|
if (!path) throw new Error(`Step ${stepId} did not produce ${kind}.`);
|
|
@@ -5316,6 +5352,8 @@ async function runFoundationPipeline(args2) {
|
|
|
5316
5352
|
await chmod2(output, 448);
|
|
5317
5353
|
const configPath2 = join7(stepDir, "config.json");
|
|
5318
5354
|
const logPath = join7(stepDir, "step.log");
|
|
5355
|
+
const metricsPath = join7(output, "metrics.json");
|
|
5356
|
+
const completionPath = join7(stepDir, "completion.json");
|
|
5319
5357
|
const fields = stepConfig(step);
|
|
5320
5358
|
let entrypoint;
|
|
5321
5359
|
let config;
|
|
@@ -5324,7 +5362,8 @@ async function runFoundationPipeline(args2) {
|
|
|
5324
5362
|
config = {
|
|
5325
5363
|
output_dir: output,
|
|
5326
5364
|
vocab_size: fields.vocabSize ?? hp.vocab_size,
|
|
5327
|
-
max_chars: fields.maxChars ?? hp.max_chars,
|
|
5365
|
+
max_chars: hp.tokenizer_max_chars ?? fields.maxChars ?? hp.max_chars,
|
|
5366
|
+
corpus_path: hp.corpus_path,
|
|
5328
5367
|
system_prompt: args2.spec.system_prompt,
|
|
5329
5368
|
examples
|
|
5330
5369
|
};
|
|
@@ -5334,12 +5373,28 @@ async function runFoundationPipeline(args2) {
|
|
|
5334
5373
|
config = {
|
|
5335
5374
|
tokenizer_dir: tokenizerDir,
|
|
5336
5375
|
output_dir: output,
|
|
5376
|
+
work_dir: join7(stepDir, "recovery"),
|
|
5337
5377
|
depth: fields.depth ?? hp.depth,
|
|
5338
5378
|
steps: fields.steps ?? hp.pretrain_steps,
|
|
5339
5379
|
batch_size: fields.batchSize ?? hp.batch_size,
|
|
5340
5380
|
sequence_length: fields.sequenceLength ?? hp.sequence_length,
|
|
5341
5381
|
nproc_per_node: fields.nprocPerNode ?? hp.nproc_per_node,
|
|
5342
5382
|
max_chars: hp.max_chars,
|
|
5383
|
+
corpus_path: hp.corpus_path,
|
|
5384
|
+
seed: hp.seed,
|
|
5385
|
+
learning_rate: hp.learning_rate,
|
|
5386
|
+
weight_decay: hp.weight_decay,
|
|
5387
|
+
warmup_steps: hp.warmup_steps,
|
|
5388
|
+
min_lr_ratio: hp.min_lr_ratio,
|
|
5389
|
+
gradient_accumulation_steps: hp.gradient_accumulation_steps,
|
|
5390
|
+
gradient_clip: hp.gradient_clip,
|
|
5391
|
+
bf16: hp.bf16,
|
|
5392
|
+
checkpoint_interval_steps: hp.checkpoint_interval_steps,
|
|
5393
|
+
checkpoint_interval_seconds: hp.checkpoint_interval_seconds,
|
|
5394
|
+
keep_checkpoints: hp.keep_checkpoints,
|
|
5395
|
+
log_interval_steps: hp.log_interval_steps,
|
|
5396
|
+
checkpoint_backup_dir: hp.checkpoint_backup_dir,
|
|
5397
|
+
resume: true,
|
|
5343
5398
|
system_prompt: args2.spec.system_prompt,
|
|
5344
5399
|
examples
|
|
5345
5400
|
};
|
|
@@ -5377,6 +5432,7 @@ async function runFoundationPipeline(args2) {
|
|
|
5377
5432
|
output_dir: output,
|
|
5378
5433
|
sequence_length: hp.sequence_length,
|
|
5379
5434
|
max_chars: hp.max_chars,
|
|
5435
|
+
validation_path: hp.validation_path,
|
|
5380
5436
|
system_prompt: args2.spec.system_prompt,
|
|
5381
5437
|
examples
|
|
5382
5438
|
};
|
|
@@ -5386,12 +5442,33 @@ async function runFoundationPipeline(args2) {
|
|
|
5386
5442
|
if ((step.uses === "finetune" || step.uses === "rl" || step.uses === "evaluate") && !tokenizerDir) {
|
|
5387
5443
|
throw new Error(`Step ${step.id} needs a tokenizer from an earlier tokenize step.`);
|
|
5388
5444
|
}
|
|
5389
|
-
|
|
5445
|
+
let completedStep = false;
|
|
5446
|
+
if (args2.resume) {
|
|
5447
|
+
try {
|
|
5448
|
+
const previousConfig = JSON.parse(await readFile4(configPath2, "utf8"));
|
|
5449
|
+
if (JSON.stringify(previousConfig) !== JSON.stringify(config)) {
|
|
5450
|
+
throw new Error(
|
|
5451
|
+
`Foundation step "${step.id}" configuration changed; resume requires the original spec and pipeline.`
|
|
5452
|
+
);
|
|
5453
|
+
}
|
|
5454
|
+
if (!upstreamExecuted) {
|
|
5455
|
+
await lstat3(metricsPath);
|
|
5456
|
+
await lstat3(completionPath);
|
|
5457
|
+
completedStep = true;
|
|
5458
|
+
}
|
|
5459
|
+
} catch (error) {
|
|
5460
|
+
if (error.code !== "ENOENT") throw error;
|
|
5461
|
+
}
|
|
5462
|
+
}
|
|
5463
|
+
if (!completedStep) {
|
|
5464
|
+
await writePrivateTextAtomic(configPath2, `${JSON.stringify(config, null, 2)}
|
|
5390
5465
|
`);
|
|
5391
|
-
|
|
5466
|
+
await spawnStep({ entrypoint, configPath: configPath2, logPath, stepId: step.id });
|
|
5467
|
+
upstreamExecuted = true;
|
|
5468
|
+
}
|
|
5392
5469
|
await makePrivateTree(stepDir);
|
|
5393
5470
|
const metrics = await readRequiredJsonObject(
|
|
5394
|
-
|
|
5471
|
+
metricsPath,
|
|
5395
5472
|
`Foundation step "${step.id}" metrics`
|
|
5396
5473
|
);
|
|
5397
5474
|
if (metrics.ok !== true) {
|
|
@@ -5434,6 +5511,26 @@ async function runFoundationPipeline(args2) {
|
|
|
5434
5511
|
record4.report = reportPath2;
|
|
5435
5512
|
artifacts.report = await artifactFor(reportPath2);
|
|
5436
5513
|
}
|
|
5514
|
+
const completion = {
|
|
5515
|
+
version: 1,
|
|
5516
|
+
config_sha256: await hashPath(configPath2),
|
|
5517
|
+
metrics_sha256: await hashPath(metricsPath),
|
|
5518
|
+
artifacts
|
|
5519
|
+
};
|
|
5520
|
+
if (completedStep) {
|
|
5521
|
+
const previousCompletion = await readRequiredJsonObject(
|
|
5522
|
+
completionPath,
|
|
5523
|
+
`Foundation step "${step.id}" completion manifest`
|
|
5524
|
+
);
|
|
5525
|
+
if (JSON.stringify(previousCompletion) !== JSON.stringify(completion)) {
|
|
5526
|
+
throw new Error(
|
|
5527
|
+
`Foundation step "${step.id}" integrity changed after completion; use a new run directory.`
|
|
5528
|
+
);
|
|
5529
|
+
}
|
|
5530
|
+
} else {
|
|
5531
|
+
await writePrivateTextAtomic(completionPath, `${JSON.stringify(completion, null, 2)}
|
|
5532
|
+
`);
|
|
5533
|
+
}
|
|
5437
5534
|
produced.set(step.id, record4);
|
|
5438
5535
|
steps.push({ id: step.id, uses: step.uses, metrics, artifacts });
|
|
5439
5536
|
}
|
|
@@ -5444,7 +5541,7 @@ async function runFoundationPipeline(args2) {
|
|
|
5444
5541
|
steps
|
|
5445
5542
|
};
|
|
5446
5543
|
const reportPath = join7(outputDir, "report.json");
|
|
5447
|
-
await
|
|
5544
|
+
await writePrivateTextAtomic(reportPath, `${JSON.stringify(report, null, 2)}
|
|
5448
5545
|
`);
|
|
5449
5546
|
return {
|
|
5450
5547
|
status: "succeeded",
|
|
@@ -5592,7 +5689,10 @@ function registerPipelineCommands(parent) {
|
|
|
5592
5689
|
const plan = createExecutionPlan(document, { only: parseList(options.only), skip: parseList(options.skip) });
|
|
5593
5690
|
outputPlan(plan, await hostWarningsForPipeline(document, options.spec));
|
|
5594
5691
|
});
|
|
5595
|
-
pipeline.command("run").description("Run an ordered local pipeline, or safely preview any pipeline").option("--dry-run", "Resolve and display only; never execute, transfer, or reserve credits").option("-f, --file <path>", "Pipeline file", DEFAULT_PIPELINE_FILE).option("--spec <path>", "Local behavior spec", DEFAULT_SPEC_FILE).option("--config <path>", "Local runtime config").option("--only <ids>", "Comma-separated step IDs to include").option("--skip <ids>", "Comma-separated step IDs to omit").action(async (options) => {
|
|
5692
|
+
pipeline.command("run").description("Run an ordered local pipeline, or safely preview any pipeline").option("--dry-run", "Resolve and display only; never execute, transfer, or reserve credits").option("-f, --file <path>", "Pipeline file", DEFAULT_PIPELINE_FILE).option("--spec <path>", "Local behavior spec", DEFAULT_SPEC_FILE).option("--config <path>", "Local runtime config").option("--output <path>", "Foundation run directory (must not already exist)").option("--resume <path>", "Resume a foundation run directory").option("--only <ids>", "Comma-separated step IDs to include").option("--skip <ids>", "Comma-separated step IDs to omit").action(async (options) => {
|
|
5693
|
+
if (options.output && options.resume) {
|
|
5694
|
+
throw new Error("--output and --resume are mutually exclusive.");
|
|
5695
|
+
}
|
|
5596
5696
|
const document = await resolvePipelineDocument(options);
|
|
5597
5697
|
const plan = createExecutionPlan(document, { only: parseList(options.only), skip: parseList(options.skip) });
|
|
5598
5698
|
const hostWarnings = await hostWarningsForPipeline(document, options.spec);
|
|
@@ -5621,12 +5721,17 @@ function registerPipelineCommands(parent) {
|
|
|
5621
5721
|
const result3 = await runFoundationPipeline({
|
|
5622
5722
|
spec: input.spec,
|
|
5623
5723
|
plan,
|
|
5624
|
-
specPath: resolve5(options.spec)
|
|
5724
|
+
specPath: resolve5(options.spec),
|
|
5725
|
+
...options.output || options.resume ? { outputDir: resolve5(options.resume ?? options.output) } : {},
|
|
5726
|
+
resume: Boolean(options.resume)
|
|
5625
5727
|
});
|
|
5626
5728
|
if (isJsonMode()) return printJson(result3);
|
|
5627
5729
|
printSuccess(`Foundation pipeline completed. Report: ${result3.report_path}`);
|
|
5628
5730
|
return;
|
|
5629
5731
|
}
|
|
5732
|
+
if (options.output || options.resume) {
|
|
5733
|
+
throw new Error("--output and --resume are only valid for foundation pipelines.");
|
|
5734
|
+
}
|
|
5630
5735
|
assertLocalRunInputReady(input.request);
|
|
5631
5736
|
const config = await loadLocalRunnerConfig(options.config ? resolve5(options.config) : void 0);
|
|
5632
5737
|
const localPipeline = {
|
|
@@ -6036,7 +6141,7 @@ async function runCli(version, runtime = {}) {
|
|
|
6036
6141
|
}
|
|
6037
6142
|
|
|
6038
6143
|
// src/index.ts
|
|
6039
|
-
runCli("0.13.1-beta.
|
|
6144
|
+
runCli("0.13.1-beta.6").catch((err) => {
|
|
6040
6145
|
reportError(err);
|
|
6041
6146
|
process.exit(1);
|
|
6042
6147
|
});
|