@tuned-tensor/cli 0.13.1-beta.4 → 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.
@@ -5,9 +5,11 @@ import {
5
5
  assertFoundationSpecReady,
6
6
  assertLocalRunInputReady,
7
7
  assertUsableModelArtifact,
8
+ assessHardware,
8
9
  baseModelRevisionSchema,
9
10
  behaviorSpecExampleSchema,
10
11
  buildBundledPythonCommand,
12
+ buildFoundationPythonCommand,
11
13
  buildModelPrefetchPayload,
12
14
  buildSystemMessage,
13
15
  canonicalLocalPipeline,
@@ -46,6 +48,7 @@ import {
46
48
  localAdapterSpecFileSchema,
47
49
  localBehaviorSpecFileSchema,
48
50
  localFoundationSpecFileSchema,
51
+ localPathsOverlap,
49
52
  localRunnerConfigSchema,
50
53
  localRuntimePackageRoot,
51
54
  localStorePaths,
@@ -70,10 +73,11 @@ import {
70
73
  verifyLocalBaseModel,
71
74
  verifyModelPrefetchCacheReport,
72
75
  withBundledPythonEnvironment,
76
+ withFoundationPythonEnvironment,
73
77
  withHuggingFaceCacheEnvironment,
74
78
  withOfflineHuggingFaceCacheEnvironment,
75
79
  writePrivateJsonAtomic
76
- } from "./chunk-7VIUHQBI.js";
80
+ } from "./chunk-W7MOBPCO.js";
77
81
 
78
82
  // src/local-runtime/index.ts
79
83
  import { readFileSync, realpathSync } from "fs";
@@ -145,7 +149,7 @@ function compareRuns(a, b) {
145
149
 
146
150
  // src/local-runtime/doctor.ts
147
151
  import { constants } from "fs";
148
- import { access, mkdir, statfs, unlink, writeFile } from "fs/promises";
152
+ import { access, lstat, mkdir, statfs, unlink, writeFile } from "fs/promises";
149
153
  import { homedir } from "os";
150
154
  import { join, resolve } from "path";
151
155
  import { spawn } from "child_process";
@@ -265,6 +269,23 @@ async function writableDirectoryCheck(name, path) {
265
269
  };
266
270
  }
267
271
  }
