@tuned-tensor/local 0.1.9 → 0.2.1

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
@@ -2,6 +2,27 @@
2
2
 
3
3
  All notable changes to TT Local will be documented in this file.
4
4
 
5
+ ## 0.2.1 - 2026-07-04
6
+
7
+ ### Added
8
+
9
+ - Added command-based local training with `training.backend: "command"` and
10
+ `training.command`, while preserving the existing uv script/module path.
11
+ - Added `evaluation.inference.provider: "batch_command"` with
12
+ `evaluation.inference.command` for custom batch evaluators that consume the
13
+ same `--input`/`--output` JSON files as the bundled Transformers evaluator.
14
+
15
+ ## 0.2.0 - 2026-07-04
16
+
17
+ ### Added
18
+
19
+ - Added a hybrid local state store: `metadata.sqlite` now indexes specs, runs,
20
+ run events, and models for CLI/dashboard queries while preserving the
21
+ transparent JSON/JSONL artifact layout. `tt-local store rebuild-index`
22
+ rebuilds SQLite metadata from canonical per-object files. The SQLite index
23
+ uses `better-sqlite3` instead of Node's experimental built-in SQLite API,
24
+ avoiding runtime experimental warnings on Node 22.
25
+
5
26
  ## 0.1.9 - 2026-07-03
6
27
 
7
28
  ### Added
package/README.md CHANGED
@@ -1,419 +1,33 @@
1
1
  # TT Local
2
2
 
