@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/README.md
CHANGED
|
@@ -141,7 +141,7 @@ guarantee that a model or dataset is compatible with a TT workflow.
|
|
|
141
141
|
|
|
142
142
|
For educational questions about how or why training works, the agent can read
|
|
143
143
|
the exact Python implementation shipped with the running TT build. The source
|
|
144
|
-
tool is limited to named foundation components (tokenizer, pretraining, model,
|
|
144
|
+
tool is limited to named foundation components (tokenizer, pretraining, checkpointing, model,
|
|
145
145
|
data, SFT, RL, evaluation, and common helpers) and adapter components (training,
|
|
146
146
|
data, model contract, and evaluation); it cannot read arbitrary paths. Answers
|
|
147
147
|
separate behavior directly visible in the code from inferred rationale and
|
|
@@ -206,16 +206,18 @@ Two local engines share that document version:
|
|
|
206
206
|
`inference` evaluators. Requires a foundation `tunedtensor.json`
|
|
207
207
|
(`engine: "foundation"`, at least two examples, no `base_model`).
|
|
208
208
|
|
|
209
|
-
The foundation engine is a
|
|
209
|
+
The foundation engine is a readable, single-GPU local trainer inspired by Andrej
|
|
210
210
|
Karpathy's [nanochat](https://github.com/karpathy/nanochat), not a port of its
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
211
|
+
distributed training stack. With `foundation.corpus_path`, it streams `.txt`
|
|
212
|
+
or JSONL (`{"text":"..."}`) shards into a deterministic on-disk token cache;
|
|
213
|
+
without that field it retains the bounded prompt/example corpus for smoke runs.
|
|
214
|
+
`foundation.validation_path` supplies a distinct held-out BPB corpus. Foundation
|
|
214
215
|
plans reject `compare` before creating run artifacts; comparison remains an
|
|
215
216
|
adapter-engine capability. Optional RL samples one seeded on-policy completion
|
|
216
217
|
per step with a sparse last-number exact reward and therefore requires a numeric
|
|
217
218
|
expected output for every example. Treat those metrics as an end-to-end
|
|
218
|
-
execution and overfit check—not held-out capability or multi-GPU evidence
|
|
219
|
+
execution and overfit check—not held-out capability or multi-GPU evidence when
|
|
220
|
+
no validation corpus is configured.
|
|
219
221
|
|
|
220
222
|
```bash
|
|
221
223
|
tt pipeline init --file pipeline.json
|
|
@@ -227,6 +229,7 @@ tt --json pipeline run --file pipeline.json \
|
|
|
227
229
|
--spec tunedtensor.json --config local-runner.json
|
|
228
230
|
tt init --engine foundation --name "Tiny GPT"
|
|
229
231
|
tt --json pipeline run --spec tunedtensor.json --dry-run
|
|
232
|
+
tt pipeline run --spec tunedtensor.json --resume /absolute/path/to/foundation-run
|
|
230
233
|
```
|
|
231
234
|
|
|
232
235
|
`--only` and `--skip` preserve dependency safety: a selected step cannot refer
|
|
@@ -234,6 +237,14 @@ to an omitted predecessor. Cloud-targeted steps fail closed unless you pass
|
|
|
234
237
|
`--dry-run`. Foundation documents are local-only. The TT agent may describe,
|
|
235
238
|
validate, and prepare a plan, but has no direct pipeline-execute tool.
|
|
236
239
|
|
|
240
|
+
Long pretraining uses BF16, warmup plus cosine decay, gradient clipping,
|
|
241
|
+
periodic metrics, and atomic rolling checkpoints. `--resume <run-directory>`
|
|
242
|
+
starts or resumes one stable run identity; completed stages are verified and
|
|
243
|
+
skipped, while an interrupted pretrain step restores model, optimizer,
|
|
244
|
+
scheduler, RNG, counters, and token cursor. See
|
|
245
|
+
[Long foundation runs](docs/local-runtime/foundation-long-runs.md) before an
|
|
246
|
+
unattended job.
|
|
247
|
+
|
|
237
248
|
## Quick start
|
|
238
249
|
|
|
239
250
|
Create a local project on an NVIDIA host:
|
|
@@ -1176,7 +1176,7 @@ function warningsFromSnapshot(report, target) {
|
|
|
1176
1176
|
}
|
|
1177
1177
|
|
|
1178
1178
|
// src/local-runtime/host-inventory.ts
|
|
1179
|
-
import { constants } from "fs";
|
|
1179
|
+
import { constants as constants2 } from "fs";
|
|
1180
1180
|
import { access, statfs } from "fs/promises";
|
|
1181
1181
|
import { cpus, freemem, homedir as homedir3, totalmem, type } from "os";
|
|
1182
1182
|
import { dirname as dirname5, join as join5, resolve as resolve7 } from "path";
|
|
@@ -1334,8 +1334,8 @@ function withOfflineHuggingFaceCacheEnvironment(env, modelCache) {
|
|
|
1334
1334
|
// src/local-runtime/process-runner.ts
|
|
1335
1335
|
import { createHash as createHash2 } from "crypto";
|
|
1336
1336
|
import { spawn } from "child_process";
|
|
1337
|
-
import {
|
|
1338
|
-
import { mkdir as mkdir2 } from "fs/promises";
|
|
1337
|
+
import { constants, readFileSync } from "fs";
|
|
1338
|
+
import { mkdir as mkdir2, open as open2 } from "fs/promises";
|
|
1339
1339
|
import { dirname as dirname4, join as join4, resolve as resolve6 } from "path";
|
|
1340
1340
|
var packageRoot = localRuntimePackageRoot(import.meta.url);
|
|
1341
1341
|
var bundledProject = join4(packageRoot, "training/adapter");
|
|
@@ -1381,6 +1381,7 @@ function withBundledPythonEnvironment(env) {
|
|
|
1381
1381
|
};
|
|
1382
1382
|
}
|
|
1383
1383
|
function buildFoundationPythonCommand(entrypoint, args = []) {
|
|
1384
|
+
const target = entrypoint === "-c" ? entrypoint : join4(foundationProject, "src", entrypoint);
|
|
1384
1385
|
const commandArgs = [
|
|
1385
1386
|
"run",
|
|
1386
1387
|
"--frozen",
|
|
@@ -1388,7 +1389,7 @@ function buildFoundationPythonCommand(entrypoint, args = []) {
|
|
|
1388
1389
|
"--project",
|
|
1389
1390
|
foundationProject,
|
|
1390
1391
|
"python",
|
|
1391
|
-
|
|
1392
|
+
target,
|
|
1392
1393
|
...args
|
|
1393
1394
|
];
|
|
1394
1395
|
return {
|
|
@@ -1409,28 +1410,19 @@ var ProcessCancelledError = class extends Error {
|
|
|
1409
1410
|
this.name = "ProcessCancelledError";
|
|
1410
1411
|
}
|
|
1411
1412
|
};
|
|
1412
|
-
async function openProcessLog(path, exclusive) {
|
|
1413
|
-
const
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
};
|
|
1422
|
-
const onError = (error) => {
|
|
1423
|
-
stream.off("open", onOpen);
|
|
1424
|
-
reject(error);
|
|
1425
|
-
};
|
|
1426
|
-
stream.once("open", onOpen);
|
|
1427
|
-
stream.once("error", onError);
|
|
1428
|
-
});
|
|
1429
|
-
return stream;
|
|
1413
|
+
async function openProcessLog(path, exclusive, append) {
|
|
1414
|
+
const flags = constants.O_WRONLY | constants.O_CREAT | (exclusive ? constants.O_EXCL : append ? constants.O_APPEND : constants.O_TRUNC) | (process.platform === "win32" ? 0 : constants.O_NOFOLLOW);
|
|
1415
|
+
const handle = await open2(path, flags, 384);
|
|
1416
|
+
try {
|
|
1417
|
+
return handle.createWriteStream({ autoClose: true });
|
|
1418
|
+
} catch (error) {
|
|
1419
|
+
await handle.close();
|
|
1420
|
+
throw error;
|
|
1421
|
+
}
|
|
1430
1422
|
}
|
|
1431
1423
|
async function runLoggedProcess(args) {
|
|
1432
1424
|
if (args.logPath) await mkdir2(dirname4(args.logPath), { recursive: true });
|
|
1433
|
-
const logStream = args.logPath ? await openProcessLog(args.logPath, args.exclusiveLog ?? false) : null;
|
|
1425
|
+
const logStream = args.logPath ? await openProcessLog(args.logPath, args.exclusiveLog ?? false, args.appendLog ?? false) : null;
|
|
1434
1426
|
let stderr = "";
|
|
1435
1427
|
try {
|
|
1436
1428
|
const exitCode = await new Promise((resolvePromise, reject) => {
|
|
@@ -1458,7 +1450,7 @@ async function runLoggedProcess(args) {
|
|
|
1458
1450
|
const requestStop = (signal = "SIGTERM") => {
|
|
1459
1451
|
killProcessGroup(signal);
|
|
1460
1452
|
if (!forceKillTimer) {
|
|
1461
|
-
forceKillTimer = setTimeout(() => killProcessGroup("SIGKILL"), 5e3);
|
|
1453
|
+
forceKillTimer = setTimeout(() => killProcessGroup("SIGKILL"), args.shutdownGraceMs ?? 5e3);
|
|
1462
1454
|
forceKillTimer.unref();
|
|
1463
1455
|
}
|
|
1464
1456
|
};
|
|
@@ -1705,7 +1697,7 @@ async function queryNvidiaGpus(env) {
|
|
|
1705
1697
|
async function diskCheck(name, path) {
|
|
1706
1698
|
const resolvedPath = resolve7(path);
|
|
1707
1699
|
try {
|
|
1708
|
-
await access(resolvedPath,
|
|
1700
|
+
await access(resolvedPath, constants2.R_OK);
|
|
1709
1701
|
const fs = await statfs(resolvedPath);
|
|
1710
1702
|
const freeBytes = Number(fs.bavail) * Number(fs.bsize);
|
|
1711
1703
|
return {
|
|
@@ -2538,14 +2530,30 @@ var specSnapshotSchema = z.object({
|
|
|
2538
2530
|
}).strict();
|
|
2539
2531
|
var foundationHyperparametersSchema = z.object({
|
|
2540
2532
|
vocab_size: z.number().int().min(64).max(65536).default(256),
|
|
2541
|
-
max_chars: z.number().int().min(1e3).max(
|
|
2533
|
+
max_chars: z.number().int().min(1e3).max(1e12).default(2e4),
|
|
2542
2534
|
depth: z.number().int().min(1).max(64).default(2),
|
|
2543
2535
|
pretrain_steps: z.number().int().min(1).max(1e7).default(2),
|
|
2544
2536
|
finetune_steps: z.number().int().min(1).max(1e7).default(2),
|
|
2545
2537
|
rl_steps: z.number().int().min(0).max(1e7).default(0),
|
|
2546
2538
|
batch_size: z.number().int().min(1).max(1024).default(2),
|
|
2547
2539
|
sequence_length: z.number().int().min(16).max(8192).default(64),
|
|
2548
|
-
nproc_per_node: z.number().int().min(1).max(16).default(1)
|
|
2540
|
+
nproc_per_node: z.number().int().min(1).max(16).default(1),
|
|
2541
|
+
corpus_path: z.string().min(1).optional(),
|
|
2542
|
+
tokenizer_max_chars: z.number().int().min(1e3).max(1e12).optional(),
|
|
2543
|
+
validation_path: z.string().min(1).optional(),
|
|
2544
|
+
checkpoint_backup_dir: z.string().min(1).optional(),
|
|
2545
|
+
seed: z.number().int().min(0).max(2147483647).optional(),
|
|
2546
|
+
learning_rate: z.number().positive().max(1).optional(),
|
|
2547
|
+
weight_decay: z.number().min(0).max(10).optional(),
|
|
2548
|
+
warmup_steps: z.number().int().min(0).max(1e7).optional(),
|
|
2549
|
+
min_lr_ratio: z.number().min(0).max(1).optional(),
|
|
2550
|
+
gradient_accumulation_steps: z.number().int().min(1).max(1024).optional(),
|
|
2551
|
+
gradient_clip: z.number().positive().max(1e3).optional(),
|
|
2552
|
+
bf16: z.boolean().optional(),
|
|
2553
|
+
checkpoint_interval_steps: z.number().int().min(1).max(1e7).optional(),
|
|
2554
|
+
checkpoint_interval_seconds: z.number().int().min(1).max(86400).optional(),
|
|
2555
|
+
keep_checkpoints: z.number().int().min(1).max(100).optional(),
|
|
2556
|
+
log_interval_steps: z.number().int().min(1).max(1e6).optional()
|
|
2549
2557
|
}).strict();
|
|
2550
2558
|
var localFoundationSpecFileSchema = z.object({
|
|
2551
2559
|
engine: z.literal("foundation"),
|
|
@@ -2799,7 +2807,7 @@ function isFoundationSpecFile(spec) {
|
|
|
2799
2807
|
// src/local-runtime/local-project.ts
|
|
2800
2808
|
import { createHash as createHash3, randomUUID as randomUUID3 } from "crypto";
|
|
2801
2809
|
import { readFile as readFile4, stat as stat3, writeFile as writeFile4 } from "fs/promises";
|
|
2802
|
-
import { dirname as dirname8, isAbsolute as isAbsolute3, resolve as resolve9 } from "path";
|
|
2810
|
+
import { dirname as dirname8, isAbsolute as isAbsolute3, relative as relative2, resolve as resolve9, sep as sep3 } from "path";
|
|
2803
2811
|
import { homedir as homedir4 } from "os";
|
|
2804
2812
|
import { mkdir as mkdir5 } from "fs/promises";
|
|
2805
2813
|
var DEFAULT_LOCAL_SPEC_PATH = "tunedtensor.json";
|
|
@@ -2811,6 +2819,13 @@ function resolveLocalReference(value, baseDirectory) {
|
|
|
2811
2819
|
if (value.startsWith("~/")) return resolve9(homedir4(), value.slice(2));
|
|
2812
2820
|
return resolve9(baseDirectory, value);
|
|
2813
2821
|
}
|
|
2822
|
+
function localPathsOverlap(left, right) {
|
|
2823
|
+
const contains = (parent, candidate) => {
|
|
2824
|
+
const relation = relative2(resolve9(parent), resolve9(candidate));
|
|
2825
|
+
return relation === "" || !isAbsolute3(relation) && relation !== ".." && !relation.startsWith(`..${sep3}`);
|
|
2826
|
+
};
|
|
2827
|
+
return contains(left, right) || contains(right, left);
|
|
2828
|
+
}
|
|
2814
2829
|
function resolveLocalRunInputPaths(raw, inputPath) {
|
|
2815
2830
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return raw;
|
|
2816
2831
|
const baseDirectory = dirname8(resolve9(inputPath));
|
|
@@ -2822,6 +2837,13 @@ function resolveLocalRunInputPaths(raw, inputPath) {
|
|
|
2822
2837
|
if (fields[key] !== void 0) fields[key] = resolveLocalReference(fields[key], baseDirectory);
|
|
2823
2838
|
}
|
|
2824
2839
|
}
|
|
2840
|
+
const foundation = value.foundation;
|
|
2841
|
+
if (foundation && typeof foundation === "object" && !Array.isArray(foundation)) {
|
|
2842
|
+
const fields = foundation;
|
|
2843
|
+
for (const key of ["corpus_path", "validation_path", "checkpoint_backup_dir"]) {
|
|
2844
|
+
if (fields[key] !== void 0) fields[key] = resolveLocalReference(fields[key], baseDirectory);
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2825
2847
|
return value;
|
|
2826
2848
|
}
|
|
2827
2849
|
async function exists2(path) {
|
|
@@ -3138,7 +3160,7 @@ async function evaluationSuiteFromChatJsonl(path, configuredSystem) {
|
|
|
3138
3160
|
import { createHash as createHash4 } from "crypto";
|
|
3139
3161
|
import { createReadStream as createReadStream2 } from "fs";
|
|
3140
3162
|
import { lstat as lstat4, mkdir as mkdir6, readFile as readFile6, readdir as readdir4, realpath as realpath2, stat as stat4 } from "fs/promises";
|
|
3141
|
-
import { isAbsolute as isAbsolute4, join as join8, relative as
|
|
3163
|
+
import { isAbsolute as isAbsolute4, join as join8, relative as relative3, resolve as resolve10, sep as sep4 } from "path";
|
|
3142
3164
|
var NEMOTRON_BF16_FILE_DIGESTS = {
|
|
3143
3165
|
".gitattributes": { algorithm: "sha1", digest: "fe3f694ce14b577c9833fca6e83c5e26a7119127" },
|
|
3144
3166
|
"LICENSE": { algorithm: "sha1", digest: "4d76cc87c9edb280753e3bee3b499ae461113835" },
|
|
@@ -3204,8 +3226,8 @@ async function certifiedFileDigest(path, expected) {
|
|
|
3204
3226
|
return digest.digest("hex");
|
|
3205
3227
|
}
|
|
3206
3228
|
function isStrictPhysicalDescendant(parent, child) {
|
|
3207
|
-
const candidate =
|
|
3208
|
-
return candidate !== "" && candidate !== ".." && !candidate.startsWith(`..${
|
|
3229
|
+
const candidate = relative3(parent, child);
|
|
3230
|
+
return candidate !== "" && candidate !== ".." && !candidate.startsWith(`..${sep4}`) && !isAbsolute4(candidate);
|
|
3209
3231
|
}
|
|
3210
3232
|
async function verifySnapshotCacheLocation(path, modelId, revision, modelCache) {
|
|
3211
3233
|
const repository = `models--${modelId.replaceAll("/", "--")}`;
|
|
@@ -3297,7 +3319,7 @@ async function verifyCertifiedLocalSnapshot(path, modelId, snapshotFiles, modelC
|
|
|
3297
3319
|
}
|
|
3298
3320
|
physicalFiles.set(file, target);
|
|
3299
3321
|
}
|
|
3300
|
-
const actualFiles = new Set(snapshotFiles.map((file) =>
|
|
3322
|
+
const actualFiles = new Set(snapshotFiles.map((file) => relative3(path, file).split("\\").join("/")));
|
|
3301
3323
|
const expectedFiles = new Set(Object.keys(digests));
|
|
3302
3324
|
const unexpected = [...actualFiles].filter((name) => !expectedFiles.has(name)).sort();
|
|
3303
3325
|
if (unexpected.length > 0) {
|
|
@@ -3578,12 +3600,12 @@ function evaluateGeneralRegressionGate(comparison, policy) {
|
|
|
3578
3600
|
}
|
|
3579
3601
|
|
|
3580
3602
|
// src/local-runtime/orchestrator.ts
|
|
3581
|
-
import { lstat as lstat5, mkdir as mkdir9, open as
|
|
3603
|
+
import { lstat as lstat5, mkdir as mkdir9, open as open3, readFile as readFile9, readdir as readdir5, rename as rename3, rm as rm3, stat as stat5 } from "fs/promises";
|
|
3582
3604
|
import { createReadStream as createReadStream3 } from "fs";
|
|
3583
3605
|
import { createHash as createHash6, randomUUID as randomUUID4 } from "crypto";
|
|
3584
3606
|
import { performance } from "perf_hooks";
|
|
3585
3607
|
import { homedir as homedir5 } from "os";
|
|
3586
|
-
import { basename, dirname as dirname10, join as join11, relative as
|
|
3608
|
+
import { basename, dirname as dirname10, join as join11, relative as relative4, resolve as resolve13 } from "path";
|
|
3587
3609
|
|
|
3588
3610
|
// src/local-runtime/evaluation.ts
|
|
3589
3611
|
import { createHash as createHash5 } from "crypto";
|
|
@@ -4678,7 +4700,7 @@ async function fingerprintLocalBaseModel(uri, expectedModelId, modelCache) {
|
|
|
4678
4700
|
}
|
|
4679
4701
|
if (!childMetadata.isFile()) continue;
|
|
4680
4702
|
files.push({
|
|
4681
|
-
path:
|
|
4703
|
+
path: relative4(root, child).split("\\").join("/"),
|
|
4682
4704
|
size_bytes: childMetadata.size,
|
|
4683
4705
|
sha256: await hashFile(child)
|
|
4684
4706
|
});
|
|
@@ -4761,7 +4783,7 @@ async function cleanupStageArtifacts(artifacts, stage) {
|
|
|
4761
4783
|
await removeReport();
|
|
4762
4784
|
}
|
|
4763
4785
|
function manifestRelativePath(artifacts, path) {
|
|
4764
|
-
return
|
|
4786
|
+
return relative4(artifacts.runDir, path).split("\\").join("/");
|
|
4765
4787
|
}
|
|
4766
4788
|
async function verifyReusableArtifacts(artifacts, paths, options = {}) {
|
|
4767
4789
|
if (!await pathExists2(artifacts.artifactManifestJson)) return false;
|
|
@@ -4782,7 +4804,7 @@ async function acquireWorkflowLock(lockPath, description) {
|
|
|
4782
4804
|
const token = randomUUID4();
|
|
4783
4805
|
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
4784
4806
|
try {
|
|
4785
|
-
const handle = await
|
|
4807
|
+
const handle = await open3(lockPath, "wx", 384);
|
|
4786
4808
|
try {
|
|
4787
4809
|
await handle.writeFile(`${JSON.stringify({ token, pid: process.pid, created_at: (/* @__PURE__ */ new Date()).toISOString() })}
|
|
4788
4810
|
`);
|
|
@@ -5773,6 +5795,7 @@ export {
|
|
|
5773
5795
|
localRunnerConfigSchema,
|
|
5774
5796
|
isFoundationSpecFile,
|
|
5775
5797
|
DEFAULT_LOCAL_SPEC_PATH,
|
|
5798
|
+
localPathsOverlap,
|
|
5776
5799
|
resolveLocalRunInputPaths,
|
|
5777
5800
|
initLocalSpecFile,
|
|
5778
5801
|
initLocalRunnerConfigFile,
|
|
@@ -5805,4 +5828,4 @@ export {
|
|
|
5805
5828
|
runLocalPipeline,
|
|
5806
5829
|
runLocalFineTune
|
|
5807
5830
|
};
|
|
5808
|
-
//# sourceMappingURL=chunk-
|
|
5831
|
+
//# sourceMappingURL=chunk-W7MOBPCO.js.map
|