@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.
- package/CHANGELOG.md +23 -0
- package/LICENSE +161 -0
- package/README.md +242 -0
- package/dist/artifacts.d.ts +35 -0
- package/dist/artifacts.js +56 -0
- package/dist/artifacts.js.map +1 -0
- package/dist/contracts.d.ts +471 -0
- package/dist/contracts.js +253 -0
- package/dist/contracts.js.map +1 -0
- package/dist/dataset.d.ts +12 -0
- package/dist/dataset.js +60 -0
- package/dist/dataset.js.map +1 -0
- package/dist/docker-training.d.ts +8 -0
- package/dist/docker-training.js +123 -0
- package/dist/docker-training.js.map +1 -0
- package/dist/doctor.d.ts +7 -0
- package/dist/doctor.js +74 -0
- package/dist/doctor.js.map +1 -0
- package/dist/evaluation.d.ts +23 -0
- package/dist/evaluation.js +318 -0
- package/dist/evaluation.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +316 -0
- package/dist/index.js.map +1 -0
- package/dist/local-project.d.ts +22 -0
- package/dist/local-project.js +74 -0
- package/dist/local-project.js.map +1 -0
- package/dist/model-registry.d.ts +18 -0
- package/dist/model-registry.js +151 -0
- package/dist/model-registry.js.map +1 -0
- package/dist/openrouter.d.ts +16 -0
- package/dist/openrouter.js +39 -0
- package/dist/openrouter.js.map +1 -0
- package/dist/orchestrator.d.ts +14 -0
- package/dist/orchestrator.js +151 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/process-training.d.ts +8 -0
- package/dist/process-training.js +135 -0
- package/dist/process-training.js.map +1 -0
- package/dist/server.d.ts +9 -0
- package/dist/server.js +211 -0
- package/dist/server.js.map +1 -0
- package/dist/store.d.ts +98 -0
- package/dist/store.js +327 -0
- package/dist/store.js.map +1 -0
- package/docs/architecture.md +109 -0
- package/docs/spark.md +86 -0
- package/examples/local-runner.json +14 -0
- package/examples/smoke-run-request.json +34 -0
- package/package.json +40 -0
- package/training/sft-local/pyproject.toml +24 -0
- package/training/sft-local/src/evaluate.py +177 -0
- 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
|