@tuned-tensor/local 0.1.7 → 0.1.8
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 +22 -1
- package/README.md +192 -210
- package/dist/contracts.d.ts +1 -5
- package/dist/contracts.js +1 -3
- package/dist/contracts.js.map +1 -1
- package/dist/evaluation.d.ts +1 -1
- package/dist/evaluation.js +54 -121
- package/dist/evaluation.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/process-runner.d.ts +36 -0
- package/dist/process-runner.js +132 -0
- package/dist/process-runner.js.map +1 -0
- package/dist/process-training.d.ts +1 -1
- package/dist/process-training.js +13 -53
- package/dist/process-training.js.map +1 -1
- package/dist/server.js +2 -2
- package/docs/architecture.md +1 -1
- package/docs/spark.md +1 -1
- package/examples/local-runner.json +6 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
All notable changes to
|
|
3
|
+
All notable changes to TT Local will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## 0.1.8 - 2026-07-02
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- Renamed the user-facing project language to TT Local with the tagline
|
|
10
|
+
"Tuning tensors locally", while keeping the `tt-local` CLI, npm package, repo,
|
|
11
|
+
and filesystem identifiers unchanged.
|
|
12
|
+
- Reworked the README around a first-time user flow: install, safe dry run,
|
|
13
|
+
real training, config basics, evaluation behavior, artifacts, Spark notes,
|
|
14
|
+
and common commands.
|
|
15
|
+
- Simplified evaluation configuration by removing `evaluation.mode`. Inference
|
|
16
|
+
is now selected with `evaluation.inference.provider`, and scoring is selected
|
|
17
|
+
with `evaluation.scoring.mode`.
|
|
18
|
+
- Shared the uv/subprocess execution helper between training and evaluation so
|
|
19
|
+
logging, timeout, and stderr handling are consistent.
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- Added direct support for `evaluation.inference.provider: "command"` with
|
|
24
|
+
`baselineCommand` and `candidateCommand` for custom inference adapters.
|
|
4
25
|
|
|
5
26
|
## 0.1.7 - 2026-07-02
|
|
6
27
|
|
package/README.md
CHANGED
|
@@ -1,62 +1,55 @@
|
|
|
1
|
-
#
|
|
1
|
+
# TT Local
|
|
2
|
+
|
|
3
|
+
Tuning tensors locally.
|
|
2
4
|
|
|
3
5
|
[](https://github.com/tunedtensor/tuned-tensor-local/actions/workflows/ci.yml)
|
|
4
6
|
[](https://www.npmjs.com/package/@tuned-tensor/local)
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
jobs on a machine you control. It stores specs, datasets, model
|
|
8
|
-
progress, reports, and dashboard state on local disk.
|
|
8
|
+
TT Local is a standalone CLI for running Tuned Tensor-style
|
|
9
|
+
fine-tuning jobs on a machine you control. It stores specs, datasets, model
|
|
10
|
+
artifacts, progress events, reports, and dashboard state on local disk.
|
|
9
11
|
|
|
10
|
-
It does not require the hosted Tuned Tensor CLI, cloud orchestration, Docker,
|
|
11
|
-
a managed database.
|
|
12
|
+
It does not require the hosted Tuned Tensor CLI, cloud orchestration, Docker,
|
|
13
|
+
or a managed database.
|
|
12
14
|
|
|
13
|
-
## What
|
|
15
|
+
## What Happens In A Run
|
|
14
16
|
|
|
15
|
-
`tt-local` runs this
|
|
17
|
+
`tt-local` runs this loop:
|
|
16
18
|
|
|
17
19
|
1. Read a behavior spec from `tunedtensor.json`.
|
|
18
|
-
2. Compile examples into a training dataset.
|
|
20
|
+
2. Compile examples into a local training dataset.
|
|
19
21
|
3. Evaluate the original Hugging Face base model.
|
|
20
22
|
4. Fine-tune locally with uv, Transformers, and PEFT.
|
|
21
23
|
5. Evaluate the fine-tuned Hugging Face/PEFT artifact.
|
|
22
|
-
6. Compare baseline vs tuned outputs and write a
|
|
23
|
-
|
|
24
|
-
OpenRouter can be used as an LLM judge for scoring generated outputs. It does
|
|
25
|
-
not generate the baseline or tuned responses.
|
|
26
|
-
|
|
27
|
-
## Install Locally
|
|
24
|
+
6. Compare baseline vs tuned outputs and write a report.
|
|
28
25
|
|
|
29
|
-
|
|
26
|
+
OpenRouter can optionally score generated outputs as an LLM judge. It does not
|
|
27
|
+
generate the baseline or tuned responses.
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
git clone https://github.com/tunedtensor/tuned-tensor-local.git
|
|
33
|
-
cd tuned-tensor-local
|
|
34
|
-
npm install
|
|
35
|
-
npm run build
|
|
36
|
-
npm link
|
|
37
|
-
```
|
|
29
|
+
## Install
|
|
38
30
|
|
|
39
|
-
|
|
40
|
-
globally:
|
|
31
|
+
Install the published CLI:
|
|
41
32
|
|
|
42
33
|
```bash
|
|
43
34
|
npm install -g @tuned-tensor/local
|
|
35
|
+
tt-local info
|
|
44
36
|
```
|
|
45
37
|
|
|
46
|
-
|
|
38
|
+
For local development or Spark:
|
|
47
39
|
|
|
48
40
|
```bash
|
|
41
|
+
git clone https://github.com/tunedtensor/tuned-tensor-local.git
|
|
42
|
+
cd tuned-tensor-local
|
|
43
|
+
npm install
|
|
44
|
+
npm run build
|
|
45
|
+
npm link
|
|
49
46
|
tt-local info
|
|
50
47
|
```
|
|
51
48
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
On the Spark host, confirm the GPU and uv are available:
|
|
49
|
+
Real training also needs `uv`:
|
|
55
50
|
|
|
56
51
|
```bash
|
|
57
|
-
nvidia-smi
|
|
58
52
|
uv --version
|
|
59
|
-
uv run python --version
|
|
60
53
|
```
|
|
61
54
|
|
|
62
55
|
If uv is missing:
|
|
@@ -65,29 +58,11 @@ If uv is missing:
|
|
|
65
58
|
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
66
59
|
```
|
|
67
60
|
|
|
68
|
-
|
|
61
|
+
## First Run: Safe Dry Run
|
|
69
62
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
Clone this repo on the Spark, install the Node dependencies, and build:
|
|
75
|
-
|
|
76
|
-
```bash
|
|
77
|
-
git clone https://github.com/tunedtensor/tuned-tensor-local.git
|
|
78
|
-
cd tuned-tensor-local
|
|
79
|
-
npm install
|
|
80
|
-
npm run build
|
|
81
|
-
npm link
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
Run the doctor:
|
|
85
|
-
|
|
86
|
-
```bash
|
|
87
|
-
tt-local doctor --config examples/local-runner.json
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
## Run Your First Job
|
|
63
|
+
The included example config is a dry run. It validates the end-to-end local
|
|
64
|
+
workflow and writes artifacts without downloading models or starting GPU
|
|
65
|
+
training.
|
|
91
66
|
|
|
92
67
|
Create a local behavior spec:
|
|
93
68
|
|
|
@@ -110,16 +85,13 @@ Validate the spec and config:
|
|
|
110
85
|
tt-local validate --config examples/local-runner.json
|
|
111
86
|
```
|
|
112
87
|
|
|
113
|
-
Run the
|
|
88
|
+
Run the dry workflow:
|
|
114
89
|
|
|
115
90
|
```bash
|
|
116
91
|
tt-local run --config examples/local-runner.json
|
|
117
92
|
```
|
|
118
93
|
|
|
119
|
-
|
|
120
|
-
writing without loading models or starting GPU training.
|
|
121
|
-
|
|
122
|
-
Inspect the run:
|
|
94
|
+
Inspect the result:
|
|
123
95
|
|
|
124
96
|
```bash
|
|
125
97
|
tt-local runs list --config examples/local-runner.json
|
|
@@ -132,11 +104,11 @@ Start the local dashboard:
|
|
|
132
104
|
tt-local serve --config examples/local-runner.json
|
|
133
105
|
```
|
|
134
106
|
|
|
135
|
-
|
|
107
|
+
Open the printed local URL in your browser.
|
|
136
108
|
|
|
137
|
-
## Run Real Training
|
|
109
|
+
## Run Real Training
|
|
138
110
|
|
|
139
|
-
Create a
|
|
111
|
+
Create a config such as `local-runner.json` or `spark-runner.json`:
|
|
140
112
|
|
|
141
113
|
```json
|
|
142
114
|
{
|
|
@@ -169,8 +141,109 @@ Create a Spark config, for example `spark-runner.json`:
|
|
|
169
141
|
}
|
|
170
142
|
```
|
|
171
143
|
|
|
172
|
-
|
|
173
|
-
|
|
144
|
+
If you use OpenRouter judging, set an API key:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
export OPENROUTER_API_KEY="your_openrouter_key"
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`tt-local` also loads a `.env` file from the working directory. Existing
|
|
151
|
+
environment variables win.
|
|
152
|
+
|
|
153
|
+
Run the job:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
tt-local doctor --config spark-runner.json
|
|
157
|
+
tt-local validate --config spark-runner.json
|
|
158
|
+
tt-local run --config spark-runner.json
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`tt-local run` prints live stage updates to stderr and writes the final JSON
|
|
162
|
+
summary to stdout. Use `--verbose` to stream Python subprocess output, or
|
|
163
|
+
`--quiet` for JSON-only automation.
|
|
164
|
+
|
|
165
|
+
Watch progress:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
tt-local runs watch <run-id> --config spark-runner.json
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
View the final report:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
tt-local runs report <run-id> --config spark-runner.json
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Compare two completed runs:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
tt-local runs compare <run-id-a> <run-id-b> --config spark-runner.json
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
The comparison aligns shared eval prompts, separates newly added eval examples,
|
|
184
|
+
and measures judge score noise for identical baseline outputs.
|
|
185
|
+
|
|
186
|
+
## Configuration Basics
|
|
187
|
+
|
|
188
|
+
The main config fields are:
|
|
189
|
+
|
|
190
|
+
- `artifactRoot`: datasets, logs, model outputs, eval JSON, and run reports.
|
|
191
|
+
- `storeRoot`: run/spec/model metadata used by CLI listing and the dashboard.
|
|
192
|
+
- `dryRun`: when `true`, skips model loading and training.
|
|
193
|
+
- `training`: uv/Python training entrypoint and environment.
|
|
194
|
+
- `evaluation.inference`: how baseline and candidate outputs are generated.
|
|
195
|
+
- `evaluation.scoring`: how generated outputs are scored.
|
|
196
|
+
- `paths.modelCache`: Hugging Face cache location.
|
|
197
|
+
|
|
198
|
+
Supported inference providers:
|
|
199
|
+
|
|
200
|
+
- `"transformers"`: run the included local evaluator with Hugging Face/PEFT.
|
|
201
|
+
- `"command"`: call `baselineCommand` or `candidateCommand` for custom inference.
|
|
202
|
+
- `"none"`: skip inference and record empty local responses.
|
|
203
|
+
|
|
204
|
+
Supported scoring modes:
|
|
205
|
+
|
|
206
|
+
- `"exact_match"`: normalized exact match.
|
|
207
|
+
- `"llm_judge"`: OpenRouter judge, with exact-match fallback by default.
|
|
208
|
+
- `"json_fields"`: field-level comparison for structured JSON outputs.
|
|
209
|
+
|
|
210
|
+
When `evaluation.scoring.mode` is `"llm_judge"`, `fallback` controls judge
|
|
211
|
+
failures. `"exact_match"` keeps the run going and records fallback reasoning.
|
|
212
|
+
`"fail"` fails the run.
|
|
213
|
+
|
|
214
|
+
## Evaluation Behavior
|
|
215
|
+
|
|
216
|
+
`evaluation.maxExamples` caps how many examples are scored. If it is unset,
|
|
217
|
+
the request hyperparameter `max_eval_examples` is used. An explicit
|
|
218
|
+
`maxExamples` in the config wins. When capped, the runner uses a deterministic
|
|
219
|
+
seeded sample rather than a prefix, so sorted or grouped eval files do not
|
|
220
|
+
bias results. The seed defaults to a hash of the run id and can be pinned with
|
|
221
|
+
`evaluation.sampleSeed`.
|
|
222
|
+
|
|
223
|
+
For spec-based runs without `dataset_prebuilt`, the runner holds out about 20%
|
|
224
|
+
of the spec examples for evaluation, with at least one training example and one
|
|
225
|
+
holdout when possible. With only one spec example, evaluation runs on that
|
|
226
|
+
example and reports `eval_split: "spec_examples"`.
|
|
227
|
+
|
|
228
|
+
For prebuilt datasets, `dataset_prebuilt.training` is always copied into the
|
|
229
|
+
run artifact as the training set. Evaluation uses `dataset_prebuilt.test` when
|
|
230
|
+
present, then `dataset_prebuilt.validation`. If neither is provided, a real
|
|
231
|
+
run fails because evaluation would otherwise measure the training split. Set
|
|
232
|
+
`evaluation.allowPrebuiltTrainingEval: true` to allow that explicitly.
|
|
233
|
+
|
|
234
|
+
Baseline evaluations are cached under `<storeRoot>/cache/baseline-evals/`.
|
|
235
|
+
Re-running an unchanged spec with the same model, eval examples, generation
|
|
236
|
+
settings, and scoring config reuses the previous baseline report. Set
|
|
237
|
+
`evaluation.baselineCache: false` to disable.
|
|
238
|
+
|
|
239
|
+
Training progress from Python metric lines and tqdm progress bars is recorded
|
|
240
|
+
as run events, so `tt-local runs watch` and per-run `progress.jsonl` show
|
|
241
|
+
epoch, loss, step, percentage, and ETA when the trainer emits them.
|
|
242
|
+
|
|
243
|
+
## Structured JSON Scoring
|
|
244
|
+
|
|
245
|
+
For structured tasks, use field-level scoring instead of whole-output exact
|
|
246
|
+
matching:
|
|
174
247
|
|
|
175
248
|
```json
|
|
176
249
|
{
|
|
@@ -183,143 +256,44 @@ exact matching:
|
|
|
183
256
|
}
|
|
184
257
|
```
|
|
185
258
|
|
|
186
|
-
The evaluation report still includes `exact_match_rate`, but `avg_score`,
|
|
187
|
-
`pass_rate`, and `json_field_metrics` are based on the selected JSON fields.
|
|
188
259
|
If `fields` is omitted, the evaluator scores every key in the expected JSON
|
|
189
|
-
object
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
noisy.
|
|
201
|
-
|
|
202
|
-
Each eval example records how it was scored (`results[].scored_by`), and the
|
|
203
|
-
report aggregates `judge_scored_count`, `fallback_scored_count`, and
|
|
204
|
-
`judge_only_avg_score`. When a judge request fails transiently and the
|
|
205
|
-
example falls back to exact match, `avg_score` mixes two metrics; use
|
|
206
|
-
`judge_only_avg_score` (and the comparison's `judge_only_avg_score_delta`)
|
|
207
|
-
when comparing judge quality. The comparison also classifies each regressed
|
|
208
|
-
example's judge reasoning as `factual`, `omission`, `style`, `fallback`, or
|
|
209
|
-
`other` and reports the counts in `regression_taxonomy`.
|
|
210
|
-
|
|
211
|
-
Baseline evaluations are cached under `<storeRoot>/cache/baseline-evals/`:
|
|
212
|
-
re-running a spec with an unchanged base model, eval examples, generation
|
|
213
|
-
settings, and scoring config reuses the previous baseline report instead of
|
|
214
|
-
re-running inference and judge calls. Cached reports carry `cached: true`.
|
|
215
|
-
Set `evaluation.baselineCache: false` to disable. Reports containing
|
|
216
|
-
fallback-scored examples are never cached, so a transient judge failure is
|
|
217
|
-
not replayed into future runs.
|
|
218
|
-
|
|
219
|
-
For thinking-mode base models (for example `Qwen/Qwen3.5-4B`, whose chat
|
|
220
|
-
template opens a `<think>` block by default and can consume the entire
|
|
221
|
-
`maxNewTokens` budget before any visible answer), forward template kwargs to
|
|
222
|
-
both evaluation and training:
|
|
260
|
+
object. A configured field missing from the expected JSON is always scored as
|
|
261
|
+
incorrect, so a misconfigured field list cannot inflate scores.
|
|
262
|
+
|
|
263
|
+
Reports still include `exact_match_rate`, but `avg_score`, `pass_rate`, and
|
|
264
|
+
`json_field_metrics` are based on the selected JSON fields.
|
|
265
|
+
|
|
266
|
+
## Thinking-Mode Models
|
|
267
|
+
|
|
268
|
+
Some models, such as `Qwen/Qwen3.5-4B`, may open a hidden thinking block by
|
|
269
|
+
default and consume the token budget before producing a visible answer. Forward
|
|
270
|
+
template kwargs to both evaluation and training:
|
|
223
271
|
|
|
224
272
|
```json
|
|
225
273
|
{
|
|
226
274
|
"evaluation": {
|
|
227
|
-
"inference": {
|
|
275
|
+
"inference": {
|
|
276
|
+
"chatTemplateKwargs": { "enable_thinking": false }
|
|
277
|
+
}
|
|
228
278
|
}
|
|
229
279
|
}
|
|
230
280
|
```
|
|
231
281
|
|
|
232
|
-
|
|
282
|
+
In `tunedtensor.json`, also set:
|
|
233
283
|
|
|
234
284
|
```json
|
|
235
|
-
{
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
```bash
|
|
242
|
-
tt-local runs compare <run-id-a> <run-id-b> --config spark-runner.json
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
It aligns the runs on their shared eval prompts, reports both runs' averages
|
|
246
|
-
on that shared subset plus the new-example subset separately, and measures
|
|
247
|
-
judge score noise on baseline outputs that are byte-identical across the two
|
|
248
|
-
runs.
|
|
249
|
-
|
|
250
|
-
With `"mode": "llm_judge"`, `scoring.fallback` controls what happens when the
|
|
251
|
-
judge is unavailable, a judge request fails, or the judge returns malformed
|
|
252
|
-
JSON. With `"fallback": "exact_match"` (the default), the affected example is
|
|
253
|
-
scored by normalized exact match and its reasoning records the judge failure;
|
|
254
|
-
the run does not fail. With `"fallback": "fail"`, judge errors fail the run.
|
|
255
|
-
|
|
256
|
-
`evaluation.maxExamples` caps how many evaluation examples are scored. If it
|
|
257
|
-
is unset, the request hyperparameter `max_eval_examples` is used instead;
|
|
258
|
-
an explicit `maxExamples` in the config always wins. When the cap truncates
|
|
259
|
-
the eval set, the runner draws a deterministic seeded random sample (not a
|
|
260
|
-
prefix), so sorted or grouped eval files do not bias the subset. The seed
|
|
261
|
-
defaults to a hash of the run id, can be pinned with `evaluation.sampleSeed`,
|
|
262
|
-
and is recorded in each eval report as `eval_sample_seed`. Baseline and
|
|
263
|
-
candidate evaluations always score the same sampled examples in the same
|
|
264
|
-
order.
|
|
265
|
-
|
|
266
|
-
If you want OpenRouter judging, set your key:
|
|
267
|
-
|
|
268
|
-
```bash
|
|
269
|
-
export OPENROUTER_API_KEY="your_openrouter_key"
|
|
285
|
+
{
|
|
286
|
+
"hyperparameters": {
|
|
287
|
+
"chat_template_kwargs": { "enable_thinking": false }
|
|
288
|
+
}
|
|
289
|
+
}
|
|
270
290
|
```
|
|
271
291
|
|
|
272
|
-
|
|
273
|
-
environment variables win), so the key can live in the project's `.env`
|
|
274
|
-
instead.
|
|
292
|
+
## Multimodal Examples
|
|
275
293
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
Run the job:
|
|
281
|
-
|
|
282
|
-
```bash
|
|
283
|
-
tt-local run --config spark-runner.json
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
`tt-local run` prints live stage updates to stderr and writes the final JSON
|
|
287
|
-
summary to stdout. Use `--verbose` to stream Python subprocess output, or
|
|
288
|
-
`--quiet` for script-only JSON output.
|
|
289
|
-
|
|
290
|
-
Training progress from Python metric lines and tqdm progress bars is also
|
|
291
|
-
recorded as run events, so `tt-local runs watch` and per-run `progress.jsonl`
|
|
292
|
-
show epoch, loss, step, percentage, and ETA updates when the trainer emits
|
|
293
|
-
them.
|
|
294
|
-
|
|
295
|
-
For spec-based runs (no `dataset_prebuilt`), the runner automatically holds
|
|
296
|
-
out about 20% of the spec examples (at least 1 holdout and at least 1 training
|
|
297
|
-
example) for evaluation, so baseline and candidate scores are not measured on
|
|
298
|
-
the training data. The split is deterministic: it uses the per-run sample seed
|
|
299
|
-
(a hash of the run id, or `evaluation.sampleSeed` when set), so re-running the
|
|
300
|
-
same run id reproduces the same split and baseline/candidate always score the
|
|
301
|
-
identical holdout. The training JSONL artifact contains only the training
|
|
302
|
-
split, `run_metadata.training_example_count` records the training split size,
|
|
303
|
-
and `eval_examples_total` records the holdout size. These runs report
|
|
304
|
-
`eval_split: "spec_holdout"`. With only 1 spec example there is nothing to
|
|
305
|
-
hold out, so evaluation runs on the training set and is reported as
|
|
306
|
-
`eval_split: "spec_examples"` (training-set evaluation due to insufficient
|
|
307
|
-
examples).
|
|
308
|
-
|
|
309
|
-
For prebuilt datasets, `dataset_prebuilt.training` is always copied into the
|
|
310
|
-
run artifact as the training set. Evaluation uses `dataset_prebuilt.test` when
|
|
311
|
-
present, then `dataset_prebuilt.validation`. If neither is provided, a real
|
|
312
|
-
(non-dry) run fails with an error, because evaluating on the training split
|
|
313
|
-
overstates improvement; set `evaluation.allowPrebuiltTrainingEval: true` to
|
|
314
|
-
evaluate on the training file anyway. Dry runs are allowed to fall back to the
|
|
315
|
-
training file without the override. Each eval report and the run's
|
|
316
|
-
`run_metadata` record the split that was evaluated in `eval_split`
|
|
317
|
-
(`spec_holdout`, `spec_examples`, `prebuilt_test`, `prebuilt_validation`, or
|
|
318
|
-
`prebuilt_training`), so training-set evaluation is always visible.
|
|
319
|
-
|
|
320
|
-
Multimodal runs use the same chat JSONL shape with structured user content.
|
|
321
|
-
For `image_text_to_text` models such as `Qwen/Qwen3-VL-2B-Instruct`, image
|
|
322
|
-
parts are loaded by the local SFT trainer and evaluator:
|
|
294
|
+
Multimodal runs use chat JSONL with structured user content. For
|
|
295
|
+
`image_text_to_text` models such as `Qwen/Qwen3-VL-2B-Instruct`, image parts
|
|
296
|
+
are loaded by the local trainer and evaluator:
|
|
323
297
|
|
|
324
298
|
```json
|
|
325
299
|
{
|
|
@@ -340,34 +314,17 @@ parts are loaded by the local SFT trainer and evaluator:
|
|
|
340
314
|
Image values in prebuilt JSONL may be absolute paths, paths relative to the
|
|
341
315
|
JSONL file, `file://` URIs, HTTP(S) URLs, or `data:` URIs. Behavior spec
|
|
342
316
|
examples can also use `input_assets` with `image`, `path`, `uri`, or
|
|
343
|
-
`data_uri
|
|
344
|
-
the copied run artifact can still resolve the image.
|
|
345
|
-
|
|
346
|
-
Watch progress:
|
|
347
|
-
|
|
348
|
-
```bash
|
|
349
|
-
tt-local runs watch <run-id> --config spark-runner.json
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
View the final report:
|
|
353
|
-
|
|
354
|
-
```bash
|
|
355
|
-
tt-local runs report <run-id> --config spark-runner.json
|
|
356
|
-
```
|
|
317
|
+
`data_uri`.
|
|
357
318
|
|
|
358
319
|
## Where Files Go
|
|
359
320
|
|
|
360
|
-
The runner writes two local roots:
|
|
361
|
-
|
|
362
|
-
- `artifactRoot`: datasets, logs, model outputs, eval JSON, and `run-report.json`.
|
|
363
|
-
- `storeRoot`: run/spec/model metadata used by CLI listing and the dashboard.
|
|
364
|
-
|
|
365
321
|
If `storeRoot` is omitted, `tt-local` uses `TT_LOCAL_HOME` or
|
|
366
322
|
`~/.tuned-tensor-local`.
|
|
367
323
|
|
|
368
324
|
Per-run artifacts include:
|
|
369
325
|
|
|
370
|
-
- `progress.jsonl`: stage changes and parsed training progress
|
|
326
|
+
- `progress.jsonl`: stage changes and parsed training progress.
|
|
327
|
+
- `baseline-eval.json` and `candidate-eval.json`: generated outputs and scores.
|
|
371
328
|
- `baseline-eval.json.inference.log` and `candidate-eval.json.inference.log`:
|
|
372
329
|
local inference subprocess output.
|
|
373
330
|
- `training/training.log`: uv fine-tuning output.
|
|
@@ -377,18 +334,43 @@ Per-run artifacts include:
|
|
|
377
334
|
|
|
378
335
|
```bash
|
|
379
336
|
tt-local init --name "Support Bot" --model Qwen/Qwen3.5-2B
|
|
337
|
+
tt-local doctor --config spark-runner.json
|
|
380
338
|
tt-local validate --config spark-runner.json
|
|
381
339
|
tt-local run --config spark-runner.json
|
|
382
340
|
tt-local run --config spark-runner.json --verbose
|
|
383
341
|
tt-local runs list --config spark-runner.json
|
|
384
342
|
tt-local runs get <run-id> --config spark-runner.json
|
|
385
343
|
tt-local runs events <run-id> --config spark-runner.json
|
|
344
|
+
tt-local runs watch <run-id> --config spark-runner.json
|
|
386
345
|
tt-local runs report <run-id> --config spark-runner.json
|
|
387
346
|
tt-local runs compare <run-id-a> <run-id-b> --config spark-runner.json
|
|
388
347
|
tt-local models list --config spark-runner.json
|
|
389
348
|
tt-local serve --config spark-runner.json
|
|
390
349
|
```
|
|
391
350
|
|
|
351
|
+
## DGX Spark Notes
|
|
352
|
+
|
|
353
|
+
On the Spark host, confirm the GPU and uv are available:
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
nvidia-smi
|
|
357
|
+
uv --version
|
|
358
|
+
uv run python --version
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
Then clone, install, build, and link the CLI on the Spark:
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
git clone https://github.com/tunedtensor/tuned-tensor-local.git
|
|
365
|
+
cd tuned-tensor-local
|
|
366
|
+
npm install
|
|
367
|
+
npm run build
|
|
368
|
+
npm link
|
|
369
|
+
tt-local doctor --config examples/local-runner.json
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
Spark-specific details live in [docs/spark.md](docs/spark.md).
|
|
373
|
+
|
|
392
374
|
## Development
|
|
393
375
|
|
|
394
376
|
```bash
|
|
@@ -404,12 +386,12 @@ secret.
|
|
|
404
386
|
|
|
405
387
|
## Notes
|
|
406
388
|
|
|
407
|
-
- Real training uses
|
|
389
|
+
- Real training uses `training/sft-local/src/train.py`.
|
|
408
390
|
- Real evaluation uses `training/sft-local/src/evaluate.py`.
|
|
409
|
-
- The default evaluation path uses Hugging Face/PEFT artifacts directly; it
|
|
410
|
-
not convert models to GGUF.
|
|
391
|
+
- The default evaluation path uses Hugging Face/PEFT artifacts directly; it
|
|
392
|
+
does not convert models to GGUF.
|
|
393
|
+
- `validate` and `run` warn about unknown hyperparameter keys and ignore them.
|
|
411
394
|
- Release notes are in [CHANGELOG.md](CHANGELOG.md).
|
|
412
|
-
- Spark-specific details live in [docs/spark.md](docs/spark.md).
|
|
413
395
|
|
|
414
396
|
## License
|
|
415
397
|
|
package/dist/contracts.d.ts
CHANGED
|
@@ -599,15 +599,11 @@ export declare const localRunnerConfigSchema: z.ZodObject<{
|
|
|
599
599
|
modelCache: z.ZodOptional<z.ZodString>;
|
|
600
600
|
}, z.core.$strip>>;
|
|
601
601
|
evaluation: z.ZodDefault<z.ZodObject<{
|
|
602
|
-
mode: z.ZodDefault<z.ZodEnum<{
|
|
603
|
-
command: "command";
|
|
604
|
-
llm_judge: "llm_judge";
|
|
605
|
-
heuristic: "heuristic";
|
|
606
|
-
}>>;
|
|
607
602
|
baselineCommand: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
608
603
|
candidateCommand: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
609
604
|
inference: z.ZodDefault<z.ZodObject<{
|
|
610
605
|
provider: z.ZodDefault<z.ZodEnum<{
|
|
606
|
+
command: "command";
|
|
611
607
|
none: "none";
|
|
612
608
|
transformers: "transformers";
|
|
613
609
|
}>>;
|
package/dist/contracts.js
CHANGED
|
@@ -227,11 +227,10 @@ export const localRunnerConfigSchema = z.object({
|
|
|
227
227
|
modelCache: z.string().optional(),
|
|
228
228
|
}).default({}),
|
|
229
229
|
evaluation: z.object({
|
|
230
|
-
mode: z.enum(["heuristic", "command", "llm_judge"]).default("heuristic"),
|
|
231
230
|
baselineCommand: commandSchema.optional(),
|
|
232
231
|
candidateCommand: commandSchema.optional(),
|
|
233
232
|
inference: z.object({
|
|
234
|
-
provider: z.enum(["transformers", "none"]).default("transformers"),
|
|
233
|
+
provider: z.enum(["transformers", "command", "none"]).default("transformers"),
|
|
235
234
|
project: z.string().optional(),
|
|
236
235
|
cwd: z.string().optional(),
|
|
237
236
|
module: z.string().optional(),
|
|
@@ -269,7 +268,6 @@ export const localRunnerConfigSchema = z.object({
|
|
|
269
268
|
allowPrebuiltTrainingEval: z.boolean().default(false),
|
|
270
269
|
baselineCache: z.boolean().default(true),
|
|
271
270
|
}).default({
|
|
272
|
-
mode: "heuristic",
|
|
273
271
|
inference: {
|
|
274
272
|
provider: "transformers",
|
|
275
273
|
script: "training/sft-local/src/evaluate.py",
|