@tuned-tensor/cli 0.23.0 → 0.25.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.
@@ -2,113 +2,127 @@
2
2
  import {
3
3
  DEFAULT_LOCAL_SPEC_PATH,
4
4
  DatasetNotDownloadedError,
5
- assertArtifactManifest,
6
5
  assertFoundationSpecReady,
7
6
  assertLocalRunInputReady,
8
- assertUsableModelArtifact,
9
7
  assessHardware,
10
8
  auditRun,
9
+ buildSystemMessage,
10
+ canonicalLocalPipeline,
11
+ checkAwsGpu,
12
+ compileSpecToJsonl,
13
+ createLocalStore,
14
+ createRunConsoleReporter,
15
+ datasetInputIdentity,
16
+ decisionPlaceholderIssues,
17
+ defaultLocalHome,
18
+ ensureDatasetCached,
19
+ evaluateGeneralRegressionGate,
20
+ evaluationSuiteFromChatJsonl,
21
+ exampleToChatRow,
22
+ examplesFromChatJsonl,
23
+ examplesFromSpec,
24
+ fingerprintLocalBaseModel,
25
+ foundationPlaceholderIssues,
26
+ generatedPlaceholderIssues,
27
+ hardenExistingLocalStore,
28
+ huggingFaceDataset,
29
+ initLocalRunnerConfigFile,
30
+ initLocalSpecFile,
31
+ isTerminalRunState,
32
+ loadJsonFile,
33
+ loadLocalRunInput,
34
+ loadLocalRunnerConfig,
35
+ loadRunRequest,
36
+ localPathsOverlap,
37
+ localStorePaths,
38
+ normalizeChatJsonlForRelocation,
39
+ parseLocalRunInput,
40
+ parseLocalRunnerConfig,
41
+ pipelineForRunInput,
42
+ prefetchHuggingFaceDataset,
43
+ resolveDownloadConfig,
44
+ resolveLocalRunInputPaths,
45
+ resolveProjectConfig,
46
+ runGpuProcess,
47
+ runLocalFineTune,
48
+ runLocalPipeline,
49
+ runRequestFromLocalSpec,
50
+ specHash,
51
+ tokenF1,
52
+ validateBehaviorSpec,
53
+ validateLocalFineTuneInput,
54
+ validateLocalPipeline,
55
+ writePrivateJsonAtomic
56
+ } from "./chunk-7RJGLRMO.js";
57
+ import {
58
+ applySpecLock,
59
+ assertArtifactManifest,
60
+ assertUsableModelArtifact,
11
61
  baseModelRevisionSchema,
12
62
  behaviorSpecExampleSchema,
13
63
  behaviorSpecSchema,
14
64
  buildBundledPythonCommand,
15
65
  buildFoundationPythonCommand,
16
66
  buildModelPrefetchPayload,
17
- buildSystemMessage,
18
- canonicalLocalPipeline,
19
- checkAwsGpu,
67
+ buildModelSourcePrefetchPayload,
20
68
  comparisonReportSchema,
21
- compileSpecToJsonl,
22
- createLocalStore,
23
- createRunConsoleReporter,
24
69
  datasetColumnsSchema,
25
70
  datasetFormatSchema,
26
- datasetInputIdentity,
27
71
  datasetPrebuiltSchema,
28
72
  datasetSourceSchema,
29
73
  decisionHyperparametersSchema,
30
74
  decisionLabels,
31
- decisionPlaceholderIssues,
32
75
  decisionQuestionSchema,
33
76
  defaultBaseModelRevision,
34
- defaultLocalHome,
35
- ensureDatasetCached,
77
+ enforceSpecLock,
36
78
  evalExampleResultSchema,
37
79
  evalReportSchema,
38
80
  evalSplitSchema,
39
- evaluateGeneralRegressionGate,
40
- evaluationSuiteFromChatJsonl,
41
- exampleToChatRow,
42
- examplesFromChatJsonl,
43
- examplesFromSpec,
81
+ findAsset,
44
82
  fineTuneHyperparametersSchema,
45
83
  fineTuneRunRequestSchema,
46
- fingerprintLocalBaseModel,
47
84
  forwardStreamLines,
48
85
  foundationHyperparametersSchema,
49
- foundationPlaceholderIssues,
50
- generatedPlaceholderIssues,
51
- hardenExistingLocalStore,
52
- huggingFaceDataset,
53
86
  huggingFaceDatasetSourceSchema,
54
87
  huggingFaceRepoIdSchema,
55
- initLocalRunnerConfigFile,
56
- initLocalSpecFile,
57
88
  isDecisionSpecFile,
58
89
  isFoundationSpecFile,
59
90
  isRepoRelativePath,
60
- isTerminalRunState,
61
91
  jsonFieldMetricsSchema,
62
- loadJsonFile,
63
- loadLocalRunInput,
64
- loadLocalRunnerConfig,
65
- loadRunRequest,
92
+ listAssets,
66
93
  localAdapterSpecFileSchema,
67
94
  localBehaviorSpecFileSchema,
68
95
  localDecisionSpecFileSchema,
69
96
  localFoundationSpecFileSchema,
70
- localPathsOverlap,
71
97
  localRunnerConfigSchema,
72
98
  localRuntimePackageRoot,
73
- localStorePaths,
74
99
  minimalMachineLearningEnvironment,
75
- normalizeChatJsonlForRelocation,
76
- parseLocalRunInput,
77
- parseLocalRunnerConfig,
78
- pipelineForRunInput,
79
100
  prefetchBaseModel,
80
- prefetchHuggingFaceDataset,
81
101
  projectRuntimeSchema,
82
102
  pythonEnvironmentPath,
103
+ readSpecLock,
83
104
  reportInBackground,
84
- resolveLocalRunInputPaths,
85
105
  resolveModelLoader,
86
- resolveProjectConfig,
87
106
  resolveRequestedBaseModelRevision,
88
107
  resolveTrainingModel,
89
- runGpuProcess,
90
- runLocalFineTune,
91
- runLocalPipeline,
92
108
  runProvenanceSchema,
93
109
  runReportSchema,
94
- runRequestFromLocalSpec,
95
110
  safeName,
96
111
  sanitizeLogLine,
97
- specHash,
112
+ snapshotDigests,
113
+ specLockPath,
98
114
  specSnapshotSchema,
99
- tokenF1,
100
115
  trainingReportSchema,
101
- validateBehaviorSpec,
102
- validateLocalFineTuneInput,
103
- validateLocalPipeline,
116
+ verifyAsset,
104
117
  verifyLocalBaseModel,
105
118
  verifyModelPrefetchCacheReport,
106
119
  withBundledPythonEnvironment,
107
120
  withFoundationPythonEnvironment,
108
121
  withHuggingFaceCacheEnvironment,
109
122
  withOfflineHuggingFaceCacheEnvironment,
110
- writePrivateJsonAtomic
111
- } from "./chunk-RPWOCVIL.js";
123
+ withProgressHeartbeat,
124
+ writeSpecLockEntry
125
+ } from "./chunk-UCJCWJ6F.js";
112
126
 
