@tuned-tensor/cli 0.13.0-beta.0 → 0.13.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.
- package/README.md +50 -9
- package/dist/{chunk-HDGBDHOB.js → chunk-YJYCGSHJ.js} +25 -11
- package/dist/chunk-YJYCGSHJ.js.map +1 -0
- package/dist/index.js +16 -10
- package/dist/index.js.map +1 -1
- package/dist/local-runtime.js +39 -10
- package/dist/local-runtime.js.map +1 -1
- package/docs/local-runtime/architecture.md +6 -5
- package/docs/local-runtime/spark.md +23 -17
- package/package.json +1 -1
- package/training/local-runner/src/evaluate.py +4 -1
- package/training/local-runner/src/model_contract.py +5 -3
- package/training/local-runner/src/serve.py +1 -1
- package/dist/chunk-HDGBDHOB.js.map +0 -1
package/README.md
CHANGED
|
@@ -8,19 +8,57 @@
|
|
|
8
8
|
|
|
9
9
|
## Cloud sunset
|
|
10
10
|
|
|
11
|
-
This release unregisters the hosted API commands (`tt auth`, `tt
|
|
12
|
-
`tt balance`, `tt topup`, `tt cloud`, and the rest of the managed
|
|
13
|
-
modules remain in the repository so they can be restored later.
|
|
14
|
-
is kept as a hidden alias; new scripts should call `tt run`,
|
|
15
|
-
`tt runs list`, and so on directly.
|
|
11
|
+
This release unregisters the hosted API commands (`tt auth`, `tt publish`,
|
|
12
|
+
`tt push`, `tt balance`, `tt topup`, `tt cloud`, and the rest of the managed
|
|
13
|
+
tree). The modules remain in the repository so they can be restored later.
|
|
14
|
+
`tt local …` is kept as a hidden alias; new scripts should call `tt run`,
|
|
15
|
+
`tt doctor`, `tt runs list`, and so on directly.
|
|
16
|
+
|
|
17
|
+
## Migrating from 0.11
|
|
18
|
+
|
|
19
|
+
`0.13` is the local-only CLI. If you are coming from `0.11.0`:
|
|
20
|
+
|
|
21
|
+
- Call `tt run`, `tt doctor`, and `tt serve` at the root. `tt local …` still
|
|
22
|
+
works as a hidden alias.
|
|
23
|
+
- Hosted account commands (`tt auth`, `tt publish`, `tt push`, and the rest)
|
|
24
|
+
are unregistered.
|
|
25
|
+
- `Qwen/Qwen3.5-2B` is pinned to Hugging Face snapshot
|
|
26
|
+
`15852e8c16360a2fea060d615a32b45270f8a8fc`. Prefetch, train, eval, and serve
|
|
27
|
+
reject any other revision.
|
|
28
|
+
- `tt serve active` fails unless an adapter is activated. Activation requires
|
|
29
|
+
a `generalRegression` suite in `local-runner.json`.
|
|
30
|
+
- `tt info` reports `status: local`.
|
|
16
31
|
|
|
17
32
|
## Install
|
|
18
33
|
|
|
34
|
+
On Linux or macOS:
|
|
35
|
+
|
|
19
36
|
```bash
|
|
20
|
-
|
|
37
|
+
curl -fsSL https://tunedtensor.com/install.sh | sh
|
|
21
38
|
tt --version
|
|
22
39
|
```
|
|
23
40
|
|
|
41
|
+
The installer bootstraps Node.js 22.19+ if needed, installs
|
|
42
|
+
`@tuned-tensor/cli` with npm `--ignore-scripts`, and falls back to
|
|
43
|
+
`~/.local` when the global npm prefix is not writable. Inspect it first
|
|
44
|
+
with `curl -fsSL https://tunedtensor.com/install.sh | less`. Pin a
|
|
45
|
+
dist-tag or version on `sh`, not `curl`:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
curl -fsSL https://tunedtensor.com/install.sh | TT_VERSION=beta sh
|
|
49
|
+
curl -fsSL https://tunedtensor.com/install.sh | TT_VERSION=0.13.0 sh
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Or install the package directly:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npm install -g --ignore-scripts @tuned-tensor/cli
|
|
56
|
+
tt --version
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Uninstall with `npm uninstall -g @tuned-tensor/cli` (add
|
|
60
|
+
`--prefix ~/.local` if the curl installer used that prefix).
|
|
61
|
+
|
|
24
62
|
Node.js 22.19.0 or newer is required. Local training additionally needs
|
|
25
63
|
[`uv`](https://docs.astral.sh/uv/) and a supported NVIDIA CUDA host; the locked
|
|
26
64
|
Python runner ships with the npm package and is prepared on first use.
|
|
@@ -67,7 +105,7 @@ If a newer stable release is available it recommends
|
|
|
67
105
|
checks are ignored.
|
|
68
106
|
|
|
69
107
|
```text
|
|
70
|
-
tt v0.13.0
|
|
108
|
+
tt v0.13.0
|
|
71
109
|
agent anthropic/claude-sonnet-4-5 · workflow model base
|
|
72
110
|
ctrl+c stop/clear · ctrl+d exit · /help commands · tab complete
|
|
73
111
|
|
|
@@ -173,7 +211,8 @@ tt models verify local-<run-id>
|
|
|
173
211
|
tt models serve local-<run-id> --config local-runner.json
|
|
174
212
|
```
|
|
175
213
|
|
|
176
|
-
Local currently certifies text SFT with `Qwen/Qwen3.5-2B
|
|
214
|
+
Local currently certifies text SFT with `Qwen/Qwen3.5-2B` (pinned to snapshot
|
|
215
|
+
`15852e8c16360a2fea060d615a32b45270f8a8fc`),
|
|
177
216
|
`nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-BF16`, and
|
|
178
217
|
`meta-models/Muse-Glimmer-30B`, using LoRA/PEFT and CUDA. The 30B/3B-active
|
|
179
218
|
Nemotron path is intended for DGX Spark-class unified memory, uses activation
|
|
@@ -184,7 +223,9 @@ vision tower frozen and unused. Evaluation and serving may use CPU. Training
|
|
|
184
223
|
artifacts, datasets, and model weights remain on the execution host.
|
|
185
224
|
|
|
186
225
|
Activation is optional and requires the run to pass a configured
|
|
187
|
-
`generalRegression` gate.
|
|
226
|
+
`generalRegression` gate. Without that suite, `tt models activate` fails
|
|
227
|
+
closed. `tt serve active` also fails closed if nothing is activated — it does
|
|
228
|
+
not silently serve the protected base model. Once activated, use
|
|
188
229
|
`tt serve active --config local-runner.json`.
|
|
189
230
|
|
|
190
231
|
`tt serve base` does not automatically inject the adjacent project spec.
|
|
@@ -558,12 +558,14 @@ async function assertArtifactManifest(manifestPath, options = {}) {
|
|
|
558
558
|
import { lstat as lstat2, readdir as readdir2 } from "fs/promises";
|
|
559
559
|
import { resolve as resolve2 } from "path";
|
|
560
560
|
import { fileURLToPath } from "url";
|
|
561
|
+
var QWEN_3_5_2B_REVISION = "15852e8c16360a2fea060d615a32b45270f8a8fc";
|
|
561
562
|
var NEMOTRON_BF16_REVISION = "ce38b6ab8b252b4b8ee7165b4605e93191cafd73";
|
|
562
563
|
var MUSE_GLIMMER_BF16_REVISION = "a4e59da52a7bc87ae7251dd5545c0dd437c44b68";
|
|
563
564
|
var TRAINING_MODELS = [
|
|
564
565
|
{
|
|
565
566
|
id: "Qwen/Qwen3.5-2B",
|
|
566
567
|
family: "qwen3_5",
|
|
568
|
+
defaultRevision: QWEN_3_5_2B_REVISION,
|
|
567
569
|
modelLoader: "causal_lm",
|
|
568
570
|
defaultLearningRate: 1e-5,
|
|
569
571
|
defaultPerDeviceBatchSize: 1,
|
|
@@ -1403,7 +1405,7 @@ function localRuntimePackageRoot(moduleUrl) {
|
|
|
1403
1405
|
// src/local-runtime/store.ts
|
|
1404
1406
|
import { appendFile, copyFile as copyFile2, mkdir as mkdir3, readdir as readdir3, readFile as readFile4, rename as rename2, rm, stat as stat3, writeFile as writeFile3 } from "fs/promises";
|
|
1405
1407
|
import { homedir as homedir2 } from "os";
|
|
1406
|
-
import { dirname as dirname4, join as join2, resolve as resolve5 } from "path";
|
|
1408
|
+
import { dirname as dirname4, join as join2, resolve as resolve5, sep as sep2 } from "path";
|
|
1407
1409
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
1408
1410
|
function isTerminalRunState(state) {
|
|
1409
1411
|
return state.status === "stage_completed" || state.status === "completed" || state.status === "failed" || state.status === "cancelled" && state.current_stage === "cancelled" && Boolean(state.completed_at);
|
|
@@ -1476,6 +1478,11 @@ function compareCreatedAt(left, right) {
|
|
|
1476
1478
|
function findByIdOrPrefix(records, id) {
|
|
1477
1479
|
return records.find((record) => record.id === id) ?? records.find((record) => record.id.startsWith(id));
|
|
1478
1480
|
}
|
|
1481
|
+
function isPathInside(root, candidate) {
|
|
1482
|
+
const resolvedRoot = resolve5(root);
|
|
1483
|
+
const resolvedCandidate = resolve5(candidate);
|
|
1484
|
+
return resolvedCandidate === resolvedRoot || resolvedCandidate.startsWith(`${resolvedRoot}${sep2}`);
|
|
1485
|
+
}
|
|
1479
1486
|
function createLocalStore(root = defaultLocalHome()) {
|
|
1480
1487
|
const resolvedRoot = resolve5(root);
|
|
1481
1488
|
const paths = localStorePaths(resolvedRoot);
|
|
@@ -1853,11 +1860,19 @@ function createLocalStore(root = defaultLocalHome()) {
|
|
|
1853
1860
|
},
|
|
1854
1861
|
async getRunReport(id) {
|
|
1855
1862
|
const state = await getRun(id);
|
|
1856
|
-
|
|
1857
|
-
|
|
1863
|
+
const candidates = [];
|
|
1864
|
+
if (state.report_path) {
|
|
1865
|
+
const reportPath = resolve5(state.report_path);
|
|
1866
|
+
const allowedRoots = [resolvedRoot];
|
|
1867
|
+
if (state.artifact_dir) allowedRoots.push(resolve5(state.artifact_dir));
|
|
1868
|
+
if (allowedRoots.some((allowed) => isPathInside(allowed, reportPath))) {
|
|
1869
|
+
candidates.push(reportPath);
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
candidates.push(runReportPath(state.id));
|
|
1873
|
+
for (const path of candidates) {
|
|
1874
|
+
if (await exists2(path)) return readJson2(path);
|
|
1858
1875
|
}
|
|
1859
|
-
const copiedReportPath = runReportPath(state.id);
|
|
1860
|
-
if (await exists2(copiedReportPath)) return readJson2(copiedReportPath);
|
|
1861
1876
|
throw new Error(`Run has no report yet: ${id}`);
|
|
1862
1877
|
},
|
|
1863
1878
|
async listModels() {
|
|
@@ -1875,7 +1890,7 @@ function createLocalStore(root = defaultLocalHome()) {
|
|
|
1875
1890
|
import { createHash as createHash4 } from "crypto";
|
|
1876
1891
|
import { createReadStream as createReadStream2 } from "fs";
|
|
1877
1892
|
import { lstat as lstat3, mkdir as mkdir5, readFile as readFile5, readdir as readdir4, realpath as realpath2, stat as stat4 } from "fs/promises";
|
|
1878
|
-
import { isAbsolute as isAbsolute3, join as join5, relative as relative2, resolve as resolve8, sep as
|
|
1893
|
+
import { isAbsolute as isAbsolute3, join as join5, relative as relative2, resolve as resolve8, sep as sep3 } from "path";
|
|
1879
1894
|
|
|
1880
1895
|
// src/local-runtime/process-runner.ts
|
|
1881
1896
|
import { createHash as createHash3 } from "crypto";
|
|
@@ -1904,6 +1919,7 @@ function buildBundledPythonCommand(entrypoint, args = []) {
|
|
|
1904
1919
|
const commandArgs = [
|
|
1905
1920
|
"run",
|
|
1906
1921
|
"--frozen",
|
|
1922
|
+
"--quiet",
|
|
1907
1923
|
"--project",
|
|
1908
1924
|
bundledProject,
|
|
1909
1925
|
"python",
|
|
@@ -2221,7 +2237,7 @@ async function certifiedFileDigest(path, expected) {
|
|
|
2221
2237
|
}
|
|
2222
2238
|
function isStrictPhysicalDescendant(parent, child) {
|
|
2223
2239
|
const candidate = relative2(parent, child);
|
|
2224
|
-
return candidate !== "" && candidate !== ".." && !candidate.startsWith(`..${
|
|
2240
|
+
return candidate !== "" && candidate !== ".." && !candidate.startsWith(`..${sep3}`) && !isAbsolute3(candidate);
|
|
2225
2241
|
}
|
|
2226
2242
|
async function verifySnapshotCacheLocation(path, modelId, revision, modelCache) {
|
|
2227
2243
|
const repository = `models--${modelId.replaceAll("/", "--")}`;
|
|
@@ -2292,9 +2308,7 @@ async function verifyCertifiedLocalSnapshot(path, modelId, snapshotFiles, modelC
|
|
|
2292
2308
|
const revision = defaultBaseModelRevision(modelId);
|
|
2293
2309
|
if (!revision) return;
|
|
2294
2310
|
const digests = certifiedFileDigests(modelId);
|
|
2295
|
-
if (!digests)
|
|
2296
|
-
throw new Error(`No certified file digests are registered for pinned model ${modelId}.`);
|
|
2297
|
-
}
|
|
2311
|
+
if (!digests) return;
|
|
2298
2312
|
if (!modelCache) {
|
|
2299
2313
|
throw new Error(`A certified local snapshot for ${modelId} requires paths.modelCache.`);
|
|
2300
2314
|
}
|
|
@@ -4797,4 +4811,4 @@ export {
|
|
|
4797
4811
|
runLocalPipeline,
|
|
4798
4812
|
runLocalFineTune
|
|
4799
4813
|
};
|
|
4800
|
-
//# sourceMappingURL=chunk-
|
|
4814
|
+
//# sourceMappingURL=chunk-YJYCGSHJ.js.map
|