@tuned-tensor/cli 0.15.1 → 0.17.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/README.md +124 -46
- package/dist/{chunk-VVKC563R.js → chunk-KRKISPHG.js} +420 -98
- package/dist/chunk-KRKISPHG.js.map +1 -0
- package/dist/index.js +1070 -706
- package/dist/index.js.map +1 -1
- package/dist/local-runtime.js +147 -32
- package/dist/local-runtime.js.map +1 -1
- package/docs/local-runtime/architecture.md +17 -6
- package/docs/local-runtime/aws-gpu.md +248 -0
- package/docs/local-runtime/foundation-long-runs.md +21 -5
- package/docs/local-runtime/serving.md +11 -1
- package/docs/spec-architecture.html +573 -0
- package/docs/spec-workflow.md +110 -0
- package/examples/local-runtime/aws-runner.json +13 -0
- package/package.json +6 -4
- package/training/adapter/src/foundation_export.py +81 -0
- package/training/adapter/src/serve.py +12 -5
- package/training/adapter/src/train.py +4 -0
- package/training/foundation/pyproject.toml +4 -0
- package/training/foundation/src/model.py +2 -0
- package/training/foundation/uv.lock +225 -0
- package/dist/chunk-VVKC563R.js.map +0 -1
package/dist/local-runtime.js
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
buildModelPrefetchPayload,
|
|
15
15
|
buildSystemMessage,
|
|
16
16
|
canonicalLocalPipeline,
|
|
17
|
+
checkAwsGpu,
|
|
17
18
|
comparisonReportSchema,
|
|
18
19
|
compileSpecToJsonl,
|
|
19
20
|
createLocalStore,
|
|
@@ -64,14 +65,17 @@ import {
|
|
|
64
65
|
resolveModelLoader,
|
|
65
66
|
resolveRequestedBaseModelRevision,
|
|
66
67
|
resolveTrainingModel,
|
|
68
|
+
runGpuProcess,
|
|
67
69
|
runLocalFineTune,
|
|
68
70
|
runLocalPipeline,
|
|
69
71
|
runReportSchema,
|
|
70
72
|
runRequestFromLocalSpec,
|
|
71
73
|
sanitizeLogLine,
|
|
74
|
+
specHash,
|
|
72
75
|
specSnapshotSchema,
|
|
73
76
|
tokenF1,
|
|
74
77
|
trainingReportSchema,
|
|
78
|
+
validateBehaviorSpec,
|
|
75
79
|
validateLocalFineTuneInput,
|
|
76
80
|
validateLocalPipeline,
|
|
77
81
|
verifyLocalBaseModel,
|
|
@@ -81,13 +85,13 @@ import {
|
|
|
81
85
|
withHuggingFaceCacheEnvironment,
|
|
82
86
|
withOfflineHuggingFaceCacheEnvironment,
|
|
83
87
|
writePrivateJsonAtomic
|
|
84
|
-
} from "./chunk-
|
|
88
|
+
} from "./chunk-KRKISPHG.js";
|
|
85
89
|
|
|
86
90
|
// src/local-runtime/index.ts
|
|
87
|
-
import { readFileSync as
|
|
91
|
+
import { readFileSync as readFileSync3, realpathSync } from "fs";
|
|
88
92
|
import { createHash as createHash2 } from "crypto";
|
|
89
93
|
import { readFile as readFile2, stat } from "fs/promises";
|
|
90
|
-
import { basename, dirname, join as
|
|
94
|
+
import { basename, dirname, join as join5, resolve as resolve3 } from "path";
|
|
91
95
|
import { cwd } from "process";
|
|
92
96
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
93
97
|
|
|
@@ -290,6 +294,22 @@ function foundationPythonProbePlan(requireBf16) {
|
|
|
290
294
|
env: withFoundationPythonEnvironment(process.env)
|
|
291
295
|
};
|
|
292
296
|
}
|
|
297
|
+
function localPreparationProbePlan(foundation) {
|
|
298
|
+
const source = [
|
|
299
|
+
"import json",
|
|
300
|
+
foundation ? "import numpy, safetensors, tokenizers, torch" : "import torch, transformers, peft, huggingface_hub",
|
|
301
|
+
"assert torch.ones(1, device='cpu').item() == 1",
|
|
302
|
+
"print(json.dumps({'python_ok': True, 'device': 'cpu', 'purpose': 'local preparation and CPU work'}))"
|
|
303
|
+
].join("; ");
|
|
304
|
+
const entrypoint = foundation ? buildFoundationPythonCommand("-c", [source]) : buildBundledPythonCommand("-c", [source]);
|
|
305
|
+
const env = minimalMachineLearningEnvironment(process.env);
|
|
306
|
+
return {
|
|
307
|
+
name: "python-runtime",
|
|
308
|
+
command: entrypoint.command,
|
|
309
|
+
args: entrypoint.commandArgs,
|
|
310
|
+
env: foundation ? withFoundationPythonEnvironment(env) : withBundledPythonEnvironment(env)
|
|
311
|
+
};
|
|
312
|
+
}
|
|
293
313
|
function pythonProbeSource(device) {
|
|
294
314
|
return [
|
|
295
315
|
"import json",
|
|
@@ -430,7 +450,7 @@ async function runDoctor(config, request, foundationSpec) {
|
|
|
430
450
|
checks.push(placeholderSpecCheck(request));
|
|
431
451
|
resolveTrainingModel(request.spec_snapshot.base_model);
|
|
432
452
|
}
|
|
433
|
-
const pythonPlans =
|
|
453
|
+
const pythonPlans = config.dryRun ? [] : config.gpu ? [localPreparationProbePlan(Boolean(foundationSpec))] : foundationSpec ? [foundationPythonProbePlan(foundationSpec.foundation.bf16 !== false)] : buildDoctorPythonPlans(config);
|
|
434
454
|
if (pythonPlans.length > 0) {
|
|
435
455
|
const uvVersion = await runCommand("uv", ["--version"]);
|
|
436
456
|
checks.push({
|
|
@@ -465,7 +485,28 @@ async function runDoctor(config, request, foundationSpec) {
|
|
|
465
485
|
});
|
|
466
486
|
}
|
|
467
487
|
const device = config.evaluation.inference.device;
|
|
468
|
-
if (!config.dryRun) {
|
|
488
|
+
if (config.gpu && !config.dryRun) {
|
|
489
|
+
try {
|
|
490
|
+
await checkAwsGpu(config.gpu);
|
|
491
|
+
const plans = foundationSpec ? [foundationPythonProbePlan(foundationSpec.foundation.bf16 !== false)] : buildDoctorPythonPlans(config);
|
|
492
|
+
for (const plan of plans) {
|
|
493
|
+
const result = await runGpuProcess({
|
|
494
|
+
gpu: config.gpu,
|
|
495
|
+
runtime: foundationSpec ? "foundation" : "adapter",
|
|
496
|
+
files: [],
|
|
497
|
+
command: plan.command,
|
|
498
|
+
commandArgs: plan.args,
|
|
499
|
+
env: plan.env,
|
|
500
|
+
stage: "gpu_check",
|
|
501
|
+
timeoutMs: 18e5
|
|
502
|
+
});
|
|
503
|
+
if (result.exitCode !== 0) throw new Error(`Remote Python/CUDA check failed: ${result.stderr}`);
|
|
504
|
+
}
|
|
505
|
+
checks.push({ name: "aws-gpu", ok: true, message: `GPU execution on ${config.gpu.instanceId}; orchestration stays local.` });
|
|
506
|
+
} catch (error) {
|
|
507
|
+
checks.push({ name: "aws-gpu", ok: false, message: error instanceof Error ? error.message : String(error) });
|
|
508
|
+
}
|
|
509
|
+
} else if (!config.dryRun) {
|
|
469
510
|
const queried = await runCommand("nvidia-smi", [
|
|
470
511
|
"--query-gpu=name,driver_version",
|
|
471
512
|
"--format=csv,noheader"
|
|
@@ -503,8 +544,9 @@ async function runDoctor(config, request, foundationSpec) {
|
|
|
503
544
|
}
|
|
504
545
|
|
|
505
546
|
// src/local-runtime/model-server.ts
|
|
547
|
+
import { readFileSync as readFileSync2, statSync } from "fs";
|
|
506
548
|
import { spawn as spawn2 } from "child_process";
|
|
507
|
-
import { resolve as resolve2 } from "path";
|
|
549
|
+
import { resolve as resolve2, join as join3 } from "path";
|
|
508
550
|
import { fileURLToPath } from "url";
|
|
509
551
|
|
|
510
552
|
// src/local-runtime/serving-runtime.ts
|
|
@@ -562,7 +604,16 @@ function buildModelServerLaunch(args) {
|
|
|
562
604
|
const options = args.options ?? {};
|
|
563
605
|
const device = options.device ?? args.config.evaluation.inference.device;
|
|
564
606
|
if (device !== "cuda") throw new Error("The pinned vLLM serving runtime requires CUDA; CPU serving is not supported.");
|
|
565
|
-
const contextLength = boundedNumber(
|
|
607
|
+
const contextLength = boundedNumber(
|
|
608
|
+
"contextLength",
|
|
609
|
+
options.contextLength ?? args.foundation?.contextLength ?? 16384,
|
|
610
|
+
args.foundation ? 1 : 512,
|
|
611
|
+
131072,
|
|
612
|
+
true
|
|
613
|
+
);
|
|
614
|
+
if (args.foundation && contextLength > args.foundation.contextLength) {
|
|
615
|
+
throw new Error("Context length exceeds the foundation checkpoint sequence_length.");
|
|
616
|
+
}
|
|
566
617
|
const gpuMemoryUtilization = boundedNumber("gpuMemoryUtilization", options.gpuMemoryUtilization ?? 0.8, 0.05, 0.95);
|
|
567
618
|
const host = options.host ?? "127.0.0.1";
|
|
568
619
|
const remote = !isLoopbackHost(host);
|
|
@@ -575,7 +626,7 @@ function buildModelServerLaunch(args) {
|
|
|
575
626
|
throw new Error("Non-loopback serving requires --api-key-env with a populated environment variable.");
|
|
576
627
|
}
|
|
577
628
|
const port = boundedNumber("port", options.port ?? 8e3, 1, 65535, true);
|
|
578
|
-
const maxTokens = boundedNumber("maxTokens", options.maxTokens ?? 512, 1, 8192, true);
|
|
629
|
+
const maxTokens = boundedNumber("maxTokens", options.maxTokens ?? Math.min(512, contextLength), 1, 8192, true);
|
|
579
630
|
const temperature = boundedNumber("temperature", options.temperature ?? 0, 0, 5);
|
|
580
631
|
const topP = boundedNumber("topP", options.topP ?? 1, 0, 1);
|
|
581
632
|
const maxConcurrentRequests = boundedNumber(
|
|
@@ -587,14 +638,14 @@ function buildModelServerLaunch(args) {
|
|
|
587
638
|
);
|
|
588
639
|
const artifactPath = args.artifactUri ? localArtifactPath(args.artifactUri) : void 0;
|
|
589
640
|
const recordedBaseModelPath = options.baseModelArtifactUri ? localArtifactPath(options.baseModelArtifactUri) : void 0;
|
|
590
|
-
const configuredBaseModelPath = args.config.paths.baseModel ? resolve2(args.config.paths.baseModel) : void 0;
|
|
641
|
+
const configuredBaseModelPath = !args.foundation && args.config.paths.baseModel ? resolve2(args.config.paths.baseModel) : void 0;
|
|
591
642
|
if (recordedBaseModelPath && configuredBaseModelPath && resolve2(recordedBaseModelPath) !== configuredBaseModelPath) {
|
|
592
643
|
throw new Error(
|
|
593
644
|
`Configured paths.baseModel ${configuredBaseModelPath} does not match the base model recorded for this artifact: ` + recordedBaseModelPath
|
|
594
645
|
);
|
|
595
646
|
}
|
|
596
647
|
const localBaseModelPath = recordedBaseModelPath ?? configuredBaseModelPath;
|
|
597
|
-
const requestedBaseModelRevision = resolveRequestedBaseModelRevision(
|
|
648
|
+
const requestedBaseModelRevision = args.foundation ? void 0 : resolveRequestedBaseModelRevision(
|
|
598
649
|
args.baseModel,
|
|
599
650
|
options.baseModelRevision
|
|
600
651
|
);
|
|
@@ -610,7 +661,11 @@ function buildModelServerLaunch(args) {
|
|
|
610
661
|
TT_MODEL_NAME: args.modelName,
|
|
611
662
|
TT_CONTEXT_LENGTH: String(contextLength),
|
|
612
663
|
TT_GPU_MEMORY_UTILIZATION: String(gpuMemoryUtilization),
|
|
613
|
-
TT_MODEL_LOADER: resolveModelLoader(args.baseModel),
|
|
664
|
+
TT_MODEL_LOADER: args.foundation ? "foundation" : resolveModelLoader(args.baseModel),
|
|
665
|
+
...args.foundation ? {
|
|
666
|
+
TT_FOUNDATION_CHECKPOINT: args.foundation.checkpoint,
|
|
667
|
+
TT_FOUNDATION_TOKENIZER: args.foundation.tokenizer
|
|
668
|
+
} : {},
|
|
614
669
|
TT_HOST: host,
|
|
615
670
|
TT_PORT: String(port),
|
|
616
671
|
TT_DEVICE: device,
|
|
@@ -710,16 +765,32 @@ async function serveLocalModel(launch) {
|
|
|
710
765
|
});
|
|
711
766
|
});
|
|
712
767
|
}
|
|
768
|
+
function buildFoundationModelServerLaunch(args) {
|
|
769
|
+
const checkpoint = resolve2(args.checkpoint);
|
|
770
|
+
const tokenizer = resolve2(args.tokenizer);
|
|
771
|
+
for (const path of [join3(checkpoint, "model.safetensors"), tokenizer]) {
|
|
772
|
+
if (!statSync(path).isFile()) throw new Error(`Expected foundation artifact file: ${path}`);
|
|
773
|
+
}
|
|
774
|
+
const metadata = JSON.parse(readFileSync2(join3(checkpoint, "config.json"), "utf8"));
|
|
775
|
+
const contextLength = boundedNumber("sequence_length", metadata.sequence_length, 1, 131072, true);
|
|
776
|
+
return buildModelServerLaunch({
|
|
777
|
+
modelName: "foundation",
|
|
778
|
+
baseModel: "foundation",
|
|
779
|
+
config: args.config,
|
|
780
|
+
options: args.options,
|
|
781
|
+
foundation: { checkpoint, tokenizer, contextLength }
|
|
782
|
+
});
|
|
783
|
+
}
|
|
713
784
|
|
|
714
785
|
// src/local-runtime/active-model.ts
|
|
715
786
|
import { randomUUID } from "crypto";
|
|
716
787
|
import { appendFile, chmod, readFile, rename, writeFile as writeFile2 } from "fs/promises";
|
|
717
|
-
import { join as
|
|
788
|
+
import { join as join4 } from "path";
|
|
718
789
|
function pointerPath(store) {
|
|
719
|
-
return
|
|
790
|
+
return join4(store.root, "active-model.json");
|
|
720
791
|
}
|
|
721
792
|
function historyPath(store) {
|
|
722
|
-
return
|
|
793
|
+
return join4(store.root, "activation-history.jsonl");
|
|
723
794
|
}
|
|
724
795
|
async function readPointer(store) {
|
|
725
796
|
if (!await hardenExistingLocalStore(store.root)) return null;
|
|
@@ -759,7 +830,7 @@ async function assertModelCanActivate(store, modelId) {
|
|
|
759
830
|
throw new Error(`Model ${model.id} belongs to run ${run.id}, which is not completed.`);
|
|
760
831
|
}
|
|
761
832
|
const report = runReportSchema.parse(
|
|
762
|
-
JSON.parse(await readFile(
|
|
833
|
+
JSON.parse(await readFile(join4(model.artifact_dir, "run-report.json"), "utf8"))
|
|
763
834
|
);
|
|
764
835
|
if (!report.general_regression) {
|
|
765
836
|
throw new Error(`Model ${model.id} has no general regression evaluation.`);
|
|
@@ -816,7 +887,7 @@ async function rollbackActiveModel(store) {
|
|
|
816
887
|
function packageVersion() {
|
|
817
888
|
try {
|
|
818
889
|
const packageJson = JSON.parse(
|
|
819
|
-
|
|
890
|
+
readFileSync3(resolve3(localRuntimePackageRoot(import.meta.url), "package.json"), "utf8")
|
|
820
891
|
);
|
|
821
892
|
return typeof packageJson.version === "string" ? packageJson.version : "unknown";
|
|
822
893
|
} catch {
|
|
@@ -851,7 +922,7 @@ Commands:
|
|
|
851
922
|
hardware [--config local-runner.json] [--quick]
|
|
852
923
|
validate [tunedtensor.json] [--config local-runner.json]
|
|
853
924
|
run [tunedtensor.json] [--config local-runner.json] [--dry-run] [--verbose] [--quiet]
|
|
854
|
-
serve <model-id|active|base> [--config local-runner.json] [--host 127.0.0.1] [--port 8000]
|
|
925
|
+
serve <model-id|active|base|foundation> [--config local-runner.json] [--host 127.0.0.1] [--port 8000]
|
|
855
926
|
runs list|get|events|report|compare [args] [--config local-runner.json]
|
|
856
927
|
models list|get|verify|prefetch|verify-base|active|activate|rollback|serve [args] [--config local-runner.json]
|
|
857
928
|
|
|
@@ -869,6 +940,8 @@ var VERBOSE_OPTION = { name: "--verbose", description: "Stream subprocess output
|
|
|
869
940
|
var QUIET_OPTION = { name: "--quiet", description: "Suppress progress output on stderr" };
|
|
870
941
|
var MODEL_SERVE_OPTIONS = [
|
|
871
942
|
CONFIG_OPTION,
|
|
943
|
+
{ name: "--checkpoint", value: "path", description: "Foundation model directory (target: foundation)" },
|
|
944
|
+
{ name: "--tokenizer", value: "path", description: "Foundation tokenizer.json (target: foundation)" },
|
|
872
945
|
{ name: "--host", value: "host", description: "Bind host (localhost by default)" },
|
|
873
946
|
{ name: "--port", value: "port", description: "Bind port" },
|
|
874
947
|
{ name: "--device", value: "device", description: "cuda (pinned vLLM runtime)" },
|
|
@@ -876,7 +949,7 @@ var MODEL_SERVE_OPTIONS = [
|
|
|
876
949
|
{ name: "--temperature", value: "number", description: "Default sampling temperature" },
|
|
877
950
|
{ name: "--top-p", value: "number", description: "Default nucleus sampling threshold" },
|
|
878
951
|
{ name: "--max-concurrent-requests", value: "count", description: "Maximum sequences in an upstream generation batch" },
|
|
879
|
-
{ name: "--context-length", value: "count", description: "Total context token budget (
|
|
952
|
+
{ name: "--context-length", value: "count", description: "Total context token budget (foundation: checkpoint limit; otherwise 16384)" },
|
|
880
953
|
{ name: "--gpu-memory-utilization", value: "fraction", description: "vLLM GPU memory budget fraction (default 0.8)" },
|
|
881
954
|
{ name: "--spec", value: "path", description: "Behavior spec whose instructions are enforced" },
|
|
882
955
|
{ name: "--no-spec-prompt", description: "Do not enforce the stored behavior-spec prompt" },
|
|
@@ -939,8 +1012,8 @@ var COMMAND_DEFINITIONS = {
|
|
|
939
1012
|
maxPositionals: 1
|
|
940
1013
|
},
|
|
941
1014
|
serve: {
|
|
942
|
-
usage: "tt serve <model-id|active|base> [options]",
|
|
943
|
-
description: "Serve
|
|
1015
|
+
usage: "tt serve <model-id|active|base|foundation> [options]",
|
|
1016
|
+
description: "Serve an adapter, pretrained base, or foundation checkpoint with vLLM.",
|
|
944
1017
|
options: MODEL_SERVE_OPTIONS,
|
|
945
1018
|
minPositionals: 1,
|
|
946
1019
|
maxPositionals: 1,
|
|
@@ -1006,7 +1079,7 @@ var COMMAND_GROUPS = {
|
|
|
1006
1079
|
maxPositionals: 0
|
|
1007
1080
|
},
|
|
1008
1081
|
serve: {
|
|
1009
|
-
usage: "tt models serve <model-id|active|base> [options]",
|
|
1082
|
+
usage: "tt models serve <model-id|active|base|foundation> [options]",
|
|
1010
1083
|
description: "Alias for `tt serve`.",
|
|
1011
1084
|
options: MODEL_SERVE_OPTIONS,
|
|
1012
1085
|
minPositionals: 1,
|
|
@@ -1146,7 +1219,7 @@ function readNumberOption(argv, name) {
|
|
|
1146
1219
|
async function selectedConfigPath(argv, adjacentTo) {
|
|
1147
1220
|
const explicitPath = readOption(argv, "--config");
|
|
1148
1221
|
if (explicitPath) return resolve3(explicitPath);
|
|
1149
|
-
const candidate =
|
|
1222
|
+
const candidate = join5(adjacentTo ? dirname(resolve3(adjacentTo)) : cwd(), "local-runner.json");
|
|
1150
1223
|
const metadata = await stat(candidate).catch(() => null);
|
|
1151
1224
|
return metadata?.isFile() ? candidate : void 0;
|
|
1152
1225
|
}
|
|
@@ -1211,7 +1284,7 @@ function createConsoleReporter(options) {
|
|
|
1211
1284
|
};
|
|
1212
1285
|
}
|
|
1213
1286
|
async function verifyStoredModel(model, config) {
|
|
1214
|
-
const manifestPath =
|
|
1287
|
+
const manifestPath = join5(model.artifact_dir, "artifact-manifest.json");
|
|
1215
1288
|
const integrity = await assertArtifactManifest(manifestPath, {
|
|
1216
1289
|
requiredPaths: ["stage-metadata.json", "training-report.json"],
|
|
1217
1290
|
scopeToRequired: true,
|
|
@@ -1246,8 +1319,8 @@ async function verifyStoredModel(model, config) {
|
|
|
1246
1319
|
};
|
|
1247
1320
|
}
|
|
1248
1321
|
async function verifyActivationEvidence(model) {
|
|
1249
|
-
const generalBaseline =
|
|
1250
|
-
const generalCandidate =
|
|
1322
|
+
const generalBaseline = join5(model.artifact_dir, "general-baseline-eval.json");
|
|
1323
|
+
const generalCandidate = join5(model.artifact_dir, "general-candidate-eval.json");
|
|
1251
1324
|
const [hasBaseline, hasCandidate] = await Promise.all([
|
|
1252
1325
|
stat(generalBaseline).then((metadata) => metadata.isFile(), () => false),
|
|
1253
1326
|
stat(generalCandidate).then((metadata) => metadata.isFile(), () => false)
|
|
@@ -1257,7 +1330,7 @@ async function verifyActivationEvidence(model) {
|
|
|
1257
1330
|
"This model cannot be activated because the run did not include a general-regression suite. Add evaluation.generalRegression to local-runner.json with a held-out dataset, re-run training, then activate. Until then, serve the adapter with `tt serve local-<run-id>`."
|
|
1258
1331
|
);
|
|
1259
1332
|
}
|
|
1260
|
-
await assertArtifactManifest(
|
|
1333
|
+
await assertArtifactManifest(join5(model.artifact_dir, "artifact-manifest.json"), {
|
|
1261
1334
|
requiredPaths: [
|
|
1262
1335
|
"run-report.json",
|
|
1263
1336
|
"general-baseline-eval.json",
|
|
@@ -1283,7 +1356,7 @@ async function verifyModelArtifactPath(input, config) {
|
|
|
1283
1356
|
if (!metadata) throw new Error(`Model not found and artifact path does not exist: ${input}`);
|
|
1284
1357
|
let current = metadata.isDirectory() ? inputPath : dirname(inputPath);
|
|
1285
1358
|
for (let depth = 0; depth < 8; depth += 1) {
|
|
1286
|
-
const candidate =
|
|
1359
|
+
const candidate = join5(current, "artifact-manifest.json");
|
|
1287
1360
|
const raw = await readFile2(candidate, "utf8").catch(() => null);
|
|
1288
1361
|
if (raw) {
|
|
1289
1362
|
const parsed = JSON.parse(raw);
|
|
@@ -1337,11 +1410,11 @@ async function modelSystemPrompt(args) {
|
|
|
1337
1410
|
throw new Error(`--spec must contain a tunedtensor.json behavior spec: ${resolve3(specPath)}`);
|
|
1338
1411
|
}
|
|
1339
1412
|
if (isFoundationSpecFile(local.data)) {
|
|
1340
|
-
throw new Error("Foundation specs have no Hugging Face base model. `tt serve
|
|
1413
|
+
throw new Error("Foundation specs have no Hugging Face base model. Use `tt serve foundation --checkpoint <model-dir> --tokenizer <tokenizer.json>`.");
|
|
1341
1414
|
}
|
|
1342
1415
|
spec = local.data;
|
|
1343
1416
|
} else {
|
|
1344
|
-
const runRequestPath =
|
|
1417
|
+
const runRequestPath = join5(args.store.paths.runsDir, args.model.run_id, "request.json");
|
|
1345
1418
|
let persistedRequest = null;
|
|
1346
1419
|
try {
|
|
1347
1420
|
persistedRequest = fineTuneRunRequestSchema.parse(JSON.parse(await readFile2(runRequestPath, "utf8")));
|
|
@@ -1357,7 +1430,7 @@ async function modelSystemPrompt(args) {
|
|
|
1357
1430
|
}
|
|
1358
1431
|
const prompt = buildSystemMessage(spec);
|
|
1359
1432
|
const metadata = JSON.parse(
|
|
1360
|
-
await readFile2(
|
|
1433
|
+
await readFile2(join5(args.model.artifact_dir, "stage-metadata.json"), "utf8")
|
|
1361
1434
|
);
|
|
1362
1435
|
const promptHash = createHash2("sha256").update(prompt).digest("hex");
|
|
1363
1436
|
if (metadata.system_prompt_sha256 !== promptHash) {
|
|
@@ -1400,7 +1473,7 @@ function printServeClientConfig(argv, launch) {
|
|
|
1400
1473
|
supportsUsageInStreaming: true,
|
|
1401
1474
|
maxTokensField: "max_tokens"
|
|
1402
1475
|
},
|
|
1403
|
-
models: [
|
|
1476
|
+
models: [...new Set(launch.env.TT_FOUNDATION_CHECKPOINT ? [launch.modelName] : [launch.modelName, `base:${launch.env.TT_BASE_MODEL}`])].map((id) => ({
|
|
1404
1477
|
id,
|
|
1405
1478
|
name: id,
|
|
1406
1479
|
// This endpoint exports ordinary text/tool calls, not reasoning-effort controls.
|
|
@@ -1488,7 +1561,7 @@ async function serveBaseModelFromCli(args) {
|
|
|
1488
1561
|
JSON.parse(await readFile2(specPath, "utf8"))
|
|
1489
1562
|
) : void 0;
|
|
1490
1563
|
if (spec && isFoundationSpecFile(spec)) {
|
|
1491
|
-
throw new Error("Foundation specs have no Hugging Face base model. `tt serve
|
|
1564
|
+
throw new Error("Foundation specs have no Hugging Face base model. Use `tt serve foundation --checkpoint <model-dir> --tokenizer <tokenizer.json>`.");
|
|
1492
1565
|
}
|
|
1493
1566
|
let baseModel = spec?.base_model;
|
|
1494
1567
|
if (!baseModel) {
|
|
@@ -1525,6 +1598,45 @@ async function serveBaseModelFromCli(args) {
|
|
|
1525
1598
|
await serveLocalModel(launch);
|
|
1526
1599
|
}
|
|
1527
1600
|
async function serveModelTargetFromCli(args) {
|
|
1601
|
+
if (args.target === "foundation") {
|
|
1602
|
+
const checkpoint = readOption(args.argv, "--checkpoint");
|
|
1603
|
+
const tokenizer = readOption(args.argv, "--tokenizer");
|
|
1604
|
+
if (!checkpoint || !tokenizer) {
|
|
1605
|
+
throw new Error("Foundation serving requires --checkpoint <model-dir> and --tokenizer <tokenizer.json>.");
|
|
1606
|
+
}
|
|
1607
|
+
const specPath = readOption(args.argv, "--spec");
|
|
1608
|
+
if (specPath && hasFlag(args.argv, "--no-spec-prompt")) {
|
|
1609
|
+
throw new Error("Use only one of --spec or --no-spec-prompt.");
|
|
1610
|
+
}
|
|
1611
|
+
const spec = specPath ? localBehaviorSpecFileSchema.parse(JSON.parse(await readFile2(resolve3(specPath), "utf8"))) : void 0;
|
|
1612
|
+
if (spec && !isFoundationSpecFile(spec)) throw new Error("Foundation serving requires a foundation spec.");
|
|
1613
|
+
const launch = buildFoundationModelServerLaunch({
|
|
1614
|
+
checkpoint,
|
|
1615
|
+
tokenizer,
|
|
1616
|
+
config: args.config,
|
|
1617
|
+
options: {
|
|
1618
|
+
...serveOptionsFromArgv(args.argv),
|
|
1619
|
+
systemPrompt: spec ? buildSystemMessage(spec) : void 0
|
|
1620
|
+
}
|
|
1621
|
+
});
|
|
1622
|
+
if (printServeClientConfig(args.argv, launch)) return;
|
|
1623
|
+
if (hasFlag(args.argv, "--print-command")) {
|
|
1624
|
+
printJson({
|
|
1625
|
+
ok: true,
|
|
1626
|
+
model_id: launch.modelName,
|
|
1627
|
+
url: launch.url,
|
|
1628
|
+
command: launch.displayCommand,
|
|
1629
|
+
checkpoint: launch.env.TT_FOUNDATION_CHECKPOINT,
|
|
1630
|
+
tokenizer: launch.env.TT_FOUNDATION_TOKENIZER
|
|
1631
|
+
});
|
|
1632
|
+
return;
|
|
1633
|
+
}
|
|
1634
|
+
await serveLocalModel(launch);
|
|
1635
|
+
return;
|
|
1636
|
+
}
|
|
1637
|
+
if (readOption(args.argv, "--checkpoint") || readOption(args.argv, "--tokenizer")) {
|
|
1638
|
+
throw new Error("--checkpoint and --tokenizer require the foundation target.");
|
|
1639
|
+
}
|
|
1528
1640
|
if (args.target === "base") {
|
|
1529
1641
|
return serveBaseModelFromCli(args);
|
|
1530
1642
|
}
|
|
@@ -1903,6 +2015,7 @@ export {
|
|
|
1903
2015
|
baseModelRevisionSchema,
|
|
1904
2016
|
behaviorSpecExampleSchema,
|
|
1905
2017
|
behaviorSpecSchema,
|
|
2018
|
+
buildFoundationModelServerLaunch,
|
|
1906
2019
|
buildLocalBaseModelServerLaunch,
|
|
1907
2020
|
buildLocalModelServerLaunch,
|
|
1908
2021
|
buildModelPrefetchPayload,
|
|
@@ -1961,8 +2074,10 @@ export {
|
|
|
1961
2074
|
runRequestFromLocalSpec,
|
|
1962
2075
|
sanitizeLogLine,
|
|
1963
2076
|
serveLocalModel,
|
|
2077
|
+
specHash,
|
|
1964
2078
|
specSnapshotSchema,
|
|
1965
2079
|
trainingReportSchema,
|
|
2080
|
+
validateBehaviorSpec,
|
|
1966
2081
|
validateLocalFineTuneInput,
|
|
1967
2082
|
validateLocalPipeline,
|
|
1968
2083
|
verifyLocalBaseModel,
|