@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.
Files changed (89) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/README.md +110 -201
  3. package/dist/active-model.d.ts +16 -0
  4. package/dist/active-model.js +88 -0
  5. package/dist/active-model.js.map +1 -0
  6. package/dist/artifacts.d.ts +5 -4
  7. package/dist/artifacts.js +57 -33
  8. package/dist/artifacts.js.map +1 -1
  9. package/dist/compare.d.ts +1 -8
  10. package/dist/compare.js +1 -23
  11. package/dist/compare.js.map +1 -1
  12. package/dist/contracts.d.ts +234 -365
  13. package/dist/contracts.js +95 -224
  14. package/dist/contracts.js.map +1 -1
  15. package/dist/dataset.d.ts +11 -4
  16. package/dist/dataset.js +67 -125
  17. package/dist/dataset.js.map +1 -1
  18. package/dist/doctor.d.ts +2 -3
  19. package/dist/doctor.js +23 -161
  20. package/dist/doctor.js.map +1 -1
  21. package/dist/evaluation.d.ts +11 -71
  22. package/dist/evaluation.js +212 -572
  23. package/dist/evaluation.js.map +1 -1
  24. package/dist/general-regression.d.ts +10 -0
  25. package/dist/general-regression.js +16 -0
  26. package/dist/general-regression.js.map +1 -0
  27. package/dist/huggingface-cache.d.ts +8 -7
  28. package/dist/huggingface-cache.js +16 -8
  29. package/dist/huggingface-cache.js.map +1 -1
  30. package/dist/index.d.ts +2 -10
  31. package/dist/index.js +358 -627
  32. package/dist/index.js.map +1 -1
  33. package/dist/local-project.d.ts +1 -11
  34. package/dist/local-project.js +33 -61
  35. package/dist/local-project.js.map +1 -1
  36. package/dist/model-registry.d.ts +3 -16
  37. package/dist/model-registry.js +76 -292
  38. package/dist/model-registry.js.map +1 -1
  39. package/dist/model-server.d.ts +7 -3
  40. package/dist/model-server.js +34 -25
  41. package/dist/model-server.js.map +1 -1
  42. package/dist/orchestrator.d.ts +11 -25
  43. package/dist/orchestrator.js +393 -563
  44. package/dist/orchestrator.js.map +1 -1
  45. package/dist/prefetch.d.ts +1 -5
  46. package/dist/prefetch.js +14 -19
  47. package/dist/prefetch.js.map +1 -1
  48. package/dist/process-runner.d.ts +10 -29
  49. package/dist/process-runner.js +63 -169
  50. package/dist/process-runner.js.map +1 -1
  51. package/dist/process-training.d.ts +0 -1
  52. package/dist/process-training.js +10 -87
  53. package/dist/process-training.js.map +1 -1
  54. package/dist/store.d.ts +1 -3
  55. package/dist/store.js +92 -290
  56. package/dist/store.js.map +1 -1
  57. package/docs/architecture.md +87 -152
  58. package/docs/spark.md +66 -97
  59. package/examples/dry-runner.json +14 -0
  60. package/examples/general-regression.jsonl +4 -0
  61. package/examples/local-runner.json +13 -6
  62. package/examples/smoke-spec.json +41 -0
  63. package/package.json +6 -6
  64. package/training/local-runner/pyproject.toml +0 -5
  65. package/training/local-runner/src/evaluate.py +89 -234
  66. package/training/local-runner/src/model_contract.py +47 -0
  67. package/training/local-runner/src/prefetch.py +18 -4
  68. package/training/local-runner/src/serve.py +60 -117
  69. package/training/local-runner/src/sft_data.py +97 -0
  70. package/training/local-runner/src/train.py +146 -346
  71. package/training/local-runner/uv.lock +0 -1197
  72. package/dist/labeling-sanitize.d.ts +0 -31
  73. package/dist/labeling-sanitize.js +0 -158
  74. package/dist/labeling-sanitize.js.map +0 -1
  75. package/dist/labeling.d.ts +0 -155
  76. package/dist/labeling.js +0 -496
  77. package/dist/labeling.js.map +0 -1
  78. package/dist/openrouter.d.ts +0 -29
  79. package/dist/openrouter.js +0 -66
  80. package/dist/openrouter.js.map +0 -1
  81. package/dist/server.d.ts +0 -9
  82. package/dist/server.js +0 -211
  83. package/dist/server.js.map +0 -1
  84. package/docs/local-workflow-remediation-2026-07-13.md +0 -130
  85. package/docs/local-workflow-ux-review-2026-07-13.md +0 -458
  86. package/examples/dpo-preferences.jsonl +0 -2
  87. package/examples/dpo-run-request.json +0 -34
  88. package/examples/smoke-run-request.json +0 -34
  89. package/training/local-runner/src/train_dpo.py +0 -286
