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