@tuned-tensor/local 0.1.6 → 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 CHANGED
@@ -1,6 +1,37 @@
1
1
  # Changelog
2
2
 
3
- All notable changes to Tuned Tensor Local will be documented in this file.
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.
25
+
26
+ ## 0.1.7 - 2026-07-02
27
+
28
+ ### Added
29
+
30
+ - Added chat-template kwargs plumbing for thinking-mode models: `evaluation.inference.chatTemplateKwargs` (runner config) and the `chat_template_kwargs` hyperparameter (spec) are forwarded to `apply_chat_template` in the local evaluator and SFT trainer. Setting `{"enable_thinking": false}` stops models like `Qwen/Qwen3.5-4B` from opening a `<think>` block that consumes the whole `maxNewTokens` budget and truncates every output.
31
+
32
+ ### Fixed
33
+
34
+ - Fixed a run-fatal report validation error when a comparison had regressions in only some taxonomy categories: zod v4 enum-keyed records are exhaustive, so the partial `regression_taxonomy` failed `runReportSchema` at the end of an otherwise-successful run. The taxonomy now always contains all categories with zero defaults.
4
35
 
5
36
  ## 0.1.6 - 2026-07-02
6
37
 
package/README.md CHANGED
@@ -1,62 +1,55 @@
1
- # Tuned Tensor Local
1
+ # TT Local
2
+
3
+ Tuning tensors locally.
2
4
 