@@ -1,154 +1,89 @@
1
1
  # Architecture
2
2
 
3
- TT Local is organized around a small set of replaceable local
4
- adapters. The first implementation targets a single Linux GPU host and is
5
- packaged as a standalone CLI.
6
-
7
- ## Subsystems
8
-
9
- - Local orchestrator: runs the workflow states in-process and records progress
10
- to local files.
11
- - Local project spec: `tt-local init` creates a local `tunedtensor.json`, and
12
- `tt-local run` converts that spec into the same run request contract used by
13
- compatible hosted exports.
14
- - Artifact store: writes datasets, logs, model outputs, and reports beneath a
15
- configured local artifact root.
16
- - Local state store: keeps a SQLite metadata index for specs, runs, progress
17
- events, and model records while preserving JSON/JSONL files under a
18
- configured `storeRoot` for portable artifacts, recovery, and inspection.
19
- - Training adapter: launches either a uv-managed Python process or an arbitrary
20
- command with local input, model cache, and output directories passed through
21
- environment variables. Bundled uv training supports SFT and text-only offline
22
- DPO; command training can implement custom methods.
23
- - Evaluation adapter: runs local Hugging Face/PEFT inference, an arbitrary
24
- batch command, or per-example commands for baseline and candidate evaluation.
25
- Evaluation can also use an OpenRouter LLM judge to score locally generated
26
- outputs.
27
- - Report writer: emits structured JSON reports that are portable across local
28
- and hosted environments.
29
- - Dashboard/API server: serves local run/model/spec metadata from the local
30
- state store and accepts local run submissions.
31
- - Artifact verifier: validates the trained adapter/full-model contract, writes
32
- an atomic SHA-256 manifest, and registers the model before downstream
33
- evaluation so successful training survives later-stage failures.
34
- - Model inference server: loads the recorded base model plus the verified local
35
- adapter and exposes localhost `/health`, `/v1/models`, and OpenAI-compatible
36
- `/v1/chat/completions` endpoints.
37
-
38
- ## Repository Boundary
39
-
40
- This repository should remain a clean open-source implementation. Private
41
- deployment code, proprietary provider adapters, account projection code, and
42
- billing integrations stay out of this project.
43
-
44
- The hosted Tuned Tensor CLI can keep cloud-specific workflows such as auth,
45
- accounts, billing, and managed runs. `tt-local` must be useful on its own: local
46
- spec creation, validation, execution, run tracking, model inspection, and the
47
- dashboard should not depend on the hosted CLI.
48
-
49
- ## First Milestone
50
-
51
- The first runnable milestone should support:
52
-
53
- - one local run at a time;
54
- - one GPU device;
55
- - local filesystem artifacts;
56
- - SQLite-backed run/model/spec tracking with recoverable JSON files;
57
- - standalone `tt-local init`, `validate`, `run`, `serve`, and inspection
58
- commands;
59
- - uv-based or command-based training;
60
- - native Transformers/PEFT, batch-command, or per-example command evaluation
61
- with optional OpenRouter judge scoring;
62
- - verified model manifests and local adapter serving;
63
- - local dashboard and CLI inspection commands;
64
- - a tiny smoke-run fixture that finishes quickly.
65
-
66
- ## Configuration Shape
67
-
68
- The public runner should accept a plain JSON run request and a local runner
69
- configuration. The run request describes the training method, behavior spec,
70
- base model, examples, datasets, and hyperparameters. The local runner
71
- configuration describes artifact paths, store paths, uv or command entrypoint
72
- settings, model cache paths, OpenRouter judge settings, and timeout limits.
73
-
74
- ## Evaluation Loop
75
-
76
- The default Transformers loop intentionally compares like-for-like model artifacts:
77
-
78
- - baseline loads the original Hugging Face base model from the behavior spec;
79
- - training writes a Hugging Face/PEFT artifact under the run directory;
80
- - candidate evaluation loads the same base model plus that fine-tuned artifact;
81
- - the report compares the two eval reports and stores generated outputs.
82
-
83
- The first native backend is `transformers`. `llama.cpp`/GGUF conversion is not
84
- part of the default path because conversion would make the baseline/candidate
85
- comparison less direct.
86
-
87
- Custom model families can use `training.backend: "command"` and
88
- `evaluation.inference.provider: "batch_command"`. Batch evaluators receive the
89
- same `--input`/`--output` JSON files as the bundled Transformers evaluator, so a
90
- custom workflow can load any artifact format as long as it writes compatible
91
- evaluation results.
92
-
93
- `paths.modelCache` is a single cache contract: it is passed as Hugging Face
94
- `HF_HOME`, and Hub snapshots live beneath `<modelCache>/hub`. The environment
95
- is finalized before importing Hugging Face or Transformers in every bundled
96
- Python process. Prefetch records the resolved snapshot revision, file count,
97
- and byte count; `models verify-base` performs a local-only structural check of
98
- the cached config, tokenizer, weights, and indexed shards.
99
-
100
- An explicitly configured `paths.baseModel` is a complete Hugging Face snapshot
101
- directory, never a generic weights file or archive. Its file contents are
102
- fingerprinted for stage identity. Remote runs should set an immutable
103
- `base_model_revision`; baseline caching is bypassed when the model or a remote
104
- input lacks stable content identity.
105
-
106
- ## Local State Layout
107
-
108
- The local store is intentionally transparent and easy to back up:
109
-
110
- - `metadata.sqlite`
111
- - `specs/<spec-id>/spec.json`
112
- - `runs/<run-id>/request.json`
113
- - `runs/<run-id>/state.json`
114
- - `runs/<run-id>/progress.jsonl`
115
- - `runs/<run-id>/run-report.json`
116
- - `models/<model-id>/model.json`
117
-
118
- Each successful real training stage also writes `artifact-manifest.json` with
119
- the model contract and SHA-256/size entries for immutable files. Dry runs do
120
- not create model records. `tt-local models verify` accepts a model ID, exact
121
- artifact path, or its manifest path and re-hashes the model before it is served
122
- or handed off.
123
-
124
- The default Transformers/PEFT contract requires exact adapter weights plus a
125
- non-empty `adapter_config.json`; a full-model contract requires recognized
126
- Transformers model weights. Command backends may publish another layout only
127
- with an explicit framework/format contract, and such artifacts are not
128
- implicitly servable.
129
-
130
- SQLite is the primary metadata index for CLI and dashboard listings. The
131
- per-object JSON files remain the recoverable source for artifacts and
132
- human-readable inspection. `tt-local store rebuild-index` can reconstruct the
133
- SQLite index from the canonical per-object files.
134
-
135
- ## Dashboard API
136
-
137
- The local server is a lightweight Node HTTP server backed by the local state
138
- store. Current endpoints include:
139
-
140
- - `GET /api/health`
141
- - `GET /api/runs`, `GET /api/runs/:id`, `GET /api/runs/:id/events`
142
- - `GET /api/runs/:id/events/stream`
143
- - `GET /api/runs/:id/report`
144
- - `POST /api/runs`
145
- - `POST /api/runs/:id/cancel`
146
- - `GET /api/models`, `GET /api/models/:id`
147
- - `GET /api/specs`, `GET /api/specs/:id`
148
-
149
- ## Failure Handling
150
-
151
- The orchestrator should fail fast when prerequisites are missing. Common checks
152
- include uv availability, Python dependency resolution, GPU visibility, model
153
- cache permissions, artifact directory writability, and dataset size limits for
154
- the selected model.
3
+ TT Local is a focused, single-host fine-tuning tool. Its supported product
4
+ boundary is text SFT of `Qwen/Qwen3.5-2B` into a LoRA adapter on CUDA.
5
+
6
+ ## Workflow
7
+
8
+ 1. Parse a strict behavior spec or request.
9
+ 2. Compile or validate chat JSONL and select distinct training/evaluation rows.
10
+ 3. Evaluate the base model with the bundled Transformers runner.
11
+ 4. Train a PEFT adapter with the bundled, locked `uv` project.
12
+ 5. Verify and register the adapter before downstream evaluation.
13
+ 6. Evaluate the adapter on the same held-out rows.
14
+ 7. Write a base-versus-tuned report.
15
+
16
+ The orchestrator retains explicit stages and fingerprints internally so an
17
+ interrupted run can safely resume. These mechanics are reliability
18
+ implementation details, not a public plugin framework.
19
+
20
+ ## Boundaries
21
+
22
+ The first version intentionally excludes:
23
+
24
+ - arbitrary training or evaluation commands;
25
+ - DPO, continued pretraining, and multimodal training;
26
+ - cloud labeling or LLM-as-judge calls;
27
+ - classic tabular model studies;
28
+ - a local web dashboard;
29
+ - unverified model-family claims.
30
+
31
+ The next model or method should arrive with its own locked dependencies,
32
+ resource defaults, data contract, and real CUDA acceptance test.
33
+
34
+ ## Python boundary
35
+
36
+ Node owns input validation, state, process lifecycle, manifests, and reports.
37
+ The bundled Python project owns four small operations:
38
+
39
+ - `prefetch.py`: download and verify the pinned Hugging Face snapshot;
40
+ - `evaluate.py`: generate predictions without receiving reference answers;
41
+ - `train.py`: perform CUDA-only Qwen LoRA SFT;
42
+ - `serve.py`: load the base model and verified adapter for inference.
43
+
44
+ Training uses assistant-only loss. Prompt tokens have label `-100`, and
45
+ truncation removes older prompt tokens before it removes any answer token.
46
+ Tokenizer chat-template failures are fatal because a fallback would silently
47
+ change the trained or served prompt format.
48
+
49
+ ## Evaluation
50
+
51
+ Baseline and candidate evaluation use the same input IDs, generation settings,
52
+ and deterministic scoring:
53
+
54
+ - baseline: recorded base-model snapshot;
55
+ - candidate: that base snapshot plus the run's verified PEFT adapter;
56
+ - scoring: normalized exact match or selected JSON fields.
57
+
58
+ The Python evaluator receives only opaque IDs and prompts. Node joins its
59
+ predictions to trusted references before scoring. This avoids accidental label
60
+ leakage across the process boundary.
61
+
62
+ ## Storage and integrity
63
+
64
+ Canonical JSON and JSONL files are the state store; there is no database or
65
+ mirrored index. Writes are atomic and listings scan the small local metadata
66
+ tree deterministically.
67
+
68
+ Each real adapter has an atomic `artifact-manifest.json` with the expected PEFT
69
+ files, byte sizes, and SHA-256 hashes. A model is registered immediately after
70
+ training and manifest verification, so a later evaluation failure does not
71
+ hide a valid artifact. Serving verifies the manifest again.
72
+
73
+ ## Configuration
74
+
75
+ The runner config controls only:
76
+
77
+ - state, artifact, and Hugging Face cache paths;
78
+ - CUDA or CPU inference for evaluation and serving;
79
+ - deterministic generation and scoring limits.
80
+
81
+ The training project, Python entrypoints, working directory, and child
82
+ environment are internal and fixed. Every stage uses the locked `uv` project
83
+ included in the npm package. The mutable virtual environment lives in the
84
+ user cache, keyed by the project and lockfile content, so a read-only global
85
+ npm install remains runnable.
86
+
87
+ Schemas are strict. Misspelled or obsolete fields fail validation instead of
88
+ silently reverting to defaults. Training itself is CUDA-only and fails fast
89
+ when PyTorch cannot see the GPU.
package/docs/spark.md CHANGED
@@ -1,134 +1,103 @@
1
- # Running On DGX Spark
1
+ # DGX Spark
2
2
 
