@tuned-tensor/local 0.2.9 → 0.4.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/CHANGELOG.md +86 -0
- package/README.md +110 -201
- package/dist/active-model.d.ts +16 -0
- package/dist/active-model.js +88 -0
- package/dist/active-model.js.map +1 -0
- package/dist/artifacts.d.ts +5 -4
- package/dist/artifacts.js +57 -33
- package/dist/artifacts.js.map +1 -1
- package/dist/compare.d.ts +1 -8
- package/dist/compare.js +1 -23
- package/dist/compare.js.map +1 -1
- package/dist/contracts.d.ts +234 -365
- package/dist/contracts.js +95 -224
- package/dist/contracts.js.map +1 -1
- package/dist/dataset.d.ts +11 -4
- package/dist/dataset.js +67 -125
- package/dist/dataset.js.map +1 -1
- package/dist/doctor.d.ts +2 -3
- package/dist/doctor.js +23 -161
- package/dist/doctor.js.map +1 -1
- package/dist/evaluation.d.ts +11 -71
- package/dist/evaluation.js +212 -572
- package/dist/evaluation.js.map +1 -1
- package/dist/general-regression.d.ts +10 -0
- package/dist/general-regression.js +16 -0
- package/dist/general-regression.js.map +1 -0
- package/dist/huggingface-cache.d.ts +8 -7
- package/dist/huggingface-cache.js +16 -8
- package/dist/huggingface-cache.js.map +1 -1
- package/dist/index.d.ts +2 -10
- package/dist/index.js +358 -627
- package/dist/index.js.map +1 -1
- package/dist/local-project.d.ts +1 -11
- package/dist/local-project.js +33 -61
- package/dist/local-project.js.map +1 -1
- package/dist/model-registry.d.ts +3 -16
- package/dist/model-registry.js +76 -292
- package/dist/model-registry.js.map +1 -1
- package/dist/model-server.d.ts +7 -3
- package/dist/model-server.js +34 -25
- package/dist/model-server.js.map +1 -1
- package/dist/orchestrator.d.ts +11 -25
- package/dist/orchestrator.js +393 -563
- package/dist/orchestrator.js.map +1 -1
- package/dist/prefetch.d.ts +1 -5
- package/dist/prefetch.js +14 -19
- package/dist/prefetch.js.map +1 -1
- package/dist/process-runner.d.ts +10 -29
- package/dist/process-runner.js +63 -169
- package/dist/process-runner.js.map +1 -1
- package/dist/process-training.d.ts +0 -1
- package/dist/process-training.js +10 -87
- package/dist/process-training.js.map +1 -1
- package/dist/store.d.ts +1 -3
- package/dist/store.js +92 -290
- package/dist/store.js.map +1 -1
- package/docs/architecture.md +87 -152
- package/docs/spark.md +66 -97
- package/examples/dry-runner.json +14 -0
- package/examples/general-regression.jsonl +4 -0
- package/examples/local-runner.json +13 -6
- package/examples/smoke-spec.json +41 -0
- package/package.json +6 -6
- package/training/local-runner/pyproject.toml +0 -5
- package/training/local-runner/src/evaluate.py +89 -234
- package/training/local-runner/src/model_contract.py +47 -0
- package/training/local-runner/src/prefetch.py +18 -4
- package/training/local-runner/src/serve.py +60 -117
- package/training/local-runner/src/sft_data.py +97 -0
- package/training/local-runner/src/train.py +146 -346
- package/training/local-runner/uv.lock +0 -1197
- package/dist/labeling-sanitize.d.ts +0 -31
- package/dist/labeling-sanitize.js +0 -158
- package/dist/labeling-sanitize.js.map +0 -1
- package/dist/labeling.d.ts +0 -155
- package/dist/labeling.js +0 -496
- package/dist/labeling.js.map +0 -1
- package/dist/openrouter.d.ts +0 -29
- package/dist/openrouter.js +0 -66
- package/dist/openrouter.js.map +0 -1
- package/dist/server.d.ts +0 -9
- package/dist/server.js +0 -211
- package/dist/server.js.map +0 -1
- package/docs/local-workflow-remediation-2026-07-13.md +0 -130
- package/docs/local-workflow-ux-review-2026-07-13.md +0 -458
- package/examples/dpo-preferences.jsonl +0 -2
- package/examples/dpo-run-request.json +0 -34
- package/examples/smoke-run-request.json +0 -34
- package/training/local-runner/src/train_dpo.py +0 -286
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,92 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to TT Local will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## Unreleased
|
|
6
|
+
|
|
7
|
+
## 0.4.0 - 2026-07-26
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Added an independent chat JSONL general-regression suite that evaluates the
|
|
12
|
+
protected base and tuned adapter with configurable score and pass-rate
|
|
13
|
+
budgets, immutable artifacts, and explicit pass/fail evidence in run reports.
|
|
14
|
+
- Added lightweight active-model promotion and rollback through
|
|
15
|
+
`models activate`, `models active`, `models rollback`, `serve active`, and
|
|
16
|
+
protected-base serving. Activation requires a completed run, a passing
|
|
17
|
+
general-regression gate, and verified run and model artifacts.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- General-regression dataset or prompt changes now invalidate only the
|
|
22
|
+
affected evaluations, while policy-only changes recalculate the report
|
|
23
|
+
without retraining or repeating inference.
|
|
24
|
+
- The bundled OpenAI-compatible server can now launch the certified protected
|
|
25
|
+
base without attaching a PEFT adapter.
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- Repeated activation of the already-active model is idempotent and preserves
|
|
30
|
+
its rollback target.
|
|
31
|
+
- Regression-budget comparisons tolerate insignificant floating-point noise at
|
|
32
|
+
the configured boundary.
|
|
33
|
+
|
|
34
|
+
## 0.3.0 - 2026-07-26
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
|
|
38
|
+
- Added one certified end-to-end local workflow for CUDA LoRA SFT of
|
|
39
|
+
`Qwen/Qwen3.5-2B`, including immutable snapshot prefetch, held-out
|
|
40
|
+
base-versus-tuned evaluation, verified PEFT artifacts, and
|
|
41
|
+
OpenAI-compatible local serving.
|
|
42
|
+
- Added exact Qwen model-contract checks in both TypeScript and Python, plus
|
|
43
|
+
strict text-only chat JSONL validation and assistant-only SFT data tests.
|
|
44
|
+
|
|
45
|
+
### Removed
|
|
46
|
+
|
|
47
|
+
- **Breaking:** reduced TT Local to its end-to-end verified purpose: text LoRA
|
|
48
|
+
SFT of `Qwen/Qwen3.5-2B` on CUDA. Removed DPO, multimodal and continuation
|
|
49
|
+
inputs, arbitrary training/evaluation commands, external model contracts,
|
|
50
|
+
cloud teacher labeling and LLM judging, the classic-ML Study subsystem, and
|
|
51
|
+
the local dashboard.
|
|
52
|
+
- Removed the SQLite metadata mirror. Canonical JSON/JSONL files are now the
|
|
53
|
+
only state store and remain directly inspectable and recoverable.
|
|
54
|
+
- Removed implicit working-directory `.env` loading. Credentials and serving
|
|
55
|
+
keys must be provided explicitly through the process environment.
|
|
56
|
+
- Removed the incomplete detached-run, watch, and remote-cancel surfaces.
|
|
57
|
+
Workflows now run in the foreground so process and state lifecycles cannot
|
|
58
|
+
diverge after a host restart or worker crash.
|
|
59
|
+
|
|
60
|
+
### Changed
|
|
61
|
+
|
|
62
|
+
- Training now computes loss only on assistant answer tokens and truncates the
|
|
63
|
+
prompt before the answer. CUDA training and tokenizer chat-template failures
|
|
64
|
+
fail fast instead of silently changing behavior.
|
|
65
|
+
- Runner, spec, and hyperparameter schemas are strict; obsolete and misspelled
|
|
66
|
+
fields are rejected rather than ignored.
|
|
67
|
+
- Training, evaluation, and serving now always use the bundled locked Python
|
|
68
|
+
project. Removed public runner project, working-directory, provider, and
|
|
69
|
+
environment overrides.
|
|
70
|
+
- Bundled `uv` commands now use `--frozen` and a content-keyed virtual
|
|
71
|
+
environment in the writable user cache instead of modifying the npm package.
|
|
72
|
+
- After prefetch verifies an immutable snapshot, training, evaluation, and
|
|
73
|
+
serving enforce offline Hugging Face cache use and the current Transformers
|
|
74
|
+
`dtype` loading API.
|
|
75
|
+
- Explicit base-model revisions must be immutable 40-character Hugging Face
|
|
76
|
+
commit SHAs; moving branches and tags are rejected.
|
|
77
|
+
- Default holdout sampling is stable across runs of the same behavior spec,
|
|
78
|
+
and baseline cache identity no longer includes the per-run UUID.
|
|
79
|
+
- The supported workflow is now documented and tested as one coherent path:
|
|
80
|
+
initialize, preflight, prepare held-out data, train, compare, verify, and
|
|
81
|
+
serve a local PEFT adapter.
|
|
82
|
+
- Prebuilt datasets with both validation and test files now use validation for
|
|
83
|
+
normal iterative evaluation, preserving test as a separate holdout.
|
|
84
|
+
|
|
85
|
+
### Fixed
|
|
86
|
+
|
|
87
|
+
- Completed process-group teardown when a logged command's direct child exits,
|
|
88
|
+
including an opt-in cleanup mode for successful commands that must not leave
|
|
89
|
+
background descendants running.
|
|
90
|
+
|
|
5
91
|
## 0.2.9 - 2026-07-14
|
|
6
92
|
|
|
7
93
|
### Changed
|
package/README.md
CHANGED
|
@@ -3,272 +3,181 @@
|
|
|
3
3
|
[](https://github.com/tunedtensor/tuned-tensor-local/actions/workflows/ci.yml)
|
|
4
4
|
[](https://www.npmjs.com/package/@tuned-tensor/local)
|
|
5
5
|
|
|
6
|
-
TT Local
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
datasets, model artifacts, events, reports, and dashboard state stay on local
|
|
11
|
-
disk.
|
|
6
|
+
TT Local fine-tunes an open-weight language model on your own NVIDIA GPU. It
|
|
7
|
+
turns a small behavior spec or chat JSONL dataset into a verified LoRA adapter,
|
|
8
|
+
compares the base and tuned model on held-out examples, and can serve the
|
|
9
|
+
adapter through an OpenAI-compatible endpoint.
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
The current release deliberately has one supported path:
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
- text supervised fine-tuning (SFT);
|
|
14
|
+
- `Qwen/Qwen3.5-2B`;
|
|
15
|
+
- LoRA/PEFT on CUDA;
|
|
16
|
+
- deterministic exact-match or JSON-field evaluation;
|
|
17
|
+
- local files for datasets, state, reports, and model artifacts.
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
New model families and training methods should be added only after they have a
|
|
20
|
+
repeatable end-to-end GPU acceptance test.
|
|
18
21
|
|
|
19
|
-
|
|
20
|
-
npm install -g @tuned-tensor/local
|
|
21
|
-
tt-local info
|
|
22
|
-
```
|
|
22
|
+
## Requirements
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
```
|
|
24
|
+
- Node.js 22 or later
|
|
25
|
+
- [`uv`](https://docs.astral.sh/uv/)
|
|
26
|
+
- an NVIDIA CUDA host with enough free disk space for the model cache and run
|
|
27
|
+
artifacts
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
`uv`.
|
|
32
|
-
|
|
33
|
-
The default uv project is included in the npm package at
|
|
34
|
-
`training/local-runner`; using bundled training does not require cloning this
|
|
35
|
-
repository.
|
|
36
|
-
|
|
37
|
-
## First Local Run
|
|
38
|
-
|
|
39
|
-
On an NVIDIA Spark or another CUDA host, initialize both the behavior spec and
|
|
40
|
-
a durable project-local runner config:
|
|
29
|
+
The npm package includes the locked Python training environment.
|
|
41
30
|
|
|
42
31
|
```bash
|
|
43
|
-
|
|
44
|
-
tt-local
|
|
32
|
+
npm install -g @tuned-tensor/local
|
|
33
|
+
tt-local info
|
|
45
34
|
```
|
|
46
35
|
|
|
47
|
-
|
|
48
|
-
TT Local refuses to validate or train the unchanged placeholder so an
|
|
49
|
-
accidental run cannot spend GPU time on it.
|
|
36
|
+
## Fine-tune a model
|
|
50
37
|
|
|
51
|
-
|
|
38
|
+
Create a project on the GPU host:
|
|
52
39
|
|
|
53
40
|
```bash
|
|
54
|
-
|
|
55
|
-
tt-local
|
|
41
|
+
mkdir support-adapter && cd support-adapter
|
|
42
|
+
tt-local init --name "Support Adapter" --model Qwen/Qwen3.5-2B --profile spark
|
|
56
43
|
```
|
|
57
44
|
|
|
58
|
-
`
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
45
|
+
This creates `tunedtensor.json` and `local-runner.json`. Edit the generated
|
|
46
|
+
system prompt and replace both placeholder examples with representative,
|
|
47
|
+
different examples. Two examples are the minimum for a real run because
|
|
48
|
+
training and evaluation must not use the same row.
|
|
62
49
|
|
|
63
|
-
|
|
50
|
+
Preflight the machine, input, and cached base model:
|
|
64
51
|
|
|
65
52
|
```bash
|
|
66
|
-
tt-local
|
|
67
|
-
tt-local
|
|
68
|
-
tt-local
|
|
53
|
+
tt-local doctor tunedtensor.json
|
|
54
|
+
tt-local validate tunedtensor.json
|
|
55
|
+
tt-local models prefetch tunedtensor.json
|
|
56
|
+
tt-local models verify-base tunedtensor.json
|
|
69
57
|
```
|
|
70
58
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
the cached snapshot structure, indexed weight shards, tokenizer files, and
|
|
74
|
-
non-empty weights without downloading a replacement. Cached Hugging Face blobs
|
|
75
|
-
whose ETags encode Git or LFS checksums are re-hashed as part of verification.
|
|
59
|
+
Commands discover `local-runner.json` beside the spec. Use
|
|
60
|
+
`--config /path/to/local-runner.json` to select another config explicitly.
|
|
76
61
|
|
|
77
|
-
|
|
78
|
-
second terminal:
|
|
62
|
+
Run the complete base-evaluate, train, tuned-evaluate, and report workflow:
|
|
79
63
|
|
|
80
64
|
```bash
|
|
81
|
-
tt-local run tunedtensor.json
|
|
82
|
-
tt-local runs watch <run-id> --config local-runner.json
|
|
65
|
+
tt-local run tunedtensor.json
|
|
83
66
|
```
|
|
84
67
|
|
|
85
|
-
|
|
86
|
-
copy-paste watch/cancel commands before model work begins.
|
|
87
|
-
|
|
88
|
-
Inspect the evidence after the run:
|
|
68
|
+
Inspect the evidence and verify the adapter:
|
|
89
69
|
|
|
90
70
|
```bash
|
|
91
|
-
tt-local runs
|
|
92
|
-
tt-local
|
|
93
|
-
tt-local models verify local-<run-id> --config local-runner.json
|
|
71
|
+
tt-local runs report <run-id>
|
|
72
|
+
tt-local models verify local-<run-id>
|
|
94
73
|
```
|
|
95
74
|
|
|
96
|
-
|
|
97
|
-
|
|
75
|
+
To protect broad capability, configure a separate chat JSONL suite in
|
|
76
|
+
`local-runner.json`:
|
|
98
77
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
tt-local models serve local-<run-id> \
|
|
110
|
-
--config local-runner.json \
|
|
111
|
-
--spec tunedtensor.json \
|
|
112
|
-
--host 127.0.0.1 \
|
|
113
|
-
--port 8000
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"evaluation": {
|
|
81
|
+
"generalRegression": {
|
|
82
|
+
"dataset": "evals/general.jsonl",
|
|
83
|
+
"maxScoreDrop": 0.03,
|
|
84
|
+
"maxPassRateDrop": 0.05
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
114
88
|
```
|
|
115
89
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
90
|
+
TT Local evaluates the protected base and candidate adapter on this suite,
|
|
91
|
+
records both reports, and marks whether the configured score and pass-rate
|
|
92
|
+
budgets were respected. The base result uses the existing immutable baseline
|
|
93
|
+
cache.
|
|
119
94
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
curl http://127.0.0.1:8000/v1/models
|
|
123
|
-
curl http://127.0.0.1:8000/v1/chat/completions \
|
|
124
|
-
-H 'Content-Type: application/json' \
|
|
125
|
-
-d '{"messages":[{"role":"user","content":"Hello"}]}'
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
Use `--device cuda|mps|cpu|auto` to override the configured inference device.
|
|
129
|
-
Non-loopback binds require both `--allow-remote` and
|
|
130
|
-
`--api-key-env <environment-variable>`; requests must then send that value as
|
|
131
|
-
a Bearer token. The server accepts multimodal images only as bounded `data:`
|
|
132
|
-
URIs and does not support streaming yet.
|
|
133
|
-
|
|
134
|
-
## Cache and Evaluation Semantics
|
|
135
|
-
|
|
136
|
-
`paths.modelCache` means Hugging Face `HF_HOME` everywhere. Hub snapshots live
|
|
137
|
-
under `<modelCache>/hub`; prefetch, training, evaluation, doctor, and serving
|
|
138
|
-
all use that same layout. Set `HF_TOKEN` in the environment or project `.env`
|
|
139
|
-
before prefetching a gated model. Do not commit `.env`.
|
|
140
|
-
|
|
141
|
-
For reproducible remote loading, set
|
|
142
|
-
`hyperparameters.base_model_revision` to an immutable Hugging Face commit.
|
|
143
|
-
Alternatively, `paths.baseModel` may point to a complete local Hugging Face
|
|
144
|
-
snapshot directory. It cannot point to a model archive or standalone weights
|
|
145
|
-
file; TT Local requires non-empty model weights, `config.json`, tokenizer
|
|
146
|
-
metadata and vocabulary, and every indexed shard. Local snapshot contents are
|
|
147
|
-
fingerprinted so a byte change invalidates dependent stages.
|
|
148
|
-
|
|
149
|
-
Shared baseline-cache entries are used only when the model and every local
|
|
150
|
-
input have a stable content identity. An unpinned remote model or remotely
|
|
151
|
-
hosted image bypasses the cache rather than risking stale evaluation results.
|
|
152
|
-
|
|
153
|
-
The safe default scoring mode is deterministic `exact_match`. To use an
|
|
154
|
-
OpenRouter judge, configure `evaluation.scoring.mode: "llm_judge"`, an `llm`
|
|
155
|
-
block, and its API-key environment variable. Judge mode fails preflight when
|
|
156
|
-
those are missing. Exact-match fallback occurs only when explicitly requested
|
|
157
|
-
with `evaluation.scoring.fallback: "exact_match"`; fallback-scored results are
|
|
158
|
-
marked and never written to the judge baseline cache.
|
|
159
|
-
|
|
160
|
-
Before the first real run, you can download the configured Hugging Face base
|
|
161
|
-
model into `paths.modelCache` explicitly:
|
|
95
|
+
After a passing run, activation is a lightweight pointer to the existing model
|
|
96
|
+
record:
|
|
162
97
|
|
|
163
98
|
```bash
|
|
164
|
-
tt-local models
|
|
165
|
-
tt-local models
|
|
99
|
+
tt-local models activate local-<run-id>
|
|
100
|
+
tt-local models active
|
|
101
|
+
tt-local serve active --config local-runner.json
|
|
102
|
+
tt-local models rollback
|
|
103
|
+
tt-local serve base --config local-runner.json
|
|
166
104
|
```
|
|
167
105
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
Individual `run --stage ...` commands finish in the terminal
|
|
172
|
-
`stage_completed` state, so `runs watch` exits instead of waiting for stages
|
|
173
|
-
that were intentionally not requested.
|
|
106
|
+
Activation re-verifies the model artifact and requires a completed run with a
|
|
107
|
+
passing general-regression result. Rollback restores the previous adapter or
|
|
108
|
+
the protected base; it does not copy or delete model files.
|
|
174
109
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
file's directory. The published package includes `uv.lock` so bundled Python
|
|
178
|
-
dependencies are reproducible.
|
|
110
|
+
The report is evidence on the selected evaluation rows, not a guarantee of
|
|
111
|
+
general model improvement.
|
|
179
112
|
|
|
180
|
-
##
|
|
113
|
+
## Use a chat JSONL dataset
|
|
181
114
|
|
|
182
|
-
|
|
183
|
-
`training_method` to `dpo` and provide a prebuilt preference JSONL training
|
|
184
|
-
dataset:
|
|
115
|
+
For more than a few examples, set `dataset_prebuilt` in `tunedtensor.json`:
|
|
185
116
|
|
|
186
117
|
```json
|
|
187
118
|
{
|
|
188
|
-
"training_method": "dpo",
|
|
189
119
|
"dataset_prebuilt": {
|
|
190
|
-
"training": "
|
|
191
|
-
"
|
|
120
|
+
"training": "data/train.jsonl",
|
|
121
|
+
"validation": "data/validation.jsonl",
|
|
122
|
+
"format": "chat_jsonl"
|
|
192
123
|
}
|
|
193
124
|
}
|
|
194
125
|
```
|
|
195
126
|
|
|
196
|
-
Each
|
|
197
|
-
|
|
127
|
+
Each line is a JSON object with a `messages` array. The final message must be
|
|
128
|
+
the assistant answer; preceding messages form the prompt:
|
|
198
129
|
|
|
199
130
|
```json
|
|
200
|
-
{"
|
|
131
|
+
{"messages":[{"role":"system","content":"Return one sentiment label."},{"role":"user","content":"I loved it."},{"role":"assistant","content":"positive"}]}
|
|
201
132
|
```
|
|
202
133
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
`spec_snapshot.examples` with reference outputs for evaluation.
|
|
134
|
+
TT Local masks every prompt token from the loss and preserves the complete
|
|
135
|
+
assistant answer when truncating a long example. Use a distinct validation or
|
|
136
|
+
test file for trustworthy evaluation.
|
|
207
137
|
|
|
208
|
-
|
|
209
|
-
`spec_snapshot.base_model` to an `external:<id>` or `command:<id>` value, for
|
|
210
|
-
example `external:karpathy/nanochat`. The bundled `uv` trainer still requires a
|
|
211
|
-
supported Hugging Face base model, but command trainers receive adapter-focused
|
|
212
|
-
hyperparameters without injected LoRA/model-loader defaults. Custom
|
|
213
|
-
hyperparameter keys are passed through to `TT_HYPERPARAMETERS_PATH`.
|
|
214
|
-
Set `hyperparameters.base_model_revision` to an immutable Hugging Face commit
|
|
215
|
-
when a run must load and record an exact base-model revision.
|
|
216
|
-
|
|
217
|
-
## Continuing From a Fine-Tuned Model
|
|
218
|
-
|
|
219
|
-
Start another loop from an existing TT Local model by passing the stored local
|
|
220
|
-
model id:
|
|
138
|
+
## Serve the adapter
|
|
221
139
|
|
|
222
140
|
```bash
|
|
223
|
-
tt-local
|
|
141
|
+
tt-local serve local-<run-id> \
|
|
142
|
+
--spec tunedtensor.json \
|
|
143
|
+
--host 127.0.0.1 \
|
|
144
|
+
--port 8000
|
|
224
145
|
```
|
|
225
146
|
|
|
226
|
-
|
|
147
|
+
`tt-local models serve` is an equivalent nested command. Before launch, TT
|
|
148
|
+
Local re-hashes the stored artifact manifest and then loads the recorded base
|
|
149
|
+
model plus PEFT adapter with the bundled Python environment.
|
|
227
150
|
|
|
228
151
|
```bash
|
|
229
|
-
|
|
152
|
+
curl http://127.0.0.1:8000/health
|
|
153
|
+
curl http://127.0.0.1:8000/v1/models
|
|
154
|
+
curl http://127.0.0.1:8000/v1/chat/completions \
|
|
155
|
+
-H 'Content-Type: application/json' \
|
|
156
|
+
-d '{"messages":[{"role":"user","content":"Classify: I loved it."}]}'
|
|
230
157
|
```
|
|
231
158
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
baseline for the new run, and the bundled SFT/DPO trainers load it before
|
|
235
|
-
continuing LoRA training.
|
|
159
|
+
The default bind is localhost. A non-loopback bind requires `--allow-remote`
|
|
160
|
+
and `--api-key-env <environment-variable>`.
|
|
236
161
|
|
|
237
|
-
|
|
238
|
-
the child's base revision from the parent (or requires the same configured
|
|
239
|
-
local snapshot content), rejects conflicting base identity, and fingerprints
|
|
240
|
-
the parent artifact so changed bytes invalidate reuse. The explicit
|
|
241
|
-
`--parent-model-artifact` form is for caller-managed local artifacts and should
|
|
242
|
-
be used only when that provenance is understood.
|
|
162
|
+
## Reproducibility and local data
|
|
243
163
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
"training": {
|
|
249
|
-
"backend": "command",
|
|
250
|
-
"command": ["python", "train_adapter.py"],
|
|
251
|
-
"artifact": {
|
|
252
|
-
"framework": "nanochat",
|
|
253
|
-
"format": "custom-directory",
|
|
254
|
-
"entrypoint": "batch_command",
|
|
255
|
-
"servable": false
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
```
|
|
164
|
+
Set `paths.modelCache` in `local-runner.json` to a durable Hugging Face cache.
|
|
165
|
+
Prefetch records the resolved model commit; set
|
|
166
|
+
`hyperparameters.base_model_revision` to a 40-character Hugging Face commit
|
|
167
|
+
SHA when an immutable revision is required before download.
|
|
260
168
|
|
|
261
|
-
|
|
169
|
+
Run state is intentionally plain and recoverable:
|
|
262
170
|
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
171
|
+
```text
|
|
172
|
+
.tt-local/
|
|
173
|
+
artifacts/users/<user-id>/specs/<spec-id>/runs/<run-id>/...
|
|
174
|
+
store/specs/<spec-id>/spec.json
|
|
175
|
+
store/runs/<run-id>/{request.json,state.json,progress.jsonl,run-report.json}
|
|
176
|
+
store/models/<model-id>/model.json
|
|
268
177
|
```
|
|
269
178
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
## License
|
|
179
|
+
Real training writes an `artifact-manifest.json` containing file sizes and
|
|
180
|
+
SHA-256 hashes. Dry runs exercise orchestration but do not register a model.
|
|
273
181
|
|
|
274
|
-
|
|
182
|
+
See [docs/spark.md](docs/spark.md) for the DGX Spark workflow and
|
|
183
|
+
[docs/architecture.md](docs/architecture.md) for the design boundary.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { LocalModelRecord, LocalStore } from "./store.js";
|
|
2
|
+
export interface ActiveModelPointer {
|
|
3
|
+
schema_version: 1;
|
|
4
|
+
model_id: string | null;
|
|
5
|
+
run_id: string | null;
|
|
6
|
+
previous_model_id: string | null;
|
|
7
|
+
activated_at: string;
|
|
8
|
+
action: "activate" | "rollback";
|
|
9
|
+
}
|
|
10
|
+
export interface ActiveModelState {
|
|
11
|
+
pointer: ActiveModelPointer | null;
|
|
12
|
+
model: LocalModelRecord | null;
|
|
13
|
+
}
|
|
14
|
+
export declare function getActiveModel(store: LocalStore): Promise<ActiveModelState>;
|
|
15
|
+
export declare function activateModel(store: LocalStore, modelId: string): Promise<ActiveModelPointer>;
|
|
16
|
+
export declare function rollbackActiveModel(store: LocalStore): Promise<ActiveModelPointer>;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { appendFile, readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { writeJsonAtomic } from "./artifacts.js";
|
|
4
|
+
import { runReportSchema } from "./contracts.js";
|
|
5
|
+
function pointerPath(store) {
|
|
6
|
+
return join(store.root, "active-model.json");
|
|
7
|
+
}
|
|
8
|
+
function historyPath(store) {
|
|
9
|
+
return join(store.root, "activation-history.jsonl");
|
|
10
|
+
}
|
|
11
|
+
async function readPointer(store) {
|
|
12
|
+
try {
|
|
13
|
+
const value = JSON.parse(await readFile(pointerPath(store), "utf8"));
|
|
14
|
+
if (value.schema_version !== 1
|
|
15
|
+
|| !["activate", "rollback"].includes(value.action)
|
|
16
|
+
|| (value.model_id !== null && typeof value.model_id !== "string")
|
|
17
|
+
|| (value.run_id !== null && typeof value.run_id !== "string")
|
|
18
|
+
|| (value.previous_model_id !== null && typeof value.previous_model_id !== "string")) {
|
|
19
|
+
throw new Error("Active model pointer has an invalid shape.");
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
if (error.code === "ENOENT")
|
|
25
|
+
return null;
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async function assertModelCanActivate(store, modelId) {
|
|
30
|
+
const model = await store.getModel(modelId);
|
|
31
|
+
const run = await store.getRun(model.run_id);
|
|
32
|
+
if (run.status !== "completed") {
|
|
33
|
+
throw new Error(`Model ${model.id} belongs to run ${run.id}, which is not completed.`);
|
|
34
|
+
}
|
|
35
|
+
const report = runReportSchema.parse(JSON.parse(await readFile(join(model.artifact_dir, "run-report.json"), "utf8")));
|
|
36
|
+
if (!report.general_regression) {
|
|
37
|
+
throw new Error(`Model ${model.id} has no general regression evaluation.`);
|
|
38
|
+
}
|
|
39
|
+
if (!report.general_regression.passed) {
|
|
40
|
+
throw new Error(`Model ${model.id} failed general regression: `
|
|
41
|
+
+ (report.general_regression.failures.join(" ") || "regression budget exceeded"));
|
|
42
|
+
}
|
|
43
|
+
return model;
|
|
44
|
+
}
|
|
45
|
+
async function publishPointer(store, pointer) {
|
|
46
|
+
await store.ensure();
|
|
47
|
+
await writeJsonAtomic(pointerPath(store), pointer);
|
|
48
|
+
await appendFile(historyPath(store), `${JSON.stringify(pointer)}\n`, "utf8");
|
|
49
|
+
return pointer;
|
|
50
|
+
}
|
|
51
|
+
export async function getActiveModel(store) {
|
|
52
|
+
const pointer = await readPointer(store);
|
|
53
|
+
if (!pointer?.model_id)
|
|
54
|
+
return { pointer, model: null };
|
|
55
|
+
return { pointer, model: await store.getModel(pointer.model_id) };
|
|
56
|
+
}
|
|
57
|
+
export async function activateModel(store, modelId) {
|
|
58
|
+
const [model, current] = await Promise.all([
|
|
59
|
+
assertModelCanActivate(store, modelId),
|
|
60
|
+
readPointer(store),
|
|
61
|
+
]);
|
|
62
|
+
if (current?.model_id === model.id)
|
|
63
|
+
return current;
|
|
64
|
+
return publishPointer(store, {
|
|
65
|
+
schema_version: 1,
|
|
66
|
+
model_id: model.id,
|
|
67
|
+
run_id: model.run_id,
|
|
68
|
+
previous_model_id: current?.model_id ?? null,
|
|
69
|
+
activated_at: new Date().toISOString(),
|
|
70
|
+
action: "activate",
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
export async function rollbackActiveModel(store) {
|
|
74
|
+
const current = await readPointer(store);
|
|
75
|
+
if (!current)
|
|
76
|
+
throw new Error("No active-model history exists to roll back.");
|
|
77
|
+
const targetId = current.previous_model_id;
|
|
78
|
+
const target = targetId ? await assertModelCanActivate(store, targetId) : null;
|
|
79
|
+
return publishPointer(store, {
|
|
80
|
+
schema_version: 1,
|
|
81
|
+
model_id: target?.id ?? null,
|
|
82
|
+
run_id: target?.run_id ?? null,
|
|
83
|
+
previous_model_id: current.model_id,
|
|
84
|
+
activated_at: new Date().toISOString(),
|
|
85
|
+
action: "rollback",
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=active-model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"active-model.js","sourceRoot":"","sources":["../src/active-model.ts"],"names":[],"mappings":"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"}
|
package/dist/artifacts.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export interface RunArtifacts {
|
|
|
7
7
|
trainingReportJson: string;
|
|
8
8
|
baselineEvalJson: string;
|
|
9
9
|
candidateEvalJson: string;
|
|
10
|
+
generalBaselineEvalJson: string;
|
|
11
|
+
generalCandidateEvalJson: string;
|
|
10
12
|
runReportJson: string;
|
|
11
13
|
artifactManifestJson: string;
|
|
12
14
|
progressJsonl: string;
|
|
@@ -77,16 +79,15 @@ export interface ArtifactManifest {
|
|
|
77
79
|
}
|
|
78
80
|
export interface ArtifactManifestModel {
|
|
79
81
|
artifact_kind: "file" | "directory";
|
|
80
|
-
format:
|
|
81
|
-
framework:
|
|
82
|
+
format: "tar.gz" | "huggingface-directory";
|
|
83
|
+
framework: "transformers-peft";
|
|
82
84
|
base_model: string;
|
|
83
85
|
base_model_revision?: string;
|
|
84
86
|
base_model_artifact_uri?: string;
|
|
85
87
|
base_model_fingerprint?: string;
|
|
86
|
-
parent_model_artifact?: string;
|
|
87
88
|
artifact_uri: string;
|
|
88
89
|
artifact_root: string;
|
|
89
|
-
servable:
|
|
90
|
+
servable: true;
|
|
90
91
|
files: ArtifactManifestFile[];
|
|
91
92
|
}
|
|
92
93
|
export interface ArtifactManifestVerification {
|