3
5
  [![CI](https://github.com/tunedtensor/tuned-tensor-local/actions/workflows/ci.yml/badge.svg)](https://github.com/tunedtensor/tuned-tensor-local/actions/workflows/ci.yml)
4
6
  [![npm](https://img.shields.io/npm/v/@tuned-tensor/local)](https://www.npmjs.com/package/@tuned-tensor/local)
5
7
 
6
- Tuned Tensor Local is a standalone CLI for running Tuned Tensor-style fine-tuning
7
- jobs on a machine you control. It stores specs, datasets, model artifacts,
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, or
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 It Does
15
+ ## What Happens In A Run
14
16
 
15
- `tt-local` runs this workflow locally:
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 local report.
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
- Recommended for development and Spark:
26
+ OpenRouter can optionally score generated outputs as an LLM judge. It does not
27
+ generate the baseline or tuned responses.
30
28
 
31
- ```bash
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
- You can install the published [@tuned-tensor/local npm package](https://www.npmjs.com/package/@tuned-tensor/local)
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
- Check the CLI:
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
- ## Set Up DGX Spark
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
- Open a new shell, then rerun:
61
+ ## First Run: Safe Dry Run
69
62
 
70
- ```bash
71
- uv --version
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 example dry workflow:
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
- The example config has `dryRun: true`, so it checks orchestration and artifact
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
- Then open the printed local URL in your browser.
107
+ Open the printed local URL in your browser.
136
108
 
137
- ## Run Real Training On Spark
109
+ ## Run Real Training
138
110
 
139
- Create a Spark config, for example `spark-runner.json`:
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
- For structured JSON tasks, use field-level scoring instead of whole-output
173
- exact matching:
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,124 +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 for each example. A configured field that is missing from the expected
190
- JSON is always scored as incorrect (with a reasoning note), so misconfigured
191
- field lists cannot inflate scores.
192
-
193
- The judge receives the spec's compiled system message (system prompt,
194
- guidelines, and constraints) as task instructions and scores how well each
195
- output fulfills the task; the expected output is treated as a reference
196
- answer, not a fact checklist. Every eval report also includes `avg_token_f1`
197
- (and the comparison includes `token_f1_delta`), a deterministic token-overlap
198
- similarity against the reference outputs that is useful for free-text tasks
199
- such as summarization, where exact match is always 0 and judge scores can be
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
- To compare two runs whose eval sets may differ (for example after raising
220
- `evaluation.maxExamples`), use:
260
+ object. A configured field missing from the expected JSON is always scored as
261
+ incorrect, so a misconfigured field list cannot inflate scores.
221
262
 
222
- ```bash
223
- tt-local runs compare <run-id-a> <run-id-b> --config spark-runner.json
224
- ```
263
+ Reports still include `exact_match_rate`, but `avg_score`, `pass_rate`, and
264
+ `json_field_metrics` are based on the selected JSON fields.
225
265
 
226
- It aligns the runs on their shared eval prompts, reports both runs' averages
227
- on that shared subset plus the new-example subset separately, and measures
228
- judge score noise on baseline outputs that are byte-identical across the two
229
- runs.
230
-
231
- With `"mode": "llm_judge"`, `scoring.fallback` controls what happens when the
232
- judge is unavailable, a judge request fails, or the judge returns malformed
233
- JSON. With `"fallback": "exact_match"` (the default), the affected example is
234
- scored by normalized exact match and its reasoning records the judge failure;
235
- the run does not fail. With `"fallback": "fail"`, judge errors fail the run.
236
-
237
- `evaluation.maxExamples` caps how many evaluation examples are scored. If it
238
- is unset, the request hyperparameter `max_eval_examples` is used instead;
239
- an explicit `maxExamples` in the config always wins. When the cap truncates
240
- the eval set, the runner draws a deterministic seeded random sample (not a
241
- prefix), so sorted or grouped eval files do not bias the subset. The seed
242
- defaults to a hash of the run id, can be pinned with `evaluation.sampleSeed`,
243
- and is recorded in each eval report as `eval_sample_seed`. Baseline and
244
- candidate evaluations always score the same sampled examples in the same
245
- order.
246
-
247
- If you want OpenRouter judging, set your key:
266
+ ## Thinking-Mode Models
248
267
 
249
- ```bash
250
- export OPENROUTER_API_KEY="your_openrouter_key"
251
- ```
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:
252
271
 
253
- `tt-local` also loads a `.env` file from the working directory (existing
254
- environment variables win), so the key can live in the project's `.env`
255
- instead.
256
-
257
- `validate` and `run` warn about hyperparameter keys the schema does not
258
- recognize (for example `per_device_train_batch_size` instead of
259
- `batch_size`); unknown keys are ignored rather than passed to training.
272
+ ```json
273
+ {
274
+ "evaluation": {
275
+ "inference": {
276
+ "chatTemplateKwargs": { "enable_thinking": false }
277
+ }
278
+ }
279
+ }
280
+ ```
260
281
 
261
- Run the job:
282
+ In `tunedtensor.json`, also set:
262
283
 
263
- ```bash
264
- tt-local run --config spark-runner.json
284
+ ```json
285
+ {
286
+ "hyperparameters": {
287
+ "chat_template_kwargs": { "enable_thinking": false }
288
+ }
289
+ }
265
290
  ```
266
291
 
267
- `tt-local run` prints live stage updates to stderr and writes the final JSON
268
- summary to stdout. Use `--verbose` to stream Python subprocess output, or
269
- `--quiet` for script-only JSON output.
270
-
271
- Training progress from Python metric lines and tqdm progress bars is also
272
- recorded as run events, so `tt-local runs watch` and per-run `progress.jsonl`
273
- show epoch, loss, step, percentage, and ETA updates when the trainer emits
274
- them.
275
-
276
- For spec-based runs (no `dataset_prebuilt`), the runner automatically holds
277
- out about 20% of the spec examples (at least 1 holdout and at least 1 training
278
- example) for evaluation, so baseline and candidate scores are not measured on
279
- the training data. The split is deterministic: it uses the per-run sample seed
280
- (a hash of the run id, or `evaluation.sampleSeed` when set), so re-running the
281
- same run id reproduces the same split and baseline/candidate always score the
282
- identical holdout. The training JSONL artifact contains only the training
283
- split, `run_metadata.training_example_count` records the training split size,
284
- and `eval_examples_total` records the holdout size. These runs report
285
- `eval_split: "spec_holdout"`. With only 1 spec example there is nothing to
286
- hold out, so evaluation runs on the training set and is reported as
287
- `eval_split: "spec_examples"` (training-set evaluation due to insufficient
288
- examples).
292
+ ## Multimodal Examples
289
293
 
290
- For prebuilt datasets, `dataset_prebuilt.training` is always copied into the
291
- run artifact as the training set. Evaluation uses `dataset_prebuilt.test` when
292
- present, then `dataset_prebuilt.validation`. If neither is provided, a real
293
- (non-dry) run fails with an error, because evaluating on the training split
294
- overstates improvement; set `evaluation.allowPrebuiltTrainingEval: true` to
295
- evaluate on the training file anyway. Dry runs are allowed to fall back to the
296
- training file without the override. Each eval report and the run's
297
- `run_metadata` record the split that was evaluated in `eval_split`
298
- (`spec_holdout`, `spec_examples`, `prebuilt_test`, `prebuilt_validation`, or
299
- `prebuilt_training`), so training-set evaluation is always visible.
300
-
301
- Multimodal runs use the same chat JSONL shape with structured user content.
302
- For `image_text_to_text` models such as `Qwen/Qwen3-VL-2B-Instruct`, image
303
- 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:
304
297
 
305
298
  ```json
306
299
  {
@@ -321,34 +314,17 @@ parts are loaded by the local SFT trainer and evaluator:
321
314
  Image values in prebuilt JSONL may be absolute paths, paths relative to the
322
315
  JSONL file, `file://` URIs, HTTP(S) URLs, or `data:` URIs. Behavior spec
323
316
  examples can also use `input_assets` with `image`, `path`, `uri`, or
324
- `data_uri`; use absolute paths, `file://` URIs, URLs, or `data:` URIs there so
325
- the copied run artifact can still resolve the image.
326
-
327
- Watch progress:
328
-
329
- ```bash
330
- tt-local runs watch <run-id> --config spark-runner.json
331
- ```
332
-
333
- View the final report:
334
-
335
- ```bash
336
- tt-local runs report <run-id> --config spark-runner.json
337
- ```
317
+ `data_uri`.
338
318
 
339
319
  ## Where Files Go
340
320
 
341
- The runner writes two local roots:
342
-
343
- - `artifactRoot`: datasets, logs, model outputs, eval JSON, and `run-report.json`.
344
- - `storeRoot`: run/spec/model metadata used by CLI listing and the dashboard.
345
-
346
321
  If `storeRoot` is omitted, `tt-local` uses `TT_LOCAL_HOME` or
347
322
  `~/.tuned-tensor-local`.
348
323
 
349
324
  Per-run artifacts include:
350
325
 
351
- - `progress.jsonl`: stage changes and parsed training progress for the run.
326
+ - `progress.jsonl`: stage changes and parsed training progress.
327
+ - `baseline-eval.json` and `candidate-eval.json`: generated outputs and scores.
352
328
  - `baseline-eval.json.inference.log` and `candidate-eval.json.inference.log`:
353
329
  local inference subprocess output.
354
330
  - `training/training.log`: uv fine-tuning output.
@@ -358,18 +334,43 @@ Per-run artifacts include:
358
334
 
359
335
  ```bash
360
336
  tt-local init --name "Support Bot" --model Qwen/Qwen3.5-2B
337
+ tt-local doctor --config spark-runner.json
361
338
  tt-local validate --config spark-runner.json
362
339
  tt-local run --config spark-runner.json
363
340
  tt-local run --config spark-runner.json --verbose
364
341
  tt-local runs list --config spark-runner.json
365
342
  tt-local runs get <run-id> --config spark-runner.json
366
343
  tt-local runs events <run-id> --config spark-runner.json
344
+ tt-local runs watch <run-id> --config spark-runner.json
367
345
  tt-local runs report <run-id> --config spark-runner.json
368
346
  tt-local runs compare <run-id-a> <run-id-b> --config spark-runner.json
369
347
  tt-local models list --config spark-runner.json
370
348
  tt-local serve --config spark-runner.json
371
349
  ```
372
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
+
373
374
  ## Development
374
375
 
375
376
  ```bash
@@ -385,12 +386,12 @@ secret.
385
386
 
386
387
  ## Notes
387
388
 
388
- - Real training uses the included SFT script in `training/sft-local/src/train.py`.
389
+ - Real training uses `training/sft-local/src/train.py`.
389
390
  - Real evaluation uses `training/sft-local/src/evaluate.py`.
390
- - The default evaluation path uses Hugging Face/PEFT artifacts directly; it does
391
- 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.
392
394
  - Release notes are in [CHANGELOG.md](CHANGELOG.md).
393
- - Spark-specific details live in [docs/spark.md](docs/spark.md).
394
395
 
395
396
  ## License
396
397
 
@@ -66,6 +66,7 @@ export declare const fineTuneHyperparametersSchema: z.ZodObject<{
66
66
  augment: z.ZodDefault<z.ZodBoolean>;
67
67
  use_llm_judge: z.ZodDefault<z.ZodBoolean>;
68
68
  max_eval_examples: z.ZodOptional<z.ZodNumber>;
69
+ chat_template_kwargs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
69
70
  }, z.core.$strip>;
70
71
  export declare const localArtifactsSchema: z.ZodObject<{
71
72
  prefix: z.ZodOptional<z.ZodString>;
@@ -110,6 +111,7 @@ export declare const fineTuneRunRequestSchema: z.ZodObject<{
110
111
  augment: z.ZodDefault<z.ZodBoolean>;
111
112
  use_llm_judge: z.ZodDefault<z.ZodBoolean>;
112
113
  max_eval_examples: z.ZodOptional<z.ZodNumber>;
114
+ chat_template_kwargs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
113
115
  }, z.core.$strip>>;
114
116
  artifacts: z.ZodOptional<z.ZodObject<{
115
117
  prefix: z.ZodOptional<z.ZodString>;
@@ -161,6 +163,7 @@ export declare const localBehaviorSpecFileSchema: z.ZodObject<{
161
163
  augment: z.ZodDefault<z.ZodBoolean>;
162
164
  use_llm_judge: z.ZodDefault<z.ZodBoolean>;
163
165
  max_eval_examples: z.ZodOptional<z.ZodNumber>;
166
+ chat_template_kwargs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
164
167
  }, z.core.$strip>>;
165
168
  artifacts: z.ZodOptional<z.ZodObject<{
166
169
  prefix: z.ZodOptional<z.ZodString>;
@@ -596,15 +599,11 @@ export declare const localRunnerConfigSchema: z.ZodObject<{
596
599
  modelCache: z.ZodOptional<z.ZodString>;
597
600
  }, z.core.$strip>>;
598
601
  evaluation: z.ZodDefault<z.ZodObject<{
599
- mode: z.ZodDefault<z.ZodEnum<{
600
- command: "command";
601
- llm_judge: "llm_judge";
602
- heuristic: "heuristic";
603
- }>>;
604
602
  baselineCommand: z.ZodOptional<z.ZodArray<z.ZodString>>;
605
603
  candidateCommand: z.ZodOptional<z.ZodArray<z.ZodString>>;
606
604
  inference: z.ZodDefault<z.ZodObject<{
607
605
  provider: z.ZodDefault<z.ZodEnum<{
606
+ command: "command";
608
607
  none: "none";
609
608
  transformers: "transformers";
610
609
  }>>;
@@ -624,6 +623,7 @@ export declare const localRunnerConfigSchema: z.ZodObject<{
624
623
  cuda: "cuda";
625
624
  mps: "mps";
626
625
  }>>;
626
+ chatTemplateKwargs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
627
627
  }, z.core.$strip>>;
628
628
  scoring: z.ZodDefault<z.ZodObject<{
629
629
  mode: z.ZodDefault<z.ZodEnum<{
package/dist/contracts.js CHANGED
@@ -43,6 +43,7 @@ export const fineTuneHyperparametersSchema = z.object({
43
43
  augment: z.boolean().default(false),
44
44
  use_llm_judge: z.boolean().default(false),
45
45
  max_eval_examples: z.number().int().min(1).optional(),
46
+ chat_template_kwargs: z.record(z.string(), z.unknown()).optional(),
46
47
  });
47
48
  export const localArtifactsSchema = z.object({
48
49
  prefix: z.string().min(1).optional(),
@@ -226,11 +227,10 @@ export const localRunnerConfigSchema = z.object({
226
227
  modelCache: z.string().optional(),
227
228
  }).default({}),
228
229
  evaluation: z.object({
229
- mode: z.enum(["heuristic", "command", "llm_judge"]).default("heuristic"),
230
230
  baselineCommand: commandSchema.optional(),
231
231
  candidateCommand: commandSchema.optional(),
232
232
  inference: z.object({
233
- provider: z.enum(["transformers", "none"]).default("transformers"),
233
+ provider: z.enum(["transformers", "command", "none"]).default("transformers"),
234
234
  project: z.string().optional(),
235
235
  cwd: z.string().optional(),
236
236
  module: z.string().optional(),
@@ -242,6 +242,7 @@ export const localRunnerConfigSchema = z.object({
242
242
  topP: z.number().min(0).max(1).default(1),
243
243
  trustRemoteCode: z.boolean().default(true),
244
244
  device: z.enum(["auto", "cpu", "cuda", "mps"]).default("auto"),
245
+ chatTemplateKwargs: z.record(z.string(), z.unknown()).optional(),
245
246
  }).default({
246
247
  provider: "transformers",
247
248
  script: "training/sft-local/src/evaluate.py",
@@ -267,7 +268,6 @@ export const localRunnerConfigSchema = z.object({
267
268
  allowPrebuiltTrainingEval: z.boolean().default(false),
268
269
  baselineCache: z.boolean().default(true),
269
270
  }).default({
270
- mode: "heuristic",
271
271
  inference: {
272
272
  provider: "transformers",
273
273
  script: "training/sft-local/src/evaluate.py",