3
- TT Local expects the Spark to behave like a single Linux GPU host:
4
- uv must be installed, Python dependencies must resolve, and CUDA/PyTorch must be
5
- able to see the GPU.
3
+ DGX Spark is the reference host for TT Local's first supported path:
4
+ `Qwen/Qwen3.5-2B` text SFT with a LoRA adapter.
6
5
 
7
- ## Host Checks
6
+ ## Check the host
8
7
 
9
- Run these on the Spark host:
8
+ Run on the Spark:
10
9
 
11
10
  ```bash
12
11
  nvidia-smi
12
+ node --version
13
13
  uv --version
14
- uv run python --version
15
14
  ```
16
15
 
17
- If uv is missing, install it with the official standalone installer:
16
+ TT Local requires Node 22+, `uv`, working CUDA PyTorch, and enough free space
17
+ for the Hugging Face cache plus run artifacts.
18
18
 
19
- ```bash
20
- curl -LsSf https://astral.sh/uv/install.sh | sh
21
- ```
22
-
23
- Open a new shell or source the installer-updated profile before rerunning
24
- `uv --version`.
25
-
26
- Then run the project doctor command:
27
-
28
- ```bash
29
- npm run build
30
- tt-local doctor --config examples/local-runner.json
31
- ```
32
-
33
- ## Dry Run
34
-
35
- The example config uses `dryRun: true`, so it validates orchestration, dataset
36
- compilation, artifact writing, and report generation without launching training:
19
+ ## Create a project
37
20
 