272
+ function foundationPythonProbePlan(requireBf16) {
273
+ const source = [
274
+ "import json",
275
+ "import numpy, safetensors, tokenizers, torch",
276
+ "assert torch.cuda.is_available(), 'TT Foundation training requires CUDA but torch.cuda.is_available() is false'",
277
+ ...requireBf16 ? ["assert torch.cuda.is_bf16_supported(), 'TT Foundation BF16 training was requested but this device does not support BF16'"] : [],
278
+ "props = torch.cuda.get_device_properties(0)",
279
+ "print(json.dumps({'python_ok': True, 'torch': torch.__version__, 'cuda_available': True, 'bf16': torch.cuda.is_bf16_supported(), 'cuda_device': torch.cuda.get_device_name(0), 'compute_capability': list(torch.cuda.get_device_capability(0)), 'total_memory_bytes': props.total_memory}))"
280
+ ].join("; ");
281
+ const entrypoint = buildFoundationPythonCommand("-c", [source]);
282
+ return {
283
+ name: "python-runtime",
284
+ command: entrypoint.command,
285
+ args: entrypoint.commandArgs,
286
+ env: withFoundationPythonEnvironment(process.env)
287
+ };
288
+ }
268
289
  function pythonProbeSource(device) {
269
290
  return [
270
291
  "import json",
@@ -310,6 +331,26 @@ function foundationPlaceholderCheck(spec) {
310
331
  message: issues.length > 0 ? "The spec still contains generated placeholder content; edit it before training." : `${spec.examples.length} spec example(s); foundation engine`
311
332
  };
312
333
  }
334
+ async function foundationCorpusCheck(name, path) {
335
+ const resolvedPath = resolve(path);
336
+ try {
337
+ const info = await lstat(resolvedPath);
338
+ const supported = info.isFile() || info.isDirectory();
339
+ return {
340
+ name,
341
+ ok: supported && !info.isSymbolicLink(),
342
+ message: supported && !info.isSymbolicLink() ? `${resolvedPath} is available for streaming.` : `${resolvedPath} must be a regular file or directory, not a symbolic link.`,
343
+ details: { path: resolvedPath }
344
+ };
345
+ } catch (error) {
346
+ return {
347
+ name,
348
+ ok: false,
349
+ message: `${resolvedPath}: ${error instanceof Error ? error.message : String(error)}`,
350
+ details: { path: resolvedPath }
351
+ };
352
+ }
353
+ }
313
354
  function placeholderSpecCheck(request) {
314
355
  const placeholder = request.spec_snapshot.examples.some(
315
356
  (example) => /replace this with/i.test(example.input) || /replace this with/i.test(example.output)
@@ -359,11 +400,33 @@ async function runDoctor(config, request, foundationSpec) {
359
400
  }
360
401
  if (foundationSpec) {
361
402
  checks.push(foundationPlaceholderCheck(foundationSpec));
403
+ if (foundationSpec.foundation.corpus_path) {
404
+ checks.push(await foundationCorpusCheck("pretraining-corpus", foundationSpec.foundation.corpus_path));
405
+ }
406
+ if (foundationSpec.foundation.validation_path) {
407
+ checks.push(await foundationCorpusCheck("validation-corpus", foundationSpec.foundation.validation_path));
408
+ }
409
+ if (foundationSpec.foundation.corpus_path && foundationSpec.foundation.validation_path && localPathsOverlap(
410
+ foundationSpec.foundation.corpus_path,
411
+ foundationSpec.foundation.validation_path
412
+ )) {
413
+ checks.push({
414
+ name: "held-out-corpus",
415
+ ok: false,
416
+ message: "foundation.corpus_path and foundation.validation_path must be distinct."
417
+ });
418
+ }
419
+ if (foundationSpec.foundation.checkpoint_backup_dir) {
420
+ checks.push(await writableDirectoryCheck(
421
+ "checkpoint-backup",
422
+ foundationSpec.foundation.checkpoint_backup_dir
423
+ ));
424
+ }
362
425
  } else if (request) {
363
426
  checks.push(placeholderSpecCheck(request));
364
427
  resolveTrainingModel(request.spec_snapshot.base_model);
365
428
  }
366
- const pythonPlans = buildDoctorPythonPlans(config);
429
+ const pythonPlans = foundationSpec && !config.dryRun ? [foundationPythonProbePlan(foundationSpec.foundation.bf16 !== false)] : buildDoctorPythonPlans(config);
367
430
  if (pythonPlans.length > 0) {
368
431
  const uvVersion = await runCommand("uv", ["--version"]);
369
432
  checks.push({
@@ -416,20 +479,20 @@ async function runDoctor(config, request, foundationSpec) {
416
479
  message: config.dryRun ? "GPU checks skipped because dryRun is enabled." : `nvidia-smi is not required for device=${device}.`
417
480
  });
418
481
  }
419
- const trainingCommand = buildBundledPythonCommand("train.py").displayCommand;
482
+ const trainingCommand = foundationSpec ? buildFoundationPythonCommand("pretrain.py").displayCommand : buildBundledPythonCommand("train.py").displayCommand;
420
483
  checks.push({
421
484
  name: "effective-plan",
422
485
  ok: true,
423
486
  message: "Resolved the configured training and evaluation plan.",
424
487
  details: {
425
488
  training_command: trainingCommand ?? null,
426
- evaluation_provider: "transformers",
489
+ evaluation_provider: foundationSpec ? "foundation" : "transformers",
427
490
  evaluation_device: device,
428
491
  artifact_root: resolve(config.artifactRoot),
429
492
  store_root: resolve(config.storeRoot ?? defaultLocalHome()),
430
493
  model_cache: resolve(config.paths.modelCache ?? process.env.HF_HOME ?? join(homedir(), ".cache", "huggingface")),
431
494
  base_model: request?.spec_snapshot.base_model ?? null,
432
- scoring_mode: config.evaluation.scoring.mode
495
+ scoring_mode: foundationSpec ? "bpb/chat" : config.evaluation.scoring.mode
433
496
  }
434
497
  });
435
498
  return checks;
@@ -741,6 +804,7 @@ Commands:
741
804
  info Show package and runner information
742
805
  init [--name "My Local Model"] [--model Qwen/Qwen3.5-2B] [--output tunedtensor.json] [--profile spark] [--force]
743
806
  doctor [tunedtensor.json] [--config local-runner.json]
807
+ hardware [--config local-runner.json] [--quick]
744
808
  validate [tunedtensor.json] [--config local-runner.json]
745
809
  run [tunedtensor.json] [--config local-runner.json] [--dry-run] [--verbose] [--quiet]
746
810
  serve <model-id|active|base> [--config local-runner.json] [--host 127.0.0.1] [--port 8000]
@@ -801,6 +865,15 @@ var COMMAND_DEFINITIONS = {
801
865
  options: [CONFIG_OPTION],
802
866
  maxPositionals: 1
803
867
  },
868
+ hardware: {
869
+ usage: "tt hardware [--config path] [--quick]",
870
+ description: "Inventory this host and report what TT can train, fine-tune, or infer.",
871
+ options: [
872
+ CONFIG_OPTION,
873
+ { name: "--quick", description: "Skip the bundled torch/uv runtime probe" }
874
+ ],
875
+ maxPositionals: 0
876
+ },
804
877
  validate: {
805
878
  usage: "tt validate [tunedtensor.json] [options]",
806
879
  description: "Validate a local behavior spec without executing it.",
@@ -1430,9 +1503,28 @@ async function main(argv) {
1430
1503
  config_path: configSelection.path ?? null,
1431
1504
  checks
1432
1505
  });
1506
+ try {
1507
+ await assessHardware({
1508
+ config: configSelection.config,
1509
+ quick: true
1510
+ });
1511
+ } catch {
1512
+ }
1433
1513
  if (!ok) process.exitCode = 1;
1434
1514
  return;
1435
1515
  }
1516
+ if (command === "hardware") {
1517
+ const configSelection = await configSelectionFromArgv(argv);
1518
+ const report = await assessHardware({
1519
+ config: configSelection.config,
1520
+ quick: hasFlag(argv, "--quick")
1521
+ });
1522
+ printJson({
1523
+ ...report,
1524
+ config_path: configSelection.path ?? null
1525
+ });
1526
+ return;
1527
+ }
1436
1528
  if (command === "init") {
1437
1529
  const outputPath = resolve3(readOption(argv, "--output") ?? DEFAULT_LOCAL_SPEC_PATH);
1438
1530
  const profile = readOption(argv, "--profile");
@@ -1758,6 +1850,7 @@ export {
1758
1850
  localAdapterSpecFileSchema,
1759
1851
  localBehaviorSpecFileSchema,
1760
1852
  localFoundationSpecFileSchema,
1853
+ localPathsOverlap,
1761
1854
  localRunnerConfigSchema,
1762
1855
  localStorePaths,
1763
1856
  normalizeChatJsonlForRelocation,