113
127
  // src/local-runtime/index.ts
114
128
  import { readFileSync as readFileSync3, realpathSync } from "fs";
@@ -948,7 +962,8 @@ Commands:
948
962
  serve <model-id|active|base|foundation> [--config local-runner.json] [--host 127.0.0.1] [--port 8000]
949
963
  runs list|get|events|report|compare|audit [args] [--config local-runner.json]
950
964
  models list|get|verify|prefetch|verify-base|active|activate|rollback|serve [args] [--config local-runner.json]
951
- datasets prefetch|verify [tunedtensor.json]
965
+ datasets prefetch|verify [tunedtensor.json] [--repo org/name --training path ...]
966
+ assets list|path|verify [org/name[@revision]]
952
967
 
953
968
  Global options:
954
969
  -h, --help Show help
@@ -961,6 +976,9 @@ TT_LOCAL_HOME overrides the default store. --config is a legacy migration option
961
976
  var CONFIG_OPTION = { name: "--config", value: "path", description: "Legacy runner config (prefer runtime/evaluation in tunedtensor.json)" };
962
977
  var VERBOSE_OPTION = { name: "--verbose", description: "Stream subprocess output" };
963
978
  var QUIET_OPTION = { name: "--quiet", description: "Suppress progress output on stderr" };
