@tuned-tensor/local 0.1.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 (53) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/LICENSE +161 -0
  3. package/README.md +242 -0
  4. package/dist/artifacts.d.ts +35 -0
  5. package/dist/artifacts.js +56 -0
  6. package/dist/artifacts.js.map +1 -0
  7. package/dist/contracts.d.ts +471 -0
  8. package/dist/contracts.js +253 -0
  9. package/dist/contracts.js.map +1 -0
  10. package/dist/dataset.d.ts +12 -0
  11. package/dist/dataset.js +60 -0
  12. package/dist/dataset.js.map +1 -0
  13. package/dist/docker-training.d.ts +8 -0
  14. package/dist/docker-training.js +123 -0
  15. package/dist/docker-training.js.map +1 -0
  16. package/dist/doctor.d.ts +7 -0
  17. package/dist/doctor.js +74 -0
  18. package/dist/doctor.js.map +1 -0
  19. package/dist/evaluation.d.ts +23 -0
  20. package/dist/evaluation.js +318 -0
  21. package/dist/evaluation.js.map +1 -0
  22. package/dist/index.d.ts +14 -0
  23. package/dist/index.js +316 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/local-project.d.ts +22 -0
  26. package/dist/local-project.js +74 -0
  27. package/dist/local-project.js.map +1 -0
  28. package/dist/model-registry.d.ts +18 -0
  29. package/dist/model-registry.js +151 -0
  30. package/dist/model-registry.js.map +1 -0
  31. package/dist/openrouter.d.ts +16 -0
  32. package/dist/openrouter.js +39 -0
  33. package/dist/openrouter.js.map +1 -0
  34. package/dist/orchestrator.d.ts +14 -0
  35. package/dist/orchestrator.js +151 -0
  36. package/dist/orchestrator.js.map +1 -0
  37. package/dist/process-training.d.ts +8 -0
  38. package/dist/process-training.js +135 -0
  39. package/dist/process-training.js.map +1 -0
  40. package/dist/server.d.ts +9 -0
  41. package/dist/server.js +211 -0
  42. package/dist/server.js.map +1 -0
  43. package/dist/store.d.ts +98 -0
  44. package/dist/store.js +327 -0
  45. package/dist/store.js.map +1 -0
  46. package/docs/architecture.md +109 -0
  47. package/docs/spark.md +86 -0
  48. package/examples/local-runner.json +14 -0
  49. package/examples/smoke-run-request.json +34 -0
  50. package/package.json +40 -0
  51. package/training/sft-local/pyproject.toml +24 -0
  52. package/training/sft-local/src/evaluate.py +177 -0
  53. package/training/sft-local/src/train.py +233 -0