38
21
  ```bash
39
- tt-local run examples/smoke-run-request.json --config examples/local-runner.json
22
+ mkdir -p ~/tuned-tensor-runs/support-adapter
23
+ cd ~/tuned-tensor-runs/support-adapter
24
+ tt-local init --name "Support Adapter" --model Qwen/Qwen3.5-2B --profile spark
40
25
  ```
41
26
 
42
- ## Real Training
27
+ Edit both generated examples in `tunedtensor.json`. For a meaningful run,
28
+ replace them with a larger, representative dataset and a separate validation
29
+ split.
43
30
 
44
- For real training with the bundled SFT or DPO workflow, install `tt-local`, set
45
- `dryRun: false`, and leave the uv project pointed at `training/local-runner`.
46
- That project is bundled in the npm package, so users do not need a local source
47
- checkout. Leave `training.script` unset: the runner chooses the bundled script
48
- from the run request's `training_method`.
49
-
50
- - `training_method: "sft"` runs `training/local-runner/src/train.py`.
51
- - `training_method: "dpo"` runs `training/local-runner/src/train_dpo.py`.
52
-
53
- Set `training.script` only when you want to override that selection with a
54
- specific custom uv script.
31
+ The generated `local-runner.json` uses CUDA and project-local artifacts. A
32
+ durable Spark configuration can set:
55
33
 