979
+ var REPO_OPTION = { name: "--repo", value: "org/name", description: "Download this Hugging Face repo instead of the spec's (records a manifest; leaves the lock alone)" };
980
+ var REVISION_OPTION = { name: "--revision", value: "sha", description: "40-character commit to download with --repo (default: latest)" };
981
+ var SPLIT_OPTIONS = ["training", "validation", "test"].map((split) => ({ name: `--${split}`, value: "path", description: `Repo-relative ${split} file (.jsonl or .parquet) to download with --repo` }));
964
982
  var MODEL_SERVE_OPTIONS = [
965
983
  CONFIG_OPTION,
966
984
  { name: "--checkpoint", value: "path", description: "Foundation model directory (target: foundation)" },
@@ -1071,9 +1089,9 @@ var COMMAND_GROUPS = {
1071
1089
  missingPositionalsMessage: "models verify requires <model-id-or-artifact-path>"
1072
1090
  },
1073
1091
  prefetch: {
1074
- usage: "tt models prefetch [tunedtensor.json] [options]",
1075
- description: "Download the configured base model before a run.",
1076
- options: [CONFIG_OPTION, VERBOSE_OPTION, QUIET_OPTION],
1092
+ usage: "tt models prefetch [tunedtensor.json] [--repo org/name [--revision sha]] [options]",
1093
+ description: "Download the spec's base model and pin it in tunedtensor.lock.json, or download any model with --repo.",
1094
+ options: [CONFIG_OPTION, REPO_OPTION, REVISION_OPTION, VERBOSE_OPTION, QUIET_OPTION],
1077
1095
  maxPositionals: 1
1078
1096
  },
1079
1097
  "verify-base": {
@@ -1117,9 +1135,9 @@ var COMMAND_GROUPS = {
1117
1135
  defaultSubcommand: "prefetch",
1118
1136
  subcommands: {
1119
1137
  prefetch: {
1120
- usage: "tt datasets prefetch [tunedtensor.json] [options]",
1121
- description: "Download dataset_prebuilt.huggingface files into the local Hugging Face cache. Resumes after interruption.",
1122
- options: [CONFIG_OPTION, VERBOSE_OPTION, QUIET_OPTION],
1138
+ usage: "tt datasets prefetch [tunedtensor.json] [--repo org/name --training path [--test path]] [options]",
1139
+ description: "Download dataset_prebuilt.huggingface files and pin them in tunedtensor.lock.json, or download any dataset files with --repo. Resumes after interruption.",
1140
+ options: [CONFIG_OPTION, REPO_OPTION, REVISION_OPTION, ...SPLIT_OPTIONS, VERBOSE_OPTION, QUIET_OPTION],
1123
1141
  maxPositionals: 1
1124
1142
  },
1125
1143
  verify: {
@@ -1129,6 +1147,27 @@ var COMMAND_GROUPS = {
1129
1147
  maxPositionals: 1
1130
1148
  }
1131
1149
  }
1150
+ },
1151
+ assets: {
1152
+ description: "Find and verify downloaded Hugging Face datasets and models (manifests under ~/.tuned-tensor/assets).",
1153
+ defaultSubcommand: "list",
1154
+ subcommands: {
1155
+ list: { usage: "tt assets list", description: "List downloads, newest first.", options: [], maxPositionals: 0 },
1156
+ path: {
1157
+ usage: "tt assets path <org/name[@revision]>",
1158
+ description: "Print where a download lives on disk.",
1159
+ options: [],
1160
+ minPositionals: 1,
1161
+ maxPositionals: 1,
1162
+ missingPositionalsMessage: "assets path requires <org/name[@revision]>"
1163
+ },
1164
+ verify: {
1165
+ usage: "tt assets verify [org/name[@revision]]",
1166
+ description: "Re-hash downloaded files and compare them with their manifests (all downloads by default).",
1167
+ options: [],
1168
+ maxPositionals: 1
1169
+ }
1170
+ }
1132
1171
  }
1133
1172
  };
1134
1173
  function hasHelpFlag(argv) {
@@ -1254,6 +1293,26 @@ function parseCli(argv) {
1254
1293
  definition: subcommandDefinition
1255
1294
  };
1256
1295
  }
1296
+ function datasetSourceFromArgv(argv) {
1297
+ const repo = readOption(argv, "--repo");
1298
+ if (!repo) return void 0;
1299
+ const revision = readOption(argv, "--revision");
1300
+ const splits = Object.fromEntries(["training", "validation", "test"].flatMap((split) => {
1301
+ const path = readOption(argv, `--${split}`);
1302
+ return path ? [[split, path]] : [];
1303
+ }));
1304
+ if (!splits.training) {
1305
+ throw new Error("--repo needs --training <path> (a repo-relative .jsonl or .parquet file); --validation and --test are optional.");
1306
+ }
1307
+ const invalid = Object.values(splits).find((path) => !isRepoRelativePath(path));
1308
+ if (invalid) throw new Error(`Dataset files must be paths inside the repo, such as data/train.jsonl: ${invalid}`);
1309
+ return {
1310
+ format: "chat_jsonl",
1311
+ huggingface: huggingFaceDatasetSourceSchema.parse({ repo, ...revision ? { revision } : {} }),
1312
+ ...splits,
1313
+ training: splits.training
1314
+ };
1315
+ }
1257
1316
  function readNumberOption(argv, name) {
1258
1317
  const value = readOption(argv, name);
1259
1318
  return value ? Number(value) : void 0;
@@ -1866,7 +1925,8 @@ ${validation.errors.join("\n")}`);
1866
1925
  verbose: hasFlag(argv, "--verbose"),
1867
1926
  quiet: hasFlag(argv, "--quiet")
1868
1927
  });
1869
- const ensured = await ensureDatasetCached({ request: input.request, config, reporter });
1928
+ const lock = await readSpecLock(input.path);
1929
+ const ensured = await ensureDatasetCached({ request: applySpecLock(input.request, lock), config, reporter });
1870
1930
  const validated = await validateLocalFineTuneInput({
1871
1931
  request: ensured.request,
1872
1932
  config
@@ -1889,6 +1949,7 @@ ${validation.errors.join("\n")}`);
1889
1949
  }
1890
1950
  });