package/dist/store.js ADDED
@@ -0,0 +1,327 @@
1
+ import { appendFile, copyFile, mkdir, readdir, readFile, rename, stat, writeFile } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import { randomUUID } from "node:crypto";
5
+ export function defaultLocalHome() {
6
+ return resolve(process.env.TT_LOCAL_HOME ?? join(homedir(), ".tuned-tensor-local"));
7
+ }
8
+ export function localStorePaths(root) {
9
+ return {
10
+ root,
11
+ specsDir: join(root, "specs"),
12
+ runsDir: join(root, "runs"),
13
+ modelsDir: join(root, "models"),
14
+ datasetsDir: join(root, "datasets"),
15
+ catalogDir: join(root, "catalog"),
16
+ runsCatalog: join(root, "catalog", "runs.jsonl"),
17
+ specsCatalog: join(root, "catalog", "specs.jsonl"),
18
+ modelsCatalog: join(root, "catalog", "models.jsonl"),
19
+ datasetsCatalog: join(root, "catalog", "datasets.jsonl"),
20
+ };
21
+ }
22
+ async function exists(path) {
23
+ try {
24
+ await stat(path);
25
+ return true;
26
+ }
27
+ catch {
28
+ return false;
29
+ }
30
+ }
31
+ async function writeJsonAtomic(path, value) {
32
+ await mkdir(dirname(path), { recursive: true });
33
+ const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
34
+ await writeFile(tmp, `${JSON.stringify(value, null, 2)}\n`, "utf8");
35
+ await rename(tmp, path);
36
+ }
37
+ async function readJson(path) {
38
+ return JSON.parse(await readFile(path, "utf8"));
39
+ }
40
+ async function appendJsonl(path, value) {
41
+ await mkdir(dirname(path), { recursive: true });
42
+ await appendFile(path, `${JSON.stringify(value)}\n`, "utf8");
43
+ }
44
+ async function readJsonlLatestById(path) {
45
+ if (!(await exists(path)))
46
+ return [];
47
+ const rows = (await readFile(path, "utf8"))
48
+ .split(/\r?\n/)
49
+ .filter(Boolean)
50
+ .map((line) => JSON.parse(line));
51
+ return [...new Map(rows.map((row) => [row.id, row])).values()];
52
+ }
53
+ async function readJsonl(path) {
54
+ if (!(await exists(path)))
55
+ return [];
56
+ return (await readFile(path, "utf8"))
57
+ .split(/\r?\n/)
58
+ .filter(Boolean)
59
+ .map((line) => JSON.parse(line));
60
+ }
61
+ async function copyIfExists(from, to) {
62
+ if (!(await exists(from)))
63
+ return;
64
+ await mkdir(dirname(to), { recursive: true });
65
+ await copyFile(from, to);
66
+ }
67
+ export function createLocalStore(root = defaultLocalHome()) {
68
+ const resolvedRoot = resolve(root);
69
+ const paths = localStorePaths(resolvedRoot);
70
+ const runDir = (id) => join(paths.runsDir, id);
71
+ const runStatePath = (id) => join(runDir(id), "state.json");
72
+ const runEventsPath = (id) => join(runDir(id), "progress.jsonl");
73
+ const runRequestPath = (id) => join(runDir(id), "request.json");
74
+ const runReportPath = (id) => join(runDir(id), "run-report.json");
75
+ const specDir = (id) => join(paths.specsDir, id);
76
+ const specPath = (id) => join(specDir(id), "spec.json");
77
+ const modelPath = (id) => join(paths.modelsDir, id, "model.json");
78
+ async function ensure() {
79
+ await Promise.all([
80
+ mkdir(paths.specsDir, { recursive: true }),
81
+ mkdir(paths.runsDir, { recursive: true }),
82
+ mkdir(paths.modelsDir, { recursive: true }),
83
+ mkdir(paths.datasetsDir, { recursive: true }),
84
+ mkdir(paths.catalogDir, { recursive: true }),
85
+ ]);
86
+ }
87
+ async function writeRunState(state) {
88
+ await writeJsonAtomic(runStatePath(state.id), state);
89
+ await appendJsonl(paths.runsCatalog, { ...state, catalog_updated_at: new Date().toISOString() });
90
+ return state;
91
+ }
92
+ async function appendRunEvent(state, event) {
93
+ const row = {
94
+ id: randomUUID(),
95
+ run_id: state.id,
96
+ occurred_at: new Date().toISOString(),
97
+ ...event,
98
+ };
99
+ await appendJsonl(runEventsPath(state.id), row);
100
+ await appendJsonl(join(state.artifact_dir, "progress.jsonl"), {
101
+ at: row.occurred_at,
102
+ stage: row.stage,
103
+ status: row.status,
104
+ message: row.message,
105
+ ...(row.details ? { details: row.details } : {}),
106
+ });
107
+ }
108
+ async function resolveRunId(id) {
109
+ if (await exists(runStatePath(id)))
110
+ return id;
111
+ const records = await readJsonlLatestById(paths.runsCatalog);
112
+ const record = records.find((row) => row.id === id || row.id.startsWith(id));
113
+ if (!record)
114
+ throw new Error(`Run not found: ${id}`);
115
+ return record.id;
116
+ }
117
+ async function getRun(id) {
118
+ return readJson(runStatePath(await resolveRunId(id)));
119
+ }
120
+ return {
121
+ root: resolvedRoot,
122
+ paths,
123
+ ensure,
124
+ async importSpec(specId, spec) {
125
+ await ensure();
126
+ const now = new Date().toISOString();
127
+ const existingRecord = (await readJsonlLatestById(paths.specsCatalog))
128
+ .find((row) => row.id === specId);
129
+ const record = {
130
+ id: specId,
131
+ name: spec.name,
132
+ base_model: spec.base_model,
133
+ path: specPath(specId),
134
+ created_at: existingRecord?.created_at ?? now,
135
+ updated_at: now,
136
+ };
137
+ await writeJsonAtomic(specPath(specId), spec);
138
+ await appendJsonl(paths.specsCatalog, record);
139
+ return record;
140
+ },
141
+ async listSpecs() {
142
+ await ensure();
143
+ return (await readJsonlLatestById(paths.specsCatalog))
144
+ .sort((a, b) => b.updated_at.localeCompare(a.updated_at));
145
+ },
146
+ async getSpec(id) {
147
+ const records = await this.listSpecs();
148
+ const record = records.find((row) => row.id === id || row.id.startsWith(id));
149
+ if (!record)
150
+ throw new Error(`Spec not found: ${id}`);
151
+ return { ...record, spec: await readJson(record.path) };
152
+ },
153
+ async startRun({ request, artifactDir }) {
154
+ await ensure();
155
+ await this.importSpec(request.behavior_spec_id, request.spec_snapshot);
156
+ const now = new Date().toISOString();
157
+ const state = {
158
+ id: request.run_id,
159
+ behavior_spec_id: request.behavior_spec_id,
160
+ user_id: request.user_id,
161
+ run_number: request.run_number,
162
+ status: "queued",
163
+ current_stage: "queued",
164
+ status_message: "Run queued.",
165
+ artifact_dir: artifactDir,
166
+ base_model: request.spec_snapshot.base_model,
167
+ spec_name: request.spec_snapshot.name,
168
+ created_at: now,
169
+ updated_at: now,
170
+ started_at: now,
171
+ };
172
+ await writeJsonAtomic(runRequestPath(request.run_id), request);
173
+ await writeRunState(state);
174
+ await appendRunEvent(state, { stage: "queued", status: "queued", message: "Run queued." });
175
+ return state;
176
+ },
177
+ async updateRun({ runId, status, stage, message, details }) {
178
+ const previous = await getRun(runId);
179
+ const state = {
180
+ ...previous,
181
+ status,
182
+ current_stage: stage,
183
+ status_message: message,
184
+ updated_at: new Date().toISOString(),
185
+ };
186
+ await writeRunState(state);
187
+ await appendRunEvent(state, { stage, status: status === "completed" ? "completed" : "running", message, details });
188
+ return state;
189
+ },
190
+ async completeRun(report, artifactDir, reportPath) {
191
+ const previous = await getRun(report.run_id);
192
+ const modelId = `local-${report.run_id}`;
193
+ const completedAt = report.created_at ?? new Date().toISOString();
194
+ const state = {
195
+ ...previous,
196
+ status: "completed",
197
+ current_stage: "completed",
198
+ status_message: "Run completed successfully.",
199
+ report_path: reportPath,
200
+ model_id: modelId,
201
+ completed_at: completedAt,
202
+ updated_at: completedAt,
203
+ };
204
+ await writeRunState(state);
205
+ await copyIfExists(reportPath, runReportPath(report.run_id));
206
+ await appendRunEvent(state, {
207
+ stage: "completed",
208
+ status: "completed",
209
+ message: "Run completed successfully.",
210
+ details: { report_path: reportPath, model_id: modelId },
211
+ });
212
+ const model = {
213
+ id: modelId,
214
+ run_id: report.run_id,
215
+ behavior_spec_id: report.behavior_spec_id,
216
+ name: `${report.run_metadata?.base_model ?? report.base_model} (${report.run_id.slice(0, 8)})`,
217
+ provider: "local-uv",
218
+ base_model: report.base_model,
219
+ artifact_uri: report.fine_tuned_model_id,
220
+ artifact_dir: artifactDir,
221
+ metrics: report.training.metrics,
222
+ created_at: completedAt,
223
+ };
224
+ await writeJsonAtomic(modelPath(model.id), model);
225
+ await appendJsonl(paths.modelsCatalog, model);
226
+ return state;
227
+ },
228
+ async failRun(runId, error) {
229
+ const previous = await getRun(runId);
230
+ const now = new Date().toISOString();
231
+ const state = {
232
+ ...previous,
233
+ status: "failed",
234
+ current_stage: "failed",
235
+ status_message: error,
236
+ error,
237
+ completed_at: now,
238
+ updated_at: now,
239
+ };
240
+ await writeRunState(state);
241
+ await appendRunEvent(state, { stage: "failed", status: "failed", message: error });
242
+ return state;
243
+ },
244
+ async cancelRun(runId) {
245
+ const state = await getRun(runId);
246
+ await writeFile(join(runDir(state.id), "cancel.requested"), `${new Date().toISOString()}\n`, "utf8");
247
+ const updated = {
248
+ ...state,
249
+ status: "cancelled",
250
+ current_stage: "cancel_requested",
251
+ status_message: "Cancellation requested.",
252
+ updated_at: new Date().toISOString(),
253
+ };
254
+ await writeRunState(updated);
255
+ await appendRunEvent(updated, { stage: "cancel_requested", status: "cancelled", message: "Cancellation requested." });
256
+ },
257
+ async listRuns() {
258
+ await ensure();
259
+ return (await readJsonlLatestById(paths.runsCatalog))
260
+ .sort((a, b) => b.updated_at.localeCompare(a.updated_at));
261
+ },
262
+ getRun,
263
+ async getRunEvents(id) {
264
+ const state = await getRun(id);
265
+ return readJsonl(runEventsPath(state.id));
266
+ },
267
+ async getRunReport(id) {
268
+ const state = await getRun(id);
269
+ if (state.report_path && await exists(state.report_path)) {
270
+ return readJson(state.report_path);
271
+ }
272
+ const copiedReportPath = runReportPath(state.id);
273
+ if (await exists(copiedReportPath))
274
+ return readJson(copiedReportPath);
275
+ throw new Error(`Run has no report yet: ${id}`);
276
+ },
277
+ async listModels() {
278
+ await ensure();
279
+ return (await readJsonlLatestById(paths.modelsCatalog))
280
+ .sort((a, b) => b.created_at.localeCompare(a.created_at));
281
+ },
282
+ async getModel(id) {
283
+ const models = await this.listModels();
284
+ const record = models.find((row) => row.id === id || row.id.startsWith(id));
285
+ if (!record)
286
+ throw new Error(`Model not found: ${id}`);
287
+ return record;
288
+ },
289
+ async rebuildIndexes() {
290
+ await ensure();
291
+ const runRecords = [];
292
+ for (const entry of await readdir(paths.runsDir, { withFileTypes: true }).catch(() => [])) {
293
+ if (!entry.isDirectory())
294
+ continue;
295
+ const statePath = runStatePath(entry.name);
296
+ if (await exists(statePath)) {
297
+ const state = await readJson(statePath);
298
+ runRecords.push({ ...state, catalog_updated_at: new Date().toISOString() });
299
+ }
300
+ }
301
+ const specRecords = [];
302
+ for (const entry of await readdir(paths.specsDir, { withFileTypes: true }).catch(() => [])) {
303
+ if (!entry.isDirectory())
304
+ continue;
305
+ const path = specPath(entry.name);
306
+ if (await exists(path)) {
307
+ const spec = await readJson(path);
308
+ const now = new Date().toISOString();
309
+ specRecords.push({ id: entry.name, name: spec.name, base_model: spec.base_model, path, created_at: now, updated_at: now });
310
+ }
311
+ }
312
+ const modelRecords = [];
313
+ for (const entry of await readdir(paths.modelsDir, { withFileTypes: true }).catch(() => [])) {
314
+ if (!entry.isDirectory())
315
+ continue;
316
+ const path = modelPath(entry.name);
317
+ if (await exists(path))
318
+ modelRecords.push(await readJson(path));
319
+ }
320
+ await writeFile(paths.runsCatalog, runRecords.map((row) => JSON.stringify(row)).join("\n") + (runRecords.length ? "\n" : ""), "utf8");
321
+ await writeFile(paths.specsCatalog, specRecords.map((row) => JSON.stringify(row)).join("\n") + (specRecords.length ? "\n" : ""), "utf8");
322
+ await writeFile(paths.modelsCatalog, modelRecords.map((row) => JSON.stringify(row)).join("\n") + (modelRecords.length ? "\n" : ""), "utf8");
323
+ await writeFile(paths.datasetsCatalog, "", "utf8");
324
+ },
325
+ };
326
+ }
327
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,109 @@
1
+ # Architecture
2
+
3
+ Tuned Tensor 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: persists specs, runs, progress events, reports, and model
17
+ records as JSON/JSONL under a configured `storeRoot` for the dashboard and CLI.
18
+ - Training adapter: launches a uv-managed Python process with local input,
19
+ model cache, and output directories passed through environment variables.
20
+ - Evaluation adapter: runs local Hugging Face/PEFT inference for baseline and
21
+ candidate evaluation. Evaluation can also use an OpenRouter LLM judge to score
22
+ locally generated outputs.
23
+ - Report writer: emits structured JSON reports that are portable across local
24
+ and hosted environments.
25
+ - Dashboard/API server: serves local run/model/spec metadata from the local
26
+ state store and accepts local run submissions.
27
+
28
+ ## Repository Boundary
29
+
30
+ This repository should remain a clean open-source implementation. Private
31
+ deployment code, proprietary provider adapters, account projection code, and
32
+ billing integrations stay out of this project.
33
+
34
+ The hosted Tuned Tensor CLI can keep cloud-specific workflows such as auth,
35
+ accounts, billing, and managed runs. `tt-local` must be useful on its own: local
36
+ spec creation, validation, execution, run tracking, model inspection, and the
37
+ dashboard should not depend on the hosted CLI.
38
+
39
+ ## First Milestone
40
+
41
+ The first runnable milestone should support:
42
+
43
+ - one local run at a time;
44
+ - one GPU device;
45
+ - local filesystem artifacts;
46
+ - local file-backed run/model/spec tracking;
47
+ - standalone `tt-local init`, `validate`, `run`, `serve`, and inspection
48
+ commands;
49
+ - uv-based training;
50
+ - native Transformers/PEFT evaluation with optional OpenRouter judge scoring;
51
+ - local dashboard and CLI inspection commands;
52
+ - a tiny smoke-run fixture that finishes quickly.
53
+
54
+ ## Configuration Shape
55
+
56
+ The public runner should accept a plain JSON run request and a local runner
57
+ configuration. The run request describes the behavior spec, base model,
58
+ examples, and hyperparameters. The local runner configuration describes artifact
59
+ paths, store paths, uv entrypoint settings, model cache paths, OpenRouter judge
60
+ settings, and timeout limits.
61
+
62
+ ## Evaluation Loop
63
+
64
+ The local loop intentionally compares like-for-like model artifacts:
65
+
66
+ - baseline loads the original Hugging Face base model from the behavior spec;
67
+ - training writes a Hugging Face/PEFT artifact under the run directory;
68
+ - candidate evaluation loads the same base model plus that fine-tuned artifact;
69
+ - the report compares the two eval reports and stores generated outputs.
70
+
71
+ The first native backend is `transformers`. `llama.cpp`/GGUF conversion is not
72
+ part of the default path because conversion would make the baseline/candidate
73
+ comparison less direct.
74
+
75
+ ## Local State Layout
76
+
77
+ The local store is intentionally transparent and easy to back up:
78
+
79
+ - `specs/<spec-id>/spec.json`
80
+ - `runs/<run-id>/request.json`
81
+ - `runs/<run-id>/state.json`
82
+ - `runs/<run-id>/progress.jsonl`
83
+ - `runs/<run-id>/run-report.json`
84
+ - `models/<model-id>/model.json`
85
+ - `catalog/*.jsonl`
86
+
87
+ Catalog files are append-only indexes for fast listing. `tt-local store
88
+ rebuild-index` can reconstruct them from the canonical per-object files.
89
+
90
+ ## Dashboard API
91
+
92
+ The local server is a lightweight Node HTTP server, not a separate database.
93
+ Current endpoints include:
94
+
95
+ - `GET /api/health`
96
+ - `GET /api/runs`, `GET /api/runs/:id`, `GET /api/runs/:id/events`
97
+ - `GET /api/runs/:id/events/stream`
98
+ - `GET /api/runs/:id/report`
99
+ - `POST /api/runs`
100
+ - `POST /api/runs/:id/cancel`
101
+ - `GET /api/models`, `GET /api/models/:id`
102
+ - `GET /api/specs`, `GET /api/specs/:id`
103
+
104
+ ## Failure Handling
105
+
106
+ The orchestrator should fail fast when prerequisites are missing. Common checks
107
+ include uv availability, Python dependency resolution, GPU visibility, model
108
+ cache permissions, artifact directory writability, and dataset size limits for
109
+ the selected model.
package/docs/spark.md ADDED
@@ -0,0 +1,86 @@
1
+ # Running On DGX Spark
2
+
3
+ Tuned Tensor 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.
6
+
7
+ ## Host Checks
8
+
9
+ Run these on the Spark host:
10
+
11
+ ```bash
12
+ nvidia-smi
13
+ uv --version
14
+ uv run python --version
15
+ ```
16
+
17
+ If uv is missing, install it with the official standalone installer:
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:
37
+
38
+ ```bash
39
+ tt-local run examples/smoke-run-request.json --config examples/local-runner.json
40
+ ```
41
+
42
+ ## Real Training
43
+
44
+ For real training, set `dryRun: false` and configure the uv training entrypoint:
45
+
46
+ ```json
47
+ {
48
+ "artifactRoot": "/home/eve/tt-local-artifacts",
49
+ "dryRun": false,
50
+ "training": {
51
+ "backend": "uv",
52
+ "project": "training/sft-local",
53
+ "script": "training/sft-local/src/train.py"
54
+ },
55
+ "paths": {
56
+ "modelCache": "/home/eve/.cache/huggingface"
57
+ }
58
+ }
59
+ ```
60
+
61
+ The runner sets:
62
+
63
+ - `SM_CHANNEL_TRAINING` to the local chat JSONL training directory;
64
+ - `TT_HYPERPARAMETERS_PATH` to the generated hyperparameter JSON file;
65
+ - `SM_OUTPUT_DIR` to the local training output directory;
66
+ - `SM_MODEL_DIR` to the local model output directory;
67
+ - `SM_CHANNEL_BASE_MODEL` when a local base-model artifact path is configured;
68
+ - `HF_HOME` when `paths.modelCache` is configured.
69
+
70
+ Real runs also use the Transformers/PEFT evaluator by default. Baseline
71
+ evaluation loads the original Hugging Face model, and candidate evaluation loads
72
+ that same base model plus the fine-tuned artifact from the run directory. Set
73
+ `paths.modelCache` to a persistent Spark-local cache so training and evaluation
74
+ reuse downloads.
75
+
76
+ The included first-pass SFT script can be run by the local runner through uv:
77
+
78
+ ```bash
79
+ tt-local run examples/smoke-run-request.json --config examples/local-runner.json
80
+ ```
81
+
82
+ The SFT uv project currently points Linux installs at the PyTorch CUDA 13.0
83
+ wheel index. If PyTorch does not publish a compatible wheel for the Spark's
84
+ architecture, use `training.env`, `training.project`, or the SFT
85
+ `pyproject.toml` to point uv at the NVIDIA/PyTorch package source that matches
86
+ the host.
@@ -0,0 +1,14 @@
1
+ {
2
+ "artifactRoot": ".tt-local/artifacts",
3
+ "storeRoot": ".tt-local/store",
4
+ "dryRun": true,
5
+ "training": {
6
+ "backend": "uv",
7
+ "project": "training/sft-local",
8
+ "script": "training/sft-local/src/train.py"
9
+ },
10
+ "evaluation": {
11
+ "mode": "heuristic",
12
+ "maxExamples": 2
13
+ }
14
+ }
@@ -0,0 +1,34 @@
1
+ {
2
+ "run_id": "11111111-1111-4111-8111-111111111111",
3
+ "user_id": "local-user",
4
+ "behavior_spec_id": "22222222-2222-4222-8222-222222222222",
5
+ "run_number": 1,
6
+ "spec_snapshot": {
7
+ "name": "Local Smoke Assistant",
8
+ "description": "Tiny local runner smoke test.",
9
+ "system_prompt": "Answer in a concise, literal style.",
10
+ "guidelines": [
11
+ "Prefer the expected label when the input is a label request."
12
+ ],
13
+ "constraints": [
14
+ "Do not include extra commentary."
15
+ ],
16
+ "base_model": "Qwen/Qwen3.5-2B",
17
+ "examples": [
18
+ {
19
+ "input": "Classify sentiment: I love this product.",
20
+ "output": "positive"
21
+ },
22
+ {
23
+ "input": "Classify sentiment: This was disappointing.",
24
+ "output": "negative"
25
+ }
26
+ ]
27
+ },
28
+ "hyperparameters": {
29
+ "n_epochs": 1,
30
+ "augment": false,
31
+ "use_llm_judge": false,
32
+ "save_adapter_only": true
33
+ }
34
+ }
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@tuned-tensor/local",
3
+ "version": "0.1.0",
4
+ "description": "Local, cloud-independent fine-tuning runner for Tuned Tensor behavior specs.",
5
+ "license": "Apache-2.0",
6
+ "private": false,
7
+ "type": "module",
8
+ "bin": {
9
+ "tt-local": "dist/index.js",
10
+ "tuned-tensor-local": "dist/index.js"
11
+ },
12
+ "exports": {
13
+ ".": "./dist/index.js"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "docs",
18
+ "examples",
19
+ "training",
20
+ "README.md",
21
+ "CHANGELOG.md",
22
+ "LICENSE"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsc -p tsconfig.json",
26
+ "typecheck": "tsc -p tsconfig.json --noEmit",
27
+ "test": "node --import tsx --test \"test/**/*.test.ts\""
28
+ },
29
+ "engines": {
30
+ "node": ">=22"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^25.0.0",
34
+ "tsx": "^4.22.4",
35
+ "typescript": "^6.0.0"
36
+ },
37
+ "dependencies": {
38
+ "zod": "^4.4.3"
39
+ }
40
+ }
@@ -0,0 +1,24 @@
1
+ [project]
2
+ name = "tuned-tensor-local-sft"
3
+ version = "0.1.0"
4
+ requires-python = ">=3.11"
5
+ dependencies = [
6
+ "accelerate>=1.0.0",
7
+ "peft>=0.13.0",
8
+ "safetensors>=0.4.5",
9
+ "torch>=2.11.0",
10
+ "transformers>=4.46.0",
11
+ ]
12
+
13
+ [tool.uv.sources]
14
+ torch = [
15
+ { index = "pytorch-cu130", marker = "sys_platform == 'linux'" },
16
+ ]
17
+
18
+ [[tool.uv.index]]
19
+ name = "pytorch-cu130"
20
+ url = "https://download.pytorch.org/whl/cu130"
21
+ explicit = true
22
+
23
+ [tool.uv]
24
+ package = false