@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 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 small, readable local baseline inspired by Andrej
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
- compute-optimal training stack. The first version deliberately builds a bounded
212
- pretraining corpus from the spec prompt and examples, reports chat accuracy on
213
- those same contract examples, and supports one training process. Foundation
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 { createWriteStream, readFileSync } from "fs";
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
- join4(foundationProject, "src", entrypoint),
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 stream = createWriteStream(path, {
1414
- flags: exclusive ? "wx" : "w",
1415
- mode: 384
1416
- });
1417
- await new Promise((resolveOpen, reject) => {
1418
- const onOpen = () => {
1419
- stream.off("error", onError);
1420
- resolveOpen();
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, constants.R_OK);
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(2e9).default(2e4),
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 relative2, resolve as resolve10, sep as sep3 } from "path";
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 = relative2(parent, child);
3208
- return candidate !== "" && candidate !== ".." && !candidate.startsWith(`..${sep3}`) && !isAbsolute4(candidate);
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) => relative2(path, file).split("\\").join("/")));
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 open2, readFile as readFile9, readdir as readdir5, rename as rename3, rm as rm3, stat as stat5 } from "fs/promises";
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 relative3, resolve as resolve13 } from "path";
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: relative3(root, child).split("\\").join("/"),
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 relative3(artifacts.runDir, path).split("\\").join("/");
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 open2(lockPath, "wx", 384);
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-AVMRHDFY.js.map
5831
+ //# sourceMappingURL=chunk-W7MOBPCO.js.map