1891
1951
  }
1952
+ if (!config.dryRun) await enforceSpecLock({ specPath: input.path, request, config, reporter });
1892
1953
  const specFile = /^\..+\.tt-local-/.test(basename(input.path)) ? void 0 : { path: input.path, sha256: specHash(await readFile2(input.path, "utf8")) };
1893
1954
  if (input.spec.pipeline) {
1894
1955
  const result2 = await runLocalPipeline({ request, config, reporter, pipeline: pipelineForRunInput(input), projectSpec: input.spec, specFile });
@@ -1966,24 +2027,57 @@ ${validation.errors.join("\n")}`);
1966
2027
  if (command === "datasets") {
1967
2028
  const subcommand = cli.subcommand;
1968
2029
  const inputPath = resolve3(cli.positionals[0] ?? DEFAULT_LOCAL_SPEC_PATH);
1969
- const config = await configFromArgv(argv, inputPath);
1970
- const input = await loadCliBehaviorSpec(inputPath);
2030
+ const config = readOption(argv, "--repo") ? await resolveDownloadConfig(inputPath, readOption(argv, "--config"), (message) => console.error(message)) : await configFromArgv(argv, inputPath);
1971
2031
  if (subcommand !== "prefetch" && subcommand !== "verify") throw new Error(`Unknown datasets command: ${subcommand}`);
2032
+ const reporter = createConsoleReporter({
2033
+ verbose: hasFlag(argv, "--verbose"),
2034
+ quiet: hasFlag(argv, "--quiet")
2035
+ });
2036
+ const source = datasetSourceFromArgv(argv);
2037
+ if (source) {
2038
+ if (subcommand !== "prefetch") throw new Error("--repo applies to `tt datasets prefetch`; use `tt assets verify` for downloads.");
2039
+ return printJson(await prefetchHuggingFaceDataset({ request: { dataset_prebuilt: source }, config, reporter }));
2040
+ }
2041
+ const input = await loadCliBehaviorSpec(inputPath);
1972
2042
  const report = await prefetchHuggingFaceDataset({
1973
- request: input.request,
2043
+ request: applySpecLock(input.request, await readSpecLock(input.path)),
1974
2044
  config,
1975
2045
  localOnly: subcommand === "verify",
1976
- reporter: createConsoleReporter({
1977
- verbose: hasFlag(argv, "--verbose"),
1978
- quiet: hasFlag(argv, "--quiet")
1979
- })
2046
+ reporter
1980
2047
  });
1981
- return printJson({ ...report, input_path: input.path });
2048
+ if (subcommand === "prefetch") {
2049
+ await writeSpecLockEntry(input.path, "dataset", {
2050
+ repo: report.repo,
2051
+ revision: report.revision,
2052
+ files: (await snapshotDigests(report.snapshot_path, Object.values(report.files).flatMap((file) => file ? [file.path] : []))).files
2053
+ });
2054
+ }
2055
+ return printJson({ ...report, input_path: input.path, ...subcommand === "prefetch" ? { lock_path: specLockPath(input.path) } : {} });
2056
+ }
2057
+ if (command === "assets") {
2058
+ const subcommand = cli.subcommand;
2059
+ if (subcommand === "list") return printJson(await listAssets());
2060
+ if (subcommand === "path") {
2061
+ const asset = await findAsset(cli.positionals[0]);
2062
+ console.log(asset.snapshot_path);
2063
+ return;
2064
+ }
2065
+ if (subcommand === "verify") {
2066
+ const assets = cli.positionals[0] ? [await findAsset(cli.positionals[0])] : await listAssets();
2067
+ const results = [];
2068
+ for (const asset of assets) {
2069
+ results.push({ kind: asset.kind, repo: asset.repo, revision: asset.revision, ...await verifyAsset(asset) });
2070
+ }
2071
+ printJson({ ok: results.every((result) => result.ok), assets: results });
2072
+ if (!results.every((result) => result.ok)) process.exitCode = 2;
2073
+ return;
2074
+ }
2075
+ throw new Error(`Unknown assets command: ${subcommand}`);
1982
2076
  }
1983
2077
  if (command === "models") {
1984
2078
  const subcommand = cli.subcommand;
1985
2079
  const modelInputPath = subcommand === "prefetch" || subcommand === "verify-base" ? resolve3(cli.positionals[0] ?? DEFAULT_LOCAL_SPEC_PATH) : void 0;
1986
- const config = await configFromArgv(argv, modelInputPath);
2080
+ const config = subcommand === "prefetch" && readOption(argv, "--repo") ? await resolveDownloadConfig(modelInputPath, readOption(argv, "--config"), (message) => console.error(message)) : await configFromArgv(argv, modelInputPath);
1987
2081
  const store = createLocalStore(config.storeRoot);
1988
2082
  if (subcommand === "list") return printJson(await store.listModels());
1989
2083
  if (subcommand === "get") {
@@ -2040,19 +2134,35 @@ ${validation.errors.join("\n")}`);
2040
2134
  return printJson({ active: pointer.model_id ?? "base", pointer });
