@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
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname, resolve } from "node:path";
|
|
4
|
+
import { performance } from "node:perf_hooks";
|
|
5
|
+
import { writeJson, fileUri } from "./artifacts.js";
|
|
6
|
+
import { openRouterChat } from "./openrouter.js";
|
|
7
|
+
function normalize(value) {
|
|
8
|
+
return value.trim().replace(/\s+/g, " ").toLowerCase();
|
|
9
|
+
}
|
|
10
|
+
function scoreActual(expected, actual) {
|
|
11
|
+
return normalize(expected) === normalize(actual) ? 1 : 0;
|
|
12
|
+
}
|
|
13
|
+
function fileUriToPath(value) {
|
|
14
|
+
if (!value)
|
|
15
|
+
return undefined;
|
|
16
|
+
return value.startsWith("file://") ? value.slice("file://".length) : value;
|
|
17
|
+
}
|
|
18
|
+
async function runInferenceCommand(args) {
|
|
19
|
+
const [cmd, ...cmdArgs] = args.command;
|
|
20
|
+
const payload = JSON.stringify({
|
|
21
|
+
system: args.system,
|
|
22
|
+
prompt: args.prompt,
|
|
23
|
+
expected: args.expected,
|
|
24
|
+
});
|
|
25
|
+
return await new Promise((resolve, reject) => {
|
|
26
|
+
const child = spawn(cmd, cmdArgs, {
|
|
27
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
28
|
+
env: process.env,
|
|
29
|
+
});
|
|
30
|
+
let stdout = "";
|
|
31
|
+
let stderr = "";
|
|
32
|
+
const timer = setTimeout(() => {
|
|
33
|
+
child.kill("SIGTERM");
|
|
34
|
+
reject(new Error(`Inference command timed out after ${args.timeoutMs}ms`));
|
|
35
|
+
}, args.timeoutMs);
|
|
36
|
+
child.stdout.setEncoding("utf8");
|
|
37
|
+
child.stderr.setEncoding("utf8");
|
|
38
|
+
child.stdout.on("data", (chunk) => { stdout += chunk; });
|
|
39
|
+
child.stderr.on("data", (chunk) => { stderr += chunk; });
|
|
40
|
+
child.on("error", (error) => {
|
|
41
|
+
clearTimeout(timer);
|
|
42
|
+
reject(error);
|
|
43
|
+
});
|
|
44
|
+
child.on("close", (code) => {
|
|
45
|
+
clearTimeout(timer);
|
|
46
|
+
if (code !== 0) {
|
|
47
|
+
reject(new Error(`Inference command exited ${code}: ${stderr.slice(0, 1000)}`));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const trimmed = stdout.trim();
|
|
51
|
+
try {
|
|
52
|
+
const parsed = JSON.parse(trimmed);
|
|
53
|
+
const content = parsed.content ?? parsed.output ?? parsed.actual;
|
|
54
|
+
resolve(typeof content === "string" ? content : trimmed);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
resolve(trimmed);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
child.stdin.end(payload);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function buildUvInferenceArgs(config, inputPath, outputPath) {
|
|
64
|
+
const inference = config.evaluation.inference;
|
|
65
|
+
const args = ["run"];
|
|
66
|
+
if (inference.project)
|
|
67
|
+
args.push("--project", inference.project);
|
|
68
|
+
for (const dependency of inference.with ?? [])
|
|
69
|
+
args.push("--with", dependency);
|
|
70
|
+
args.push("python");
|
|
71
|
+
if (inference.module) {
|
|
72
|
+
args.push("-m", inference.module);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
args.push(inference.script);
|
|
76
|
+
}
|
|
77
|
+
args.push("--input", inputPath, "--output", outputPath);
|
|
78
|
+
return args;
|
|
79
|
+
}
|
|
80
|
+
async function runTransformersInference(args) {
|
|
81
|
+
const inputPath = `${args.outputPath}.inference-input.json`;
|
|
82
|
+
const outputPath = `${args.outputPath}.inference-output.json`;
|
|
83
|
+
await mkdir(dirname(inputPath), { recursive: true });
|
|
84
|
+
await writeFile(inputPath, `${JSON.stringify({
|
|
85
|
+
kind: args.kind,
|
|
86
|
+
model_id: args.modelId,
|
|
87
|
+
base_model: args.baseModelId,
|
|
88
|
+
adapter_path: fileUriToPath(args.adapterPath),
|
|
89
|
+
system: args.system,
|
|
90
|
+
examples: args.examples,
|
|
91
|
+
model_cache: args.config.paths.modelCache ? resolve(args.config.paths.modelCache) : undefined,
|
|
92
|
+
trust_remote_code: args.config.evaluation.inference.trustRemoteCode,
|
|
93
|
+
device: args.config.evaluation.inference.device,
|
|
94
|
+
generation: {
|
|
95
|
+
max_new_tokens: args.config.evaluation.inference.maxNewTokens,
|
|
96
|
+
temperature: args.config.evaluation.inference.temperature,
|
|
97
|
+
top_p: args.config.evaluation.inference.topP,
|
|
98
|
+
},
|
|
99
|
+
}, null, 2)}\n`, "utf8");
|
|
100
|
+
const uvArgs = buildUvInferenceArgs(args.config, inputPath, outputPath);
|
|
101
|
+
await new Promise((resolvePromise, reject) => {
|
|
102
|
+
const child = spawn("uv", uvArgs, {
|
|
103
|
+
cwd: args.config.evaluation.inference.cwd ? resolve(args.config.evaluation.inference.cwd) : process.cwd(),
|
|
104
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
105
|
+
env: {
|
|
106
|
+
...process.env,
|
|
107
|
+
...args.config.evaluation.inference.env,
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
let stderr = "";
|
|
111
|
+
const timer = setTimeout(() => {
|
|
112
|
+
child.kill("SIGTERM");
|
|
113
|
+
reject(new Error(`Transformers inference timed out after ${args.config.evaluation.timeoutMs}ms`));
|
|
114
|
+
}, args.config.evaluation.timeoutMs);
|
|
115
|
+
child.stderr.setEncoding("utf8");
|
|
116
|
+
child.stderr.on("data", (chunk) => { stderr += chunk; });
|
|
117
|
+
child.on("error", (error) => {
|
|
118
|
+
clearTimeout(timer);
|
|
119
|
+
reject(error);
|
|
120
|
+
});
|
|
121
|
+
child.on("close", (code) => {
|
|
122
|
+
clearTimeout(timer);
|
|
123
|
+
if (code !== 0) {
|
|
124
|
+
reject(new Error(`Transformers inference exited ${code}: ${stderr.slice(0, 1000)}`));
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
resolvePromise();
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
return JSON.parse(await readFile(outputPath, "utf8"));
|
|
131
|
+
}
|
|
132
|
+
async function judgeWithOpenRouter(args) {
|
|
133
|
+
if (!args.config.llm) {
|
|
134
|
+
throw new Error("evaluation.mode=llm_judge requires llm OpenRouter config");
|
|
135
|
+
}
|
|
136
|
+
const result = await openRouterChat([
|
|
137
|
+
{
|
|
138
|
+
role: "system",
|
|
139
|
+
content: "You are a strict evaluator. Return JSON only with keys score (0 to 1), passed (boolean), and reasoning (string).",
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
role: "user",
|
|
143
|
+
content: JSON.stringify({
|
|
144
|
+
prompt: args.prompt,
|
|
145
|
+
expected: args.expected,
|
|
146
|
+
actual: args.actual,
|
|
147
|
+
}),
|
|
148
|
+
},
|
|
149
|
+
], {
|
|
150
|
+
model: args.config.llm.model,
|
|
151
|
+
apiKeyEnv: args.config.llm.apiKeyEnv,
|
|
152
|
+
appName: args.config.llm.appName,
|
|
153
|
+
siteUrl: args.config.llm.siteUrl,
|
|
154
|
+
timeoutMs: args.config.evaluation.timeoutMs,
|
|
155
|
+
});
|
|
156
|
+
const parsed = JSON.parse(result.content);
|
|
157
|
+
const score = typeof parsed.score === "number"
|
|
158
|
+
? Math.max(0, Math.min(1, parsed.score))
|
|
159
|
+
: 0;
|
|
160
|
+
return {
|
|
161
|
+
score,
|
|
162
|
+
passed: typeof parsed.passed === "boolean" ? parsed.passed : score === 1,
|
|
163
|
+
reasoning: typeof parsed.reasoning === "string" ? parsed.reasoning : "OpenRouter judge returned no reasoning.",
|
|
164
|
+
model: result.model ?? args.config.llm.model,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function canUseOpenRouterJudge(config) {
|
|
168
|
+
return Boolean(config.llm && process.env[config.llm.apiKeyEnv]);
|
|
169
|
+
}
|
|
170
|
+
export async function evaluateExamples(args) {
|
|
171
|
+
const maxExamples = args.config.evaluation.maxExamples ?? args.examples.length;
|
|
172
|
+
const selected = args.examples.slice(0, maxExamples);
|
|
173
|
+
const command = args.kind === "baseline"
|
|
174
|
+
? args.config.evaluation.baselineCommand
|
|
175
|
+
: args.config.evaluation.candidateCommand;
|
|
176
|
+
const inferenceProvider = args.config.dryRun
|
|
177
|
+
? "none"
|
|
178
|
+
: args.config.evaluation.mode === "command" && command
|
|
179
|
+
? "command"
|
|
180
|
+
: args.config.evaluation.inference.provider;
|
|
181
|
+
const scoringMode = args.config.dryRun || inferenceProvider === "none" || inferenceProvider === "command"
|
|
182
|
+
? "exact_match"
|
|
183
|
+
: args.config.evaluation.mode === "llm_judge"
|
|
184
|
+
? "llm_judge"
|
|
185
|
+
: args.config.evaluation.scoring.mode;
|
|
186
|
+
const results = [];
|
|
187
|
+
let judgeModelId = null;
|
|
188
|
+
let generationConfig;
|
|
189
|
+
const inferred = inferenceProvider === "transformers"
|
|
190
|
+
? await runTransformersInference({
|
|
191
|
+
kind: args.kind,
|
|
192
|
+
modelId: args.modelId,
|
|
193
|
+
baseModelId: args.baseModelId ?? args.modelId,
|
|
194
|
+
adapterPath: args.adapterPath,
|
|
195
|
+
examples: selected,
|
|
196
|
+
system: args.system,
|
|
197
|
+
config: args.config,
|
|
198
|
+
outputPath: args.outputPath,
|
|
199
|
+
})
|
|
200
|
+
: null;
|
|
201
|
+
generationConfig = inferred?.generation_config;
|
|
202
|
+
for (const [index, example] of selected.entries()) {
|
|
203
|
+
const started = performance.now();
|
|
204
|
+
const commandActual = inferenceProvider === "command" && command
|
|
205
|
+
? await runInferenceCommand({
|
|
206
|
+
command,
|
|
207
|
+
prompt: example.input,
|
|
208
|
+
system: args.system,
|
|
209
|
+
expected: example.output,
|
|
210
|
+
timeoutMs: args.config.evaluation.timeoutMs,
|
|
211
|
+
})
|
|
212
|
+
: undefined;
|
|
213
|
+
const inferredResult = inferred?.results[index];
|
|
214
|
+
const actual = commandActual ?? inferredResult?.actual ?? "";
|
|
215
|
+
const exactScore = scoreActual(example.output, actual);
|
|
216
|
+
const shouldJudge = scoringMode === "llm_judge" && canUseOpenRouterJudge(args.config);
|
|
217
|
+
if (scoringMode === "llm_judge" && !shouldJudge && args.config.evaluation.scoring.fallback === "fail") {
|
|
218
|
+
const keyName = args.config.llm?.apiKeyEnv ?? "OPENROUTER_API_KEY";
|
|
219
|
+
throw new Error(`evaluation.scoring.mode=llm_judge requires ${keyName} or scoring.fallback=exact_match`);
|
|
220
|
+
}
|
|
221
|
+
const judged = shouldJudge
|
|
222
|
+
? await judgeWithOpenRouter({
|
|
223
|
+
prompt: example.input,
|
|
224
|
+
expected: example.output,
|
|
225
|
+
actual,
|
|
226
|
+
config: args.config,
|
|
227
|
+
})
|
|
228
|
+
: null;
|
|
229
|
+
if (judged?.model)
|
|
230
|
+
judgeModelId = judged.model;
|
|
231
|
+
const latencyMs = inferredResult?.latency_ms ?? Math.max(0, Math.round(performance.now() - started));
|
|
232
|
+
const score = judged?.score ?? exactScore;
|
|
233
|
+
results.push({
|
|
234
|
+
prompt: example.input,
|
|
235
|
+
expected: example.output,
|
|
236
|
+
actual,
|
|
237
|
+
passed: judged?.passed ?? score === 1,
|
|
238
|
+
score,
|
|
239
|
+
reasoning: judged?.reasoning ?? (inferenceProvider === "none"
|
|
240
|
+
? "No inference command configured; recorded an empty local response."
|
|
241
|
+
: scoringMode === "llm_judge"
|
|
242
|
+
? "OpenRouter judge unavailable; fell back to normalized exact match."
|
|
243
|
+
: "Scored by normalized exact match."),
|
|
244
|
+
latency_ms: latencyMs,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
const total = results.length;
|
|
248
|
+
const avgScore = total > 0
|
|
249
|
+
? results.reduce((sum, result) => sum + result.score, 0) / total
|
|
250
|
+
: 0;
|
|
251
|
+
const passRate = total > 0
|
|
252
|
+
? results.filter((result) => result.passed).length / total
|
|
253
|
+
: 0;
|
|
254
|
+
const exactMatchRate = total > 0
|
|
255
|
+
? results.filter((result) => scoreActual(result.expected, result.actual) === 1).length / total
|
|
256
|
+
: 0;
|
|
257
|
+
const avgLatency = total > 0
|
|
258
|
+
? Math.round(results.reduce((sum, result) => sum + result.latency_ms, 0) / total)
|
|
259
|
+
: 0;
|
|
260
|
+
const scoringMethod = judgeModelId
|
|
261
|
+
? "llm_judge"
|
|
262
|
+
: inferenceProvider === "command"
|
|
263
|
+
? "command"
|
|
264
|
+
: inferenceProvider === "none"
|
|
265
|
+
? "heuristic"
|
|
266
|
+
: "exact_match";
|
|
267
|
+
const report = {
|
|
268
|
+
kind: args.kind,
|
|
269
|
+
model_id: args.modelId,
|
|
270
|
+
total,
|
|
271
|
+
eval_examples_total: args.examples.length,
|
|
272
|
+
eval_examples_used: total,
|
|
273
|
+
eval_truncated: args.examples.length > total,
|
|
274
|
+
avg_score: avgScore,
|
|
275
|
+
pass_rate: passRate,
|
|
276
|
+
exact_match_rate: exactMatchRate,
|
|
277
|
+
avg_latency_ms: avgLatency,
|
|
278
|
+
results,
|
|
279
|
+
artifact_uri: fileUri(args.outputPath),
|
|
280
|
+
scoring_method: scoringMethod,
|
|
281
|
+
judge_model_id: judgeModelId,
|
|
282
|
+
inference_provider: inferenceProvider,
|
|
283
|
+
scoring_mode: scoringMode,
|
|
284
|
+
generation_config: generationConfig,
|
|
285
|
+
};
|
|
286
|
+
await writeJson(args.outputPath, report);
|
|
287
|
+
return report;
|
|
288
|
+
}
|
|
289
|
+
export function compareEvalReports(baseline, candidate) {
|
|
290
|
+
let regressions = 0;
|
|
291
|
+
let improvements = 0;
|
|
292
|
+
const regressedExamples = [];
|
|
293
|
+
const count = Math.min(baseline.results.length, candidate.results.length);
|
|
294
|
+
for (let index = 0; index < count; index += 1) {
|
|
295
|
+
const oldScore = baseline.results[index]?.score ?? 0;
|
|
296
|
+
const newScore = candidate.results[index]?.score ?? 0;
|
|
297
|
+
if (newScore < oldScore) {
|
|
298
|
+
regressions += 1;
|
|
299
|
+
regressedExamples.push({
|
|
300
|
+
prompt: baseline.results[index]?.prompt ?? "",
|
|
301
|
+
old_score: oldScore,
|
|
302
|
+
new_score: newScore,
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
else if (newScore > oldScore) {
|
|
306
|
+
improvements += 1;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return {
|
|
310
|
+
avg_score_delta: candidate.avg_score - baseline.avg_score,
|
|
311
|
+
pass_rate_delta: candidate.pass_rate - baseline.pass_rate,
|
|
312
|
+
exact_match_rate_delta: candidate.exact_match_rate - baseline.exact_match_rate,
|
|
313
|
+
regressions,
|
|
314
|
+
improvements,
|
|
315
|
+
regressed_examples: regressedExamples,
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
//# sourceMappingURL=evaluation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluation.js","sourceRoot":"","sources":["../src/evaluation.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
export * from "./contracts.js";
|
|
3
|
+
export * from "./dataset.js";
|
|
4
|
+
export * from "./orchestrator.js";
|
|
5
|
+
export * from "./local-project.js";
|
|
6
|
+
export * from "./openrouter.js";
|
|
7
|
+
export * from "./server.js";
|
|
8
|
+
export * from "./store.js";
|
|
9
|
+
export interface LocalRunnerInfo {
|
|
10
|
+
name: "tuned-tensor-local";
|
|
11
|
+
status: "local-runner-preview";
|
|
12
|
+
description: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function getLocalRunnerInfo(): LocalRunnerInfo;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { fineTuneRunRequestSchema, localBehaviorSpecFileSchema, localRunnerConfigSchema, specSnapshotSchema } from "./contracts.js";
|
|
5
|
+
import { loadLocalRunnerConfig, runLocalFineTune, } from "./orchestrator.js";
|
|
6
|
+
import { runDoctor } from "./doctor.js";
|
|
7
|
+
import { createLocalStore } from "./store.js";
|
|
8
|
+
import { serveLocalDashboard } from "./server.js";
|
|
9
|
+
import { DEFAULT_LOCAL_SPEC_PATH, initLocalSpecFile, loadLocalRunInput, runRequestFromLocalSpec, } from "./local-project.js";
|
|
10
|
+
export * from "./contracts.js";
|
|
11
|
+
export * from "./dataset.js";
|
|
12
|
+
export * from "./orchestrator.js";
|
|
13
|
+
export * from "./local-project.js";
|
|
14
|
+
export * from "./openrouter.js";
|
|
15
|
+
export * from "./server.js";
|
|
16
|
+
export * from "./store.js";
|
|
17
|
+
export function getLocalRunnerInfo() {
|
|
18
|
+
return {
|
|
19
|
+
name: "tuned-tensor-local",
|
|
20
|
+
status: "local-runner-preview",
|
|
21
|
+
description: "Local fine-tuning runner for single-GPU uv/Python hosts.",
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function readOption(argv, name) {
|
|
25
|
+
const index = argv.indexOf(name);
|
|
26
|
+
if (index === -1)
|
|
27
|
+
return undefined;
|
|
28
|
+
return argv[index + 1];
|
|
29
|
+
}
|
|
30
|
+
function hasFlag(argv, name) {
|
|
31
|
+
return argv.includes(name);
|
|
32
|
+
}
|
|
33
|
+
function printHelp() {
|
|
34
|
+
console.log(`tt-local
|
|
35
|
+
|
|
36
|
+
Commands:
|
|
37
|
+
info
|
|
38
|
+
init [--name "My Local Model"] [--model Qwen/Qwen3.5-2B] [--output tunedtensor.json] [--force]
|
|
39
|
+
doctor [--config local-runner.json]
|
|
40
|
+
validate [tunedtensor.json|request.json] [--config local-runner.json]
|
|
41
|
+
run [tunedtensor.json|request.json] [--config local-runner.json] [--dry-run]
|
|
42
|
+
serve [--config local-runner.json] [--host 127.0.0.1] [--port 8787]
|
|
43
|
+
runs list|get|events|watch|report|cancel|reconcile [args] [--config local-runner.json]
|
|
44
|
+
models list|get [args] [--config local-runner.json]
|
|
45
|
+
specs list|get|import [args] [--config local-runner.json]
|
|
46
|
+
store rebuild-index [--config local-runner.json]
|
|
47
|
+
|
|
48
|
+
The run command writes local artifacts under config.artifactRoot, defaulting to
|
|
49
|
+
.tt-local/artifacts. The file-backed local store defaults to
|
|
50
|
+
~/.tuned-tensor-local unless config.storeRoot or TT_LOCAL_HOME is set.`);
|
|
51
|
+
}
|
|
52
|
+
const optionNamesWithValues = new Set([
|
|
53
|
+
"--config",
|
|
54
|
+
"--host",
|
|
55
|
+
"--port",
|
|
56
|
+
"--name",
|
|
57
|
+
"--model",
|
|
58
|
+
"--output",
|
|
59
|
+
"--id",
|
|
60
|
+
"--user-id",
|
|
61
|
+
"--run-number",
|
|
62
|
+
]);
|
|
63
|
+
function readPositionals(argv, startIndex = 3) {
|
|
64
|
+
const positionals = [];
|
|
65
|
+
for (let index = startIndex; index < argv.length; index += 1) {
|
|
66
|
+
const arg = argv[index];
|
|
67
|
+
if (!arg)
|
|
68
|
+
continue;
|
|
69
|
+
if (optionNamesWithValues.has(arg)) {
|
|
70
|
+
index += 1;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (arg.startsWith("--"))
|
|
74
|
+
continue;
|
|
75
|
+
positionals.push(arg);
|
|
76
|
+
}
|
|
77
|
+
return positionals;
|
|
78
|
+
}
|
|
79
|
+
function readNumberOption(argv, name) {
|
|
80
|
+
const value = readOption(argv, name);
|
|
81
|
+
return value ? Number(value) : undefined;
|
|
82
|
+
}
|
|
83
|
+
async function configFromArgv(argv) {
|
|
84
|
+
const configPath = readOption(argv, "--config");
|
|
85
|
+
return loadLocalRunnerConfig(configPath ? resolve(configPath) : undefined);
|
|
86
|
+
}
|
|
87
|
+
function printJson(value) {
|
|
88
|
+
console.log(JSON.stringify(value, null, 2));
|
|
89
|
+
}
|
|
90
|
+
async function sleep(ms) {
|
|
91
|
+
await new Promise((resolveSleep) => setTimeout(resolveSleep, ms));
|
|
92
|
+
}
|
|
93
|
+
function isTerminalStatus(status) {
|
|
94
|
+
return status === "completed" || status === "failed" || status === "cancelled";
|
|
95
|
+
}
|
|
96
|
+
async function main(argv) {
|
|
97
|
+
const command = argv[2] ?? "info";
|
|
98
|
+
if (command === "info" || command === "--help" || command === "-h") {
|
|
99
|
+
const info = getLocalRunnerInfo();
|
|
100
|
+
console.log(`${info.name}: ${info.description}`);
|
|
101
|
+
console.log(`Status: ${info.status}`);
|
|
102
|
+
if (command !== "info")
|
|
103
|
+
printHelp();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (command === "doctor") {
|
|
107
|
+
const config = await configFromArgv(argv);
|
|
108
|
+
const checks = await runDoctor(config);
|
|
109
|
+
const ok = checks.every((check) => check.ok);
|
|
110
|
+
printJson({ ok, checks });
|
|
111
|
+
if (!ok)
|
|
112
|
+
process.exitCode = 1;
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (command === "init") {
|
|
116
|
+
const outputPath = resolve(readOption(argv, "--output") ?? DEFAULT_LOCAL_SPEC_PATH);
|
|
117
|
+
const spec = await initLocalSpecFile({
|
|
118
|
+
outputPath,
|
|
119
|
+
name: readOption(argv, "--name") ?? "Local Tuned Tensor Spec",
|
|
120
|
+
baseModel: readOption(argv, "--model") ?? "Qwen/Qwen3.5-2B",
|
|
121
|
+
force: hasFlag(argv, "--force"),
|
|
122
|
+
});
|
|
123
|
+
printJson({
|
|
124
|
+
ok: true,
|
|
125
|
+
path: outputPath,
|
|
126
|
+
id: spec.id,
|
|
127
|
+
name: spec.name,
|
|
128
|
+
base_model: spec.base_model,
|
|
129
|
+
});
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (command === "validate") {
|
|
133
|
+
const inputPath = resolve(readPositionals(argv)[0] ?? DEFAULT_LOCAL_SPEC_PATH);
|
|
134
|
+
const input = await loadLocalRunInput(inputPath, {
|
|
135
|
+
userId: readOption(argv, "--user-id"),
|
|
136
|
+
runNumber: readNumberOption(argv, "--run-number"),
|
|
137
|
+
});
|
|
138
|
+
const request = input.request;
|
|
139
|
+
const config = await configFromArgv(argv);
|
|
140
|
+
printJson({
|
|
141
|
+
ok: true,
|
|
142
|
+
input_kind: input.kind,
|
|
143
|
+
input_path: input.path,
|
|
144
|
+
run_id: request.run_id,
|
|
145
|
+
behavior_spec_id: request.behavior_spec_id,
|
|
146
|
+
base_model: request.spec_snapshot.base_model,
|
|
147
|
+
artifact_root: config.artifactRoot,
|
|
148
|
+
store_root: config.storeRoot,
|
|
149
|
+
dry_run: config.dryRun,
|
|
150
|
+
});
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (command === "run") {
|
|
154
|
+
const inputPath = resolve(readPositionals(argv)[0] ?? DEFAULT_LOCAL_SPEC_PATH);
|
|
155
|
+
const configInput = await configFromArgv(argv);
|
|
156
|
+
const config = localRunnerConfigSchema.parse({
|
|
157
|
+
...configInput,
|
|
158
|
+
dryRun: hasFlag(argv, "--dry-run") ? true : configInput.dryRun,
|
|
159
|
+
});
|
|
160
|
+
const input = await loadLocalRunInput(inputPath, {
|
|
161
|
+
userId: readOption(argv, "--user-id"),
|
|
162
|
+
runNumber: readNumberOption(argv, "--run-number"),
|
|
163
|
+
});
|
|
164
|
+
const request = input.request;
|
|
165
|
+
const result = await runLocalFineTune({ request, config });
|
|
166
|
+
printJson({
|
|
167
|
+
status: result.report.status,
|
|
168
|
+
input_kind: input.kind,
|
|
169
|
+
run_id: result.report.run_id,
|
|
170
|
+
behavior_spec_id: result.report.behavior_spec_id,
|
|
171
|
+
report_path: result.reportPath,
|
|
172
|
+
artifact_dir: result.artifactDir,
|
|
173
|
+
avg_score_delta: result.report.comparison.avg_score_delta,
|
|
174
|
+
});
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (command === "serve") {
|
|
178
|
+
const config = await configFromArgv(argv);
|
|
179
|
+
const host = readOption(argv, "--host") ?? "127.0.0.1";
|
|
180
|
+
const port = Number(readOption(argv, "--port") ?? "8787");
|
|
181
|
+
const dashboard = await serveLocalDashboard({ host, port, config });
|
|
182
|
+
console.log(`Tuned Tensor Local dashboard: ${dashboard.url}`);
|
|
183
|
+
await new Promise((resolveStop) => {
|
|
184
|
+
const stop = () => {
|
|
185
|
+
dashboard.close().then(resolveStop, resolveStop);
|
|
186
|
+
};
|
|
187
|
+
process.once("SIGINT", stop);
|
|
188
|
+
process.once("SIGTERM", stop);
|
|
189
|
+
});
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (command === "runs") {
|
|
193
|
+
const subcommand = argv[3] ?? "list";
|
|
194
|
+
const config = await configFromArgv(argv);
|
|
195
|
+
const store = createLocalStore(config.storeRoot);
|
|
196
|
+
if (subcommand === "list")
|
|
197
|
+
return printJson(await store.listRuns());
|
|
198
|
+
if (subcommand === "get") {
|
|
199
|
+
const id = argv[4];
|
|
200
|
+
if (!id)
|
|
201
|
+
throw new Error("runs get requires <run-id>");
|
|
202
|
+
return printJson(await store.getRun(id));
|
|
203
|
+
}
|
|
204
|
+
if (subcommand === "events") {
|
|
205
|
+
const id = argv[4];
|
|
206
|
+
if (!id)
|
|
207
|
+
throw new Error("runs events requires <run-id>");
|
|
208
|
+
return printJson(await store.getRunEvents(id));
|
|
209
|
+
}
|
|
210
|
+
if (subcommand === "report") {
|
|
211
|
+
const id = argv[4];
|
|
212
|
+
if (!id)
|
|
213
|
+
throw new Error("runs report requires <run-id>");
|
|
214
|
+
return printJson(await store.getRunReport(id));
|
|
215
|
+
}
|
|
216
|
+
if (subcommand === "cancel") {
|
|
217
|
+
const id = argv[4];
|
|
218
|
+
if (!id)
|
|
219
|
+
throw new Error("runs cancel requires <run-id>");
|
|
220
|
+
await store.cancelRun(id);
|
|
221
|
+
return printJson({ ok: true, run_id: id });
|
|
222
|
+
}
|
|
223
|
+
if (subcommand === "watch") {
|
|
224
|
+
const id = argv[4];
|
|
225
|
+
if (!id)
|
|
226
|
+
throw new Error("runs watch requires <run-id>");
|
|
227
|
+
const printed = new Set();
|
|
228
|
+
while (true) {
|
|
229
|
+
const events = await store.getRunEvents(id);
|
|
230
|
+
for (const event of events) {
|
|
231
|
+
if (printed.has(event.id))
|
|
232
|
+
continue;
|
|
233
|
+
printed.add(event.id);
|
|
234
|
+
console.log(`${event.occurred_at} ${event.stage} ${event.status}: ${event.message}`);
|
|
235
|
+
}
|
|
236
|
+
const run = await store.getRun(id);
|
|
237
|
+
if (isTerminalStatus(run.status))
|
|
238
|
+
return;
|
|
239
|
+
await sleep(1000);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (subcommand === "reconcile") {
|
|
243
|
+
await store.rebuildIndexes();
|
|
244
|
+
return printJson({ ok: true });
|
|
245
|
+
}
|
|
246
|
+
throw new Error(`Unknown runs command: ${subcommand}`);
|
|
247
|
+
}
|
|
248
|
+
if (command === "models") {
|
|
249
|
+
const subcommand = argv[3] ?? "list";
|
|
250
|
+
const config = await configFromArgv(argv);
|
|
251
|
+
const store = createLocalStore(config.storeRoot);
|
|
252
|
+
if (subcommand === "list")
|
|
253
|
+
return printJson(await store.listModels());
|
|
254
|
+
if (subcommand === "get") {
|
|
255
|
+
const id = argv[4];
|
|
256
|
+
if (!id)
|
|
257
|
+
throw new Error("models get requires <model-id>");
|
|
258
|
+
return printJson(await store.getModel(id));
|
|
259
|
+
}
|
|
260
|
+
throw new Error(`Unknown models command: ${subcommand}`);
|
|
261
|
+
}
|
|
262
|
+
if (command === "specs") {
|
|
263
|
+
const subcommand = argv[3] ?? "list";
|
|
264
|
+
const config = await configFromArgv(argv);
|
|
265
|
+
const store = createLocalStore(config.storeRoot);
|
|
266
|
+
if (subcommand === "list")
|
|
267
|
+
return printJson(await store.listSpecs());
|
|
268
|
+
if (subcommand === "get") {
|
|
269
|
+
const id = argv[4];
|
|
270
|
+
if (!id)
|
|
271
|
+
throw new Error("specs get requires <spec-id>");
|
|
272
|
+
return printJson(await store.getSpec(id));
|
|
273
|
+
}
|
|
274
|
+
if (subcommand === "import") {
|
|
275
|
+
const path = argv[4];
|
|
276
|
+
if (!path)
|
|
277
|
+
throw new Error("specs import requires <spec-or-request.json>");
|
|
278
|
+
const input = JSON.parse(await readFile(resolve(path), "utf8"));
|
|
279
|
+
const request = fineTuneRunRequestSchema.safeParse(input);
|
|
280
|
+
if (request.success)
|
|
281
|
+
return printJson(await store.importSpec(request.data.behavior_spec_id, request.data.spec_snapshot));
|
|
282
|
+
const localSpec = localBehaviorSpecFileSchema.safeParse(input);
|
|
283
|
+
if (localSpec.success) {
|
|
284
|
+
const localRequest = runRequestFromLocalSpec(localSpec.data, {
|
|
285
|
+
userId: readOption(argv, "--user-id"),
|
|
286
|
+
runNumber: readNumberOption(argv, "--run-number"),
|
|
287
|
+
});
|
|
288
|
+
return printJson(await store.importSpec(localRequest.behavior_spec_id, localRequest.spec_snapshot));
|
|
289
|
+
}
|
|
290
|
+
const id = readOption(argv, "--id");
|
|
291
|
+
if (!id)
|
|
292
|
+
throw new Error("specs import requires --id when importing a raw spec snapshot");
|
|
293
|
+
return printJson(await store.importSpec(id, specSnapshotSchema.parse(input)));
|
|
294
|
+
}
|
|
295
|
+
throw new Error(`Unknown specs command: ${subcommand}`);
|
|
296
|
+
}
|
|
297
|
+
if (command === "store") {
|
|
298
|
+
const subcommand = argv[3];
|
|
299
|
+
const config = await configFromArgv(argv);
|
|
300
|
+
const store = createLocalStore(config.storeRoot);
|
|
301
|
+
if (subcommand === "rebuild-index") {
|
|
302
|
+
await store.rebuildIndexes();
|
|
303
|
+
return printJson({ ok: true });
|
|
304
|
+
}
|
|
305
|
+
throw new Error(`Unknown store command: ${subcommand ?? ""}`);
|
|
306
|
+
}
|
|
307
|
+
console.error(`Unknown command: ${command}`);
|
|
308
|
+
process.exitCode = 1;
|
|
309
|
+
}
|
|
310
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
311
|
+
main(process.argv).catch((error) => {
|
|
312
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
313
|
+
process.exitCode = 1;
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
//# sourceMappingURL=index.js.map
|