3
- Tuning tensors locally.
4
-
5
3
  [![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)
6
4
  [![npm](https://img.shields.io/npm/v/@tuned-tensor/local)](https://www.npmjs.com/package/@tuned-tensor/local)
7
5
 
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.
11
-
12
- It does not require the hosted Tuned Tensor CLI, cloud orchestration, Docker,
13
- or a managed database.
14
-
15
- ## What Happens In A Run
6
+ TT Local runs Tuned Tensor-style fine-tuning jobs on your own machine. It keeps
7
+ specs, datasets, model artifacts, events, reports, and dashboard state on local
8
+ disk.
16
9
 
17
- `tt-local` runs this loop:
10
+ Usage docs:
18
11
 
19
- 1. Read a behavior spec from `tunedtensor.json`.
20
- 2. Compile examples into a local training dataset.
21
- 3. Evaluate the original Hugging Face base model.
22
- 4. Fine-tune locally with uv, Transformers, and PEFT.
23
- 5. Evaluate the fine-tuned Hugging Face/PEFT artifact.
24
- 6. Compare baseline vs tuned outputs and write a report.
25
-
26
- OpenRouter can optionally score generated outputs as an LLM judge. It does not
27
- generate the baseline or tuned responses.
12
+ https://tunedtensor.com/docs/local-training
28
13
 
29
14
  ## Install
30
15
 
31
- Install the published CLI:
32
-
33
16
  ```bash
34
17
  npm install -g @tuned-tensor/local
35
18
  tt-local info
36
19
  ```
37
20
 
38
- For local development or Spark:
39
-
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
46
- tt-local info
47
- ```
48
-
49
- Real training also needs `uv`:
21
+ The bundled SFT and Transformers evaluator path also needs `uv`:
50
22
 
51
23
  ```bash
52
24
  uv --version
53
25
  ```
54
26
 
55
- If uv is missing:
56
-
57
- ```bash
58
- curl -LsSf https://astral.sh/uv/install.sh | sh
59
- ```
60
-
61
- ## First Run: Safe Dry Run
62
-
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.
66
-
67
- Create a local behavior spec:
68
-
69
- ```bash
70
- tt-local init --name "Support Bot" --model Qwen/Qwen3.5-2B
71
- ```
72
-
73
- Edit `tunedtensor.json` and replace the placeholder example with your task:
27
+ Custom training or evaluation workflows can use command entrypoints instead of
28
+ `uv`.
74
29
 
75
- ```json
76
- {
77
- "input": "Classify sentiment: I love this product.",
78
- "output": "positive"
79
- }
80
- ```
81
-
82
- Validate the spec and config:
83
-
84
- ```bash
85
- tt-local validate --config examples/local-runner.json
86
- ```
87
-
88
- Run the dry workflow:
89
-
90
- ```bash
91
- tt-local run --config examples/local-runner.json
92
- ```
93
-
94
- Inspect the result:
95
-
96
- ```bash
97
- tt-local runs list --config examples/local-runner.json
98
- tt-local runs report <run-id> --config examples/local-runner.json
99
- ```
100
-
101
- Start the local dashboard:
102
-
103
- ```bash
104
- tt-local serve --config examples/local-runner.json
105
- ```
106
-
107
- Open the printed local URL in your browser.
108
-
109
- ## Run Real Training
110
-
111
- Create a config such as `local-runner.json` or `spark-runner.json`:
112
-
113
- ```json
114
- {
115
- "artifactRoot": "/home/eve/tt-local-artifacts",
116
- "storeRoot": "/home/eve/tt-local-store",
117
- "dryRun": false,
118
- "training": {
119
- "backend": "uv",
120
- "project": "training/sft-local",
121
- "script": "training/sft-local/src/train.py"
122
- },
123
- "evaluation": {
124
- "inference": {
125
- "provider": "transformers",
126
- "project": "training/sft-local",
127
- "script": "training/sft-local/src/evaluate.py",
128
- "maxNewTokens": 256,
129
- "temperature": 0,
130
- "topP": 1
131
- },
132
- "scoring": {
133
- "mode": "llm_judge",
134
- "fallback": "exact_match"
135
- },
136
- "maxExamples": 10
137
- },
138
- "paths": {
139
- "modelCache": "/home/eve/.cache/huggingface"
140
- }
141
- }
142
- ```
143
-
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:
247
-
248
- ```json
249
- {
250
- "evaluation": {
251
- "scoring": {
252
- "mode": "json_fields",
253
- "fields": ["triage", "priority", "should_process"]
254
- }
255
- }
256
- }
257
- ```
258
-
259
- If `fields` is omitted, the evaluator scores every key in the expected JSON
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
- ## Labeling Data With A Teacher Model
267
-
268
- `tt-local label` turns unlabeled inputs into training examples by asking a
269
- stronger teacher model on OpenRouter to answer each row under your spec's
270
- system message — the same instructions the fine-tuned model will see at
271
- inference:
272
-
273
- ```bash
274
- export OPENROUTER_API_KEY=... # or put it in .env
275
- tt-local label rows.jsonl --output labeled.jsonl
276
- tt-local label rows.csv --input-column text --model anthropic/claude-fable-5
277
- ```
278
-
279
- Sources are JSONL (`{"input": "..."}` per line; rows that already carry an
280
- `"output"` skip the teacher) or CSV with a header row and `--input-column`.
281
- The system message comes from `tunedtensor.json` (or `--spec <path>`);
282
- pass `--system-prompt "..."` to override it. Use `--dry-run` to parse,
283
- sanitize, and estimate without calling the teacher.
284
-
285
- Because rows are sent to OpenRouter, each row is sanitized first:
286
- secret-like content (API keys, private keys, connection strings) blocks the
287
- row, and PII (emails, phone numbers, SSNs, card numbers) is redacted before
288
- sending. The labeled `{"input", "output"}` JSONL, a full per-row
289
- `rows.jsonl` (including failures for review), and a job report are written
290
- under `<artifactRoot>/labeling/<job-id>/`.
291
-
292
- Teacher behavior is configured in the runner config; the model falls back to
293
- `llm.model`:
294
-
295
- ```json
296
- {
297
- "labeling": {
298
- "model": "anthropic/claude-fable-5",
299
- "maxTokens": 2048,
300
- "temperature": 0.2,
301
- "concurrency": 4,
302
- "maxAttempts": 3
303
- }
304
- }
305
- ```
306
-
307
- ## Thinking-Mode Models
308
-
309
- Some models, such as `Qwen/Qwen3.5-4B`, may open a hidden thinking block by
310
- default and consume the token budget before producing a visible answer. Forward
311
- template kwargs to both evaluation and training:
312
-
313
- ```json
314
- {
315
- "evaluation": {
316
- "inference": {
317
- "chatTemplateKwargs": { "enable_thinking": false }
318
- }
319
- }
320
- }
321
- ```
322
-
323
- In `tunedtensor.json`, also set:
324
-
325
- ```json
326
- {
327
- "hyperparameters": {
328
- "chat_template_kwargs": { "enable_thinking": false }
329
- }
330
- }
331
- ```
332
-
333
- ## Multimodal Examples
334
-
335
- Multimodal runs use chat JSONL with structured user content. For
336
- `image_text_to_text` models such as `Qwen/Qwen3-VL-2B-Instruct`, image parts
337
- are loaded by the local trainer and evaluator:
338
-
339
- ```json
340
- {
341
- "messages": [
342
- { "role": "system", "content": "Answer chart questions concisely." },
343
- {
344
- "role": "user",
345
- "content": [
346
- { "type": "image", "image": "charts/example.png" },
347
- { "type": "text", "text": "What is the blue value?" }
348
- ]
349
- },
350
- { "role": "assistant", "content": "42" }
351
- ]
352
- }
353
- ```
354
-
355
- Image values in prebuilt JSONL may be absolute paths, paths relative to the
356
- JSONL file, `file://` URIs, HTTP(S) URLs, or `data:` URIs. Behavior spec
357
- examples can also use `input_assets` with `image`, `path`, `uri`, or
358
- `data_uri`.
359
-
360
- ## Where Files Go
361
-
362
- If `storeRoot` is omitted, `tt-local` uses `TT_LOCAL_HOME` or
363
- `~/.tuned-tensor-local`.
364
-
365
- Per-run artifacts include:
366
-
367
- - `progress.jsonl`: stage changes and parsed training progress.
368
- - `baseline-eval.json` and `candidate-eval.json`: generated outputs and scores.
369
- - `baseline-eval.json.inference.log` and `candidate-eval.json.inference.log`:
370
- local inference subprocess output.
371
- - `training/training.log`: uv fine-tuning output.
372
- - `run-report.json`: baseline, candidate, comparison, and artifact references.
373
-
374
- ## Useful Commands
375
-
376
- ```bash
377
- tt-local init --name "Support Bot" --model Qwen/Qwen3.5-2B
378
- tt-local doctor --config spark-runner.json
379
- tt-local validate --config spark-runner.json
380
- tt-local run --config spark-runner.json
381
- tt-local run --config spark-runner.json --verbose
382
- tt-local label rows.jsonl --output labeled.jsonl
383
- tt-local runs list --config spark-runner.json
384
- tt-local runs get <run-id> --config spark-runner.json
385
- tt-local runs events <run-id> --config spark-runner.json
386
- tt-local runs watch <run-id> --config spark-runner.json
387
- tt-local runs report <run-id> --config spark-runner.json
388
- tt-local runs compare <run-id-a> <run-id-b> --config spark-runner.json
389
- tt-local models list --config spark-runner.json
390
- tt-local serve --config spark-runner.json
391
- ```
392
-
393
- ## DGX Spark Notes
394
-
395
- On the Spark host, confirm the GPU and uv are available:
396
-
397
- ```bash
398
- nvidia-smi
399
- uv --version
400
- uv run python --version
401
- ```
402
-
403
- Then clone, install, build, and link the CLI on the Spark:
404
-
405
- ```bash
406
- git clone https://github.com/tunedtensor/tuned-tensor-local.git
407
- cd tuned-tensor-local
408
- npm install
409
- npm run build
410
- npm link
411
- tt-local doctor --config examples/local-runner.json
412
- ```
413
-
414
- Spark-specific details live in [docs/spark.md](docs/spark.md).
415
-
416
- ## Development
30
+ ## Local Development
417
31
 
418
32
  ```bash
419
33
  npm install
@@ -422,18 +36,7 @@ npm test
422
36
  npm run build
423
37
  ```
424
38
 
425
- CI runs those checks on pull requests and pushes to `main`. Publishing to npm
426
- runs when a GitHub Release is published and requires an `NPM_TOKEN` repository
427
- secret.
428
-
429
- ## Notes
430
-
431
- - Real training uses `training/sft-local/src/train.py`.
432
- - Real evaluation uses `training/sft-local/src/evaluate.py`.
433
- - The default evaluation path uses Hugging Face/PEFT artifacts directly; it
434
- does not convert models to GGUF.
435
- - `validate` and `run` warn about unknown hyperparameter keys and ignore them.
436
- - Release notes are in [CHANGELOG.md](CHANGELOG.md).
39
+ Release notes are in [CHANGELOG.md](CHANGELOG.md).
437
40
 
438
41
  ## License
439
42
 
@@ -271,6 +271,7 @@ export declare const evalReportSchema: z.ZodObject<{
271
271
  inference_provider: z.ZodOptional<z.ZodEnum<{
272
272
  command: "command";
273
273
  none: "none";
274
+ batch_command: "batch_command";
274
275
  transformers: "transformers";
275
276
  }>>;
276
277
  scoring_mode: z.ZodOptional<z.ZodEnum<{
@@ -331,7 +332,10 @@ export declare const comparisonReportSchema: z.ZodObject<{
331
332
  }, z.core.$strip>>;
332
333
  }, z.core.$strip>;
333
334
  export declare const trainingReportSchema: z.ZodObject<{
334
- provider: z.ZodLiteral<"local-uv">;
335
+ provider: z.ZodEnum<{
336
+ "local-uv": "local-uv";
337
+ "local-command": "local-command";
338
+ }>;
335
339
  training_job_name: z.ZodString;
336
340
  model_artifact_uri: z.ZodOptional<z.ZodString>;
337
341
  base_model_artifact_uri: z.ZodOptional<z.ZodString>;
@@ -407,6 +411,7 @@ export declare const runReportSchema: z.ZodObject<{
407
411
  inference_provider: z.ZodOptional<z.ZodEnum<{
408
412
  command: "command";
409
413
  none: "none";
414
+ batch_command: "batch_command";
410
415
  transformers: "transformers";
411
416
  }>>;
412
417
  scoring_mode: z.ZodOptional<z.ZodEnum<{
@@ -487,6 +492,7 @@ export declare const runReportSchema: z.ZodObject<{
487
492
  inference_provider: z.ZodOptional<z.ZodEnum<{
488
493
  command: "command";
489
494
  none: "none";
495
+ batch_command: "batch_command";
490
496
  transformers: "transformers";
491
497
  }>>;
492
498
  scoring_mode: z.ZodOptional<z.ZodEnum<{
@@ -540,7 +546,10 @@ export declare const runReportSchema: z.ZodObject<{
540
546
  }, z.core.$strip>>;
541
547
  }, z.core.$strip>;
542
548
  training: z.ZodObject<{
543
- provider: z.ZodLiteral<"local-uv">;
549
+ provider: z.ZodEnum<{
550
+ "local-uv": "local-uv";
551
+ "local-command": "local-command";
552
+ }>;
544
553
  training_job_name: z.ZodString;
545
554
  model_artifact_uri: z.ZodOptional<z.ZodString>;
546
555
  base_model_artifact_uri: z.ZodOptional<z.ZodString>;
@@ -595,7 +604,11 @@ export declare const localRunnerConfigSchema: z.ZodObject<{
595
604
  artifactRoot: z.ZodDefault<z.ZodString>;
596
605
  dryRun: z.ZodDefault<z.ZodBoolean>;
597
606
  training: z.ZodDefault<z.ZodObject<{
598
- backend: z.ZodDefault<z.ZodLiteral<"uv">>;
607
+ backend: z.ZodDefault<z.ZodEnum<{
608
+ command: "command";
609
+ uv: "uv";
610
+ }>>;
611
+ command: z.ZodOptional<z.ZodArray<z.ZodString>>;
599
612
  project: z.ZodOptional<z.ZodString>;
600
613
  cwd: z.ZodOptional<z.ZodString>;
601
614
  module: z.ZodOptional<z.ZodString>;
@@ -615,12 +628,15 @@ export declare const localRunnerConfigSchema: z.ZodObject<{
615
628
  provider: z.ZodDefault<z.ZodEnum<{
616
629
  command: "command";
617
630
  none: "none";
631
+ batch_command: "batch_command";
618
632
  transformers: "transformers";
619
633
  }>>;
634
+ command: z.ZodOptional<z.ZodArray<z.ZodString>>;
620
635
  project: z.ZodOptional<z.ZodString>;
621
636
  cwd: z.ZodOptional<z.ZodString>;
622
637
  module: z.ZodOptional<z.ZodString>;
623
638
  script: z.ZodDefault<z.ZodString>;
639
+ args: z.ZodDefault<z.ZodArray<z.ZodString>>;
624
640
  with: z.ZodDefault<z.ZodArray<z.ZodString>>;
625
641
  env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
626
642
  maxNewTokens: z.ZodDefault<z.ZodNumber>;
package/dist/contracts.js CHANGED
@@ -133,7 +133,7 @@ export const evalReportSchema = z.object({
133
133
  artifact_uri: z.string(),
134
134
  scoring_method: z.enum(["heuristic", "command", "llm_judge", "exact_match", "json_fields"]),
135
135
  judge_model_id: z.string().nullable().optional(),
136
- inference_provider: z.enum(["none", "command", "transformers"]).optional(),
136
+ inference_provider: z.enum(["none", "command", "batch_command", "transformers"]).optional(),
137
137
  scoring_mode: z.enum(["exact_match", "llm_judge", "json_fields"]).optional(),
138
138
  json_field_metrics: jsonFieldMetricsSchema.optional(),
139
139
  generation_config: z.record(z.string(), z.unknown()).optional(),
@@ -157,7 +157,7 @@ export const comparisonReportSchema = z.object({
157
157
  })),
158
158
  });
159
159
  export const trainingReportSchema = z.object({
160
- provider: z.literal("local-uv"),
160
+ provider: z.enum(["local-uv", "local-command"]),
161
161
  training_job_name: z.string(),
162
162
  model_artifact_uri: z.string().optional(),
163
163
  base_model_artifact_uri: z.string().optional(),
@@ -220,7 +220,8 @@ export const localRunnerConfigSchema = z.object({
220
220
  artifactRoot: z.string().default(".tt-local/artifacts"),
221
221
  dryRun: z.boolean().default(false),
222
222
  training: z.object({
223
- backend: z.literal("uv").default("uv"),
223
+ backend: z.enum(["uv", "command"]).default("uv"),
224
+ command: commandSchema.optional(),
224
225
  project: z.string().optional(),
225
226
  cwd: z.string().optional(),
226
227
  module: z.string().optional(),
@@ -242,11 +243,13 @@ export const localRunnerConfigSchema = z.object({
242
243
  baselineCommand: commandSchema.optional(),
243
244
  candidateCommand: commandSchema.optional(),
244
245
  inference: z.object({
245
- provider: z.enum(["transformers", "command", "none"]).default("transformers"),
246
+ provider: z.enum(["transformers", "batch_command", "command", "none"]).default("transformers"),
247
+ command: commandSchema.optional(),
246
248
  project: z.string().optional(),
247
249
  cwd: z.string().optional(),
248
250
  module: z.string().optional(),
249
251
  script: z.string().default("training/sft-local/src/evaluate.py"),
252
+ args: z.array(z.string()).default([]),
250
253
  with: z.array(z.string()).default([]),
251
254
  env: z.record(z.string(), z.string()).default({}),
252
255
  maxNewTokens: z.number().int().min(1).default(256),
@@ -258,6 +261,7 @@ export const localRunnerConfigSchema = z.object({
258
261
  }).default({
259
262
  provider: "transformers",
260
263
  script: "training/sft-local/src/evaluate.py",
264
+ args: [],
261
265
  with: [],
262
266
  env: {},
263
267
  maxNewTokens: 256,
@@ -283,6 +287,7 @@ export const localRunnerConfigSchema = z.object({
283
287
  inference: {
284
288
  provider: "transformers",
285
289
  script: "training/sft-local/src/evaluate.py",
290
+ args: [],
286
291
  with: [],
287
292
  env: {},
288
293
  maxNewTokens: 256,