2041
2135
  }
2042
2136
  if (subcommand === "prefetch" || subcommand === "verify-base") {
2137
+ const reporter = createConsoleReporter({
2138
+ verbose: hasFlag(argv, "--verbose"),
2139
+ quiet: hasFlag(argv, "--quiet")
2140
+ });
2141
+ const repo = readOption(argv, "--repo");
2142
+ if (repo) {
2143
+ if (subcommand !== "prefetch") throw new Error("--repo applies to `tt models prefetch`; use `tt assets verify` for downloads.");
2144
+ const revision = readOption(argv, "--revision");
2145
+ return printJson(await prefetchBaseModel({ source: { repo, ...revision ? { revision } : {} }, config, reporter }));
2146
+ }
2043
2147
  const input = await loadCliBehaviorSpec(modelInputPath);
2044
2148
  const report = await prefetchBaseModel({
2045
2149
  request: input.request,
2046
2150
  config,
2047
2151
  localOnly: subcommand === "verify-base",
2048
- reporter: createConsoleReporter({
2049
- verbose: hasFlag(argv, "--verbose"),
2050
- quiet: hasFlag(argv, "--quiet")
2051
- })
2152
+ reporter
2052
2153
  });
2154
+ const locked = subcommand === "prefetch" && report.snapshot_path && report.snapshot_revision;
2155
+ if (locked) {
2156
+ await writeSpecLockEntry(input.path, "base_model", {
2157
+ repo: report.base_model,
2158
+ revision: report.snapshot_revision,
2159
+ files: (await snapshotDigests(report.snapshot_path)).files
2160
+ });
2161
+ }
2053
2162
  return printJson({
2054
2163
  ...report,
2055
- input_path: input.path
2164
+ input_path: input.path,
2165
+ ...locked ? { lock_path: specLockPath(input.path) } : {}
2056
2166
  });
2057
2167
  }
2058
2168
  if (subcommand === "serve") {
@@ -2089,6 +2199,7 @@ export {
2089
2199
  buildLocalBaseModelServerLaunch,
2090
2200
  buildLocalModelServerLaunch,
2091
2201
  buildModelPrefetchPayload,
2202
+ buildModelSourcePrefetchPayload,
2092
2203
  buildSystemMessage,
2093
2204
  canonicalLocalPipeline,
2094
2205
  compareRuns,
@@ -2168,6 +2279,7 @@ export {
2168
2279
  verifyLocalBaseModel,
2169
2280
  verifyLocalModelServerLaunch,
2170
2281
  verifyModelPrefetchCacheReport,
2282
+ withProgressHeartbeat,
2171
2283
  writePrivateJsonAtomic
2172
2284
  };
2173
2285
  //# sourceMappingURL=local-runtime.js.map