56
34
  ```json
57
35
  {
58
- "artifactRoot": "/home/eve/tt-local-artifacts",
59
- "dryRun": false,
60
- "training": {
61
- "backend": "uv",
62
- "project": "training/local-runner"
63
- },
36
+ "artifactRoot": "/home/eve/tuned-tensor-runs/artifacts",
37
+ "storeRoot": "/home/eve/tuned-tensor-runs/store",
64
38
  "paths": {
65
39
  "modelCache": "/home/eve/.cache/huggingface"
40
+ },
41
+ "evaluation": {
42
+ "inference": {
43
+ "device": "cuda"
44
+ },
45
+ "scoring": {
46
+ "mode": "exact_match"
47
+ },
48
+ "timeoutMs": 1800000
66
49
  }
67
50
  }
68
51
  ```
69
52
 
70
- The runner sets:
71
-
72
- - `SM_CHANNEL_TRAINING` to the local training JSONL directory;
73
- - `TT_HYPERPARAMETERS_PATH` to the generated hyperparameter JSON file;
74
- - `SM_OUTPUT_DIR` to the local training output directory;
75
- - `SM_MODEL_DIR` to the local model output directory;
76
- - `SM_CHANNEL_BASE_MODEL` when a local base-model artifact path is configured;
77
- - `HF_HOME` when `paths.modelCache` is configured.
78
-
79
- Real runs also use the Transformers/PEFT evaluator by default. Baseline
80
- evaluation loads the original Hugging Face model, and candidate evaluation loads
81
- that same base model plus the fine-tuned artifact from the run directory. Set
82
- `paths.modelCache` to a persistent Spark-local cache so training and evaluation
83
- reuse downloads.
53
+ Every Python stage uses the locked runtime included in the npm package; a
54
+ source checkout and a custom runner path are neither required nor supported.
84
55
 
85
- To make the first large download explicit, prefetch the base model after
86
- creating `tunedtensor.json` and the runner config:
56
+ ## Preflight and run
87
57
 
88
58
  ```bash
89
- tt-local models prefetch tunedtensor.json --config spark-runner.json
59
+ tt-local doctor tunedtensor.json
60
+ tt-local validate tunedtensor.json
61
+ tt-local models prefetch tunedtensor.json
62
+ tt-local models verify-base tunedtensor.json
63
+ tt-local run tunedtensor.json
90
64
  ```
91
65
 
92
- If you skip this step, the first real `tt-local run` still downloads through
93
- Transformers automatically.
66
+ `doctor` resolves the same bundled project and paths the run will use, imports
67
+ Torch/Transformers/PEFT, requires visible CUDA, checks writable storage, and
68
+ rejects unchanged placeholders. `validate` reads and normalizes the actual
69
+ dataset before any run state or artifact directory is claimed.
94
70
 
95
- Custom workflows such as a from-scratch nanoGPT trainer can use command
96
- entrypoints instead:
71
+ The runner provides these paths to Python:
97
72
 
98
- ```json
99
- {
100
- "artifactRoot": "/home/eve/tt-local-artifacts",
101
- "dryRun": false,
102
- "training": {
103
- "backend": "command",
104
- "command": ["python", "nanogpt/train.py"]
105
- },
106
- "evaluation": {
107
- "inference": {
108
- "provider": "batch_command",
109
- "command": ["python", "nanogpt/evaluate.py"]
110
- },
111
- "scoring": {
112
- "mode": "exact_match"
113
- }
114
- }
115
- }
116
- ```
73
+ - `SM_CHANNEL_TRAINING`: prepared chat JSONL directory;
74
+ - `TT_HYPERPARAMETERS_PATH`: generated SFT/LoRA parameters;
75
+ - `SM_OUTPUT_DIR`: logs and metrics;
76
+ - `SM_MODEL_DIR`: adapter output;
77
+ - `SM_CHANNEL_BASE_MODEL`: optional verified local model snapshot;
78
+ - `HF_HOME`: configured persistent model cache.
79
+
80
+ ## Verify and serve
117
81
 
118
- The training command receives the same environment variables listed above. The
119
- batch evaluator is called with `--input <path> --output <path>` and should write
120
- the same results JSON shape as the bundled evaluator.
82
+ ```bash
83
+ tt-local runs report <run-id>
84
+ tt-local models verify local-<run-id>
85
+ tt-local serve local-<run-id> --spec tunedtensor.json --port 8000
86
+ ```
121
87
 
122
- The included first-pass SFT and DPO scripts can be run by the local runner
123
- through uv:
88
+ In another shell:
124
89
 
125
90
  ```bash
126
- tt-local run examples/smoke-run-request.json --config examples/local-runner.json
127
- tt-local run examples/dpo-run-request.json --config examples/local-runner.json
91
+ curl http://127.0.0.1:8000/health
92
+ curl http://127.0.0.1:8000/v1/models
93
+ curl http://127.0.0.1:8000/v1/chat/completions \
94
+ -H 'Content-Type: application/json' \
95
+ -d '{"messages":[{"role":"user","content":"Classify: I loved it."}]}'
128
96
  ```
129
97
 
130
- The SFT uv project currently points Linux installs at the PyTorch CUDA 13.0
131
- wheel index. If PyTorch does not publish a compatible wheel for the Spark's
132
- architecture, use `training.env`, `training.project`, or the SFT
133
- `pyproject.toml` to point uv at the NVIDIA/PyTorch package source that matches
134
- the host.
98
+ If a run fails, start with `tt-local runs events <run-id>` and
99
+ `tt-local runs get <run-id>`. The run record reports its `artifact_dir`; the
100
+ main subprocess logs there are `training/training.log`,
101
+ `baseline-eval.json.inference.log`, and `candidate-eval.json.inference.log`.
102
+ The adapter is registered as soon as its manifest verifies, even if candidate
103
+ evaluation fails afterward.
@@ -0,0 +1,14 @@
1
+ {
2
+ "artifactRoot": ".tt-local/dry-artifacts",
3
+ "storeRoot": ".tt-local/dry-store",
4
+ "dryRun": true,
5
+ "evaluation": {
6
+ "inference": {
7
+ "device": "cuda"
8
+ },
9
+ "scoring": {
10
+ "mode": "exact_match"
11
+ },
12
+ "maxExamples": 2
13
+ }
14
+ }
@@ -0,0 +1,4 @@
1
+ {"messages":[{"role":"system","content":"Follow the instruction exactly and answer without explanation."},{"role":"user","content":"Answer with only the numeral: What is 7 + 5?"},{"role":"assistant","content":"12"}]}
2
+ {"messages":[{"role":"system","content":"Follow the instruction exactly and answer without explanation."},{"role":"user","content":"Reply with exactly the lowercase word blue."},{"role":"assistant","content":"blue"}]}
3
+ {"messages":[{"role":"system","content":"Follow the instruction exactly and answer without explanation."},{"role":"user","content":"Return the three letters ABC in reverse order."},{"role":"assistant","content":"CBA"}]}
4
+ {"messages":[{"role":"system","content":"Follow the instruction exactly and answer without explanation."},{"role":"user","content":"Answer yes or no only: Is London in the United Kingdom?"},{"role":"assistant","content":"yes"}]}
@@ -1,18 +1,25 @@
1
1
  {
2
2
  "artifactRoot": ".tt-local/artifacts",
3
3
  "storeRoot": ".tt-local/store",
4
- "dryRun": true,
5
- "training": {
6
- "backend": "uv",
7
- "project": "training/local-runner"
4
+ "paths": {
5
+ "modelCache": "~/.cache/huggingface"
8
6
  },
9
7
  "evaluation": {
10
8
  "inference": {
11
- "provider": "none"
9
+ "device": "cuda",
10
+ "maxNewTokens": 32,
11
+ "temperature": 0,
12
+ "topP": 1
12
13
  },
13
14
  "scoring": {
14
15
  "mode": "exact_match"
15
16
  },
16
- "maxExamples": 2
17
+ "generalRegression": {
18
+ "dataset": "general-regression.jsonl",
19
+ "maxScoreDrop": 0.03,
20
+ "maxPassRateDrop": 0.05
21
+ },
22
+ "maxExamples": 2,
23
+ "timeoutMs": 1800000
17
24
  }
18
25
  }
@@ -0,0 +1,41 @@
1
+ {
2
+ "id": "22222222-2222-4222-8222-222222222222",
3
+ "name": "Local Smoke Assistant",
4
+ "description": "Tiny local runner smoke test.",
5
+ "system_prompt": "Return exactly one sentiment label.",
6
+ "guidelines": [
7
+ "Reply with positive or negative."
8
+ ],
9
+ "constraints": [
10
+ "Do not include extra commentary."
11
+ ],
12
+ "base_model": "Qwen/Qwen3.5-2B",
13
+ "examples": [
14
+ {
15
+ "input": "Classify sentiment: I love this product.",
16
+ "output": "positive"
17
+ },
18
+ {
19
+ "input": "Classify sentiment: This was disappointing.",
20
+ "output": "negative"
21
+ },
22
+ {
23
+ "input": "Classify sentiment: The update works perfectly.",
24
+ "output": "positive"
25
+ },
26
+ {
27
+ "input": "Classify sentiment: Support never replied.",
28
+ "output": "negative"
29
+ }
30
+ ],
31
+ "hyperparameters": {
32
+ "n_epochs": 1,
33
+ "learning_rate": 0.0002,
34
+ "batch_size": 1,
35
+ "gradient_accumulation_steps": 1,
36
+ "lora_rank": 4,
37
+ "lora_alpha": 8,
38
+ "max_seq_length": 256,
39
+ "max_eval_examples": 2
40
+ }
41
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tuned-tensor/local",
3
- "version": "0.2.9",
4
- "description": "Local-first fine-tuning with baseline-vs-tuned evaluation for small open-weight models.",
3
+ "version": "0.4.0",
4
+ "description": "Local CUDA LoRA fine-tuning with held-out base-versus-tuned evaluation.",
5
5
  "license": "Apache-2.0",
6
6
  "private": false,
7
7
  "repository": {
@@ -28,22 +28,22 @@
28
28
  "LICENSE"
29
29
  ],
30
30
  "scripts": {
31
- "build": "tsc -p tsconfig.json",
31
+ "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
32
+ "build": "npm run clean && tsc -p tsconfig.json",
32
33
  "prepack": "npm run build",
33
34
  "typecheck": "tsc -p tsconfig.json --noEmit",
34
- "test": "node --import tsx --test \"test/**/*.test.ts\""
35
+ "test": "node --import tsx --test \"test/**/*.test.ts\"",
36
+ "test:python": "python3 -m unittest discover -s training/local-runner/tests -p 'test_*.py'"
35
37
  },
36
38
  "engines": {
37
39
  "node": ">=22"
38
40
  },
39
41
  "devDependencies": {
40
- "@types/better-sqlite3": "^7.6.13",
41
42
  "@types/node": "^25.0.0",
42
43
  "tsx": "^4.22.4",
43
44
  "typescript": "^6.0.0"
44
45
  },
45
46
  "dependencies": {
46
- "better-sqlite3": "^12.11.1",
47
47
  "zod": "^4.4.3"
48
48
  }
49
49
  }
@@ -4,16 +4,11 @@ version = "0.1.0"
4
4
  requires-python = ">=3.11"
5
5
  dependencies = [
6
6
  "accelerate>=1.0.0",
7
- "datasets>=4.0.0",
8
7
  "huggingface-hub>=0.36.0",
9
8
  "peft>=0.13.0",
10
- "pillow>=10.0.0",
11
- "qwen-vl-utils>=0.0.14",
12
9
  "safetensors>=0.4.5",
13
10
  "torch>=2.11.0",
14
- "torchvision>=0.24.0",
15
11
  "transformers>=4.57.0",
16
- "trl>=1.7.0",
17
12
  ]
18
13
 
19
14
  [tool.uv.sources]