trainfabric 0.1.11 → 0.1.13
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 +2 -2
- package/dist/index.cjs +60 -26
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,8 +28,8 @@ Secrets are stored in macOS Keychain when available. Other platforms use an encr
|
|
|
28
28
|
trainfabric projects:list
|
|
29
29
|
trainfabric datasets:upload ./train.jsonl --project <projectId>
|
|
30
30
|
trainfabric runs:quote --summary --project <projectId> --dataset <datasetId> --model llama-3-8b
|
|
31
|
-
trainfabric runs:create --yes --project <projectId> --dataset <datasetId> --model llama-3-8b
|
|
32
|
-
trainfabric runs:watch <runId>
|
|
31
|
+
trainfabric runs:create --yes --quote <quoteId> --project <projectId> --dataset <datasetId> --model llama-3-8b
|
|
32
|
+
trainfabric runs:watch <runId> --timeout 30m
|
|
33
33
|
trainfabric runs:cost-breakdown <runId> --summary
|
|
34
34
|
```
|
|
35
35
|
|
package/dist/index.cjs
CHANGED
|
@@ -7975,12 +7975,6 @@ function validateChatJsonl(text) {
|
|
|
7975
7975
|
}
|
|
7976
7976
|
tokenEstimate += estimateTokens(result.data.messages.map((message) => `${message.role}:${message.content}`).join("\n"));
|
|
7977
7977
|
}
|
|
7978
|
-
if (tokenEstimate < 100) {
|
|
7979
|
-
warnings.push({
|
|
7980
|
-
code: "small_dataset",
|
|
7981
|
-
message: "Dataset is valid but very small for a meaningful training run."
|
|
7982
|
-
});
|
|
7983
|
-
}
|
|
7984
7978
|
const invalidWarnings = /* @__PURE__ */ new Set([
|
|
7985
7979
|
"empty_file",
|
|
7986
7980
|
"invalid_jsonl",
|
|
@@ -7988,8 +7982,15 @@ function validateChatJsonl(text) {
|
|
|
7988
7982
|
"missing_assistant",
|
|
7989
7983
|
"empty_assistant"
|
|
7990
7984
|
]);
|
|
7985
|
+
const isValid2 = !warnings.some((warning) => invalidWarnings.has(warning.code));
|
|
7986
|
+
if (tokenEstimate < 100) {
|
|
7987
|
+
warnings.push({
|
|
7988
|
+
code: "small_dataset",
|
|
7989
|
+
message: isValid2 ? "Dataset is valid but very small for a meaningful training run." : "Dataset is very small; fix schema errors before using it for a training run."
|
|
7990
|
+
});
|
|
7991
|
+
}
|
|
7991
7992
|
return {
|
|
7992
|
-
valid:
|
|
7993
|
+
valid: isValid2,
|
|
7993
7994
|
format: "chat_jsonl",
|
|
7994
7995
|
rowCount,
|
|
7995
7996
|
duplicateCount,
|
|
@@ -8388,8 +8389,11 @@ var RunHandle = class {
|
|
|
8388
8389
|
this.listeners.get(event)?.delete(listener);
|
|
8389
8390
|
};
|
|
8390
8391
|
}
|
|
8391
|
-
async wait() {
|
|
8392
|
+
async wait(options) {
|
|
8392
8393
|
this.ensureStreaming();
|
|
8394
|
+
const startedAt = Date.now();
|
|
8395
|
+
const pollMs = options?.pollMs ?? 1e3;
|
|
8396
|
+
const timeoutMs = options?.timeoutMs;
|
|
8393
8397
|
for (; ; ) {
|
|
8394
8398
|
const detail = await this.client.runs.get(this.snapshot.id);
|
|
8395
8399
|
this.snapshot = detail.run;
|
|
@@ -8406,7 +8410,11 @@ var RunHandle = class {
|
|
|
8406
8410
|
}
|
|
8407
8411
|
return detail;
|
|
8408
8412
|
}
|
|
8409
|
-
|
|
8413
|
+
if (timeoutMs !== void 0 && Date.now() - startedAt > timeoutMs) {
|
|
8414
|
+
this.close();
|
|
8415
|
+
throw new Error(`Timed out waiting for run ${this.snapshot.id}.`);
|
|
8416
|
+
}
|
|
8417
|
+
await new Promise((resolve) => setTimeout(resolve, pollMs));
|
|
8410
8418
|
}
|
|
8411
8419
|
}
|
|
8412
8420
|
close() {
|
|
@@ -8624,9 +8632,9 @@ var RunsClient = class extends ResourceClient {
|
|
|
8624
8632
|
explanation(runId) {
|
|
8625
8633
|
return this.requestGet(`/v1/runs/${runId}/explanation`);
|
|
8626
8634
|
}
|
|
8627
|
-
async wait(runId) {
|
|
8635
|
+
async wait(runId, options) {
|
|
8628
8636
|
const handle = await this.watch(runId);
|
|
8629
|
-
return handle.wait();
|
|
8637
|
+
return handle.wait(options);
|
|
8630
8638
|
}
|
|
8631
8639
|
};
|
|
8632
8640
|
|
|
@@ -8914,7 +8922,7 @@ function buildComputeSpec(options) {
|
|
|
8914
8922
|
|
|
8915
8923
|
// src/index.ts
|
|
8916
8924
|
var DEFAULT_TRAINFABRIC_API_URL2 = "https://api.trainfabric.com";
|
|
8917
|
-
var CLI_VERSION = "0.1.
|
|
8925
|
+
var CLI_VERSION = "0.1.13";
|
|
8918
8926
|
var CONFIG_DIR = import_node_path3.default.join(import_node_os2.default.homedir(), ".trainfabric");
|
|
8919
8927
|
var CONFIG_PATH = import_node_path3.default.join(CONFIG_DIR, "config.json");
|
|
8920
8928
|
var FALLBACK_SECRET_PATH = import_node_path3.default.join(CONFIG_DIR, "session.enc");
|
|
@@ -9208,6 +9216,7 @@ function printQuoteSummary(bundle) {
|
|
|
9208
9216
|
console.log(` data path: ${quote.dataPath ?? "auto"}`);
|
|
9209
9217
|
console.log(` GPUs: ${gpuCount}`);
|
|
9210
9218
|
console.log(` pool: ${quote.selectedPoolId}`);
|
|
9219
|
+
console.log(` launch with: --quote ${quote.id}`);
|
|
9211
9220
|
}
|
|
9212
9221
|
console.log("");
|
|
9213
9222
|
console.log("Launch only after the customer accepts the quote. Streaming/data-plane cost is included in the estimate when the selected data path is stream, and realized usage may differ.");
|
|
@@ -9260,6 +9269,7 @@ function buildRunInput(options) {
|
|
|
9260
9269
|
baseModel: normalizeBaseModel(options.model),
|
|
9261
9270
|
datasetId: options.dataset,
|
|
9262
9271
|
evalDatasetId: options.eval,
|
|
9272
|
+
pricingQuoteId: options.quote,
|
|
9263
9273
|
...sourceOptions,
|
|
9264
9274
|
mode: options.mode,
|
|
9265
9275
|
compute: buildComputeSpec(options),
|
|
@@ -9290,7 +9300,7 @@ function assertQuoteOptions(bundle, options) {
|
|
|
9290
9300
|
)}). Omit --accelerator or increase --min-memory.`
|
|
9291
9301
|
);
|
|
9292
9302
|
}
|
|
9293
|
-
async function watchRun(runId, json = false) {
|
|
9303
|
+
async function watchRun(runId, json = false, timeoutMs) {
|
|
9294
9304
|
const handle = await createClient(loadConfig()).runs.watch(runId);
|
|
9295
9305
|
if (json) {
|
|
9296
9306
|
const emit = (type, payload) => printJson({ type, payload });
|
|
@@ -9327,9 +9337,26 @@ async function watchRun(runId, json = false) {
|
|
|
9327
9337
|
console.log(`[failed] ${payload.reason ?? "Run failed."}`);
|
|
9328
9338
|
});
|
|
9329
9339
|
}
|
|
9330
|
-
const detail = await handle.wait();
|
|
9340
|
+
const detail = await handle.wait({ timeoutMs });
|
|
9331
9341
|
printJson(detail);
|
|
9332
9342
|
}
|
|
9343
|
+
function parseDurationMs(value) {
|
|
9344
|
+
if (!value) {
|
|
9345
|
+
return void 0;
|
|
9346
|
+
}
|
|
9347
|
+
const trimmed = value.trim().toLowerCase();
|
|
9348
|
+
const match = trimmed.match(/^(\d+(?:\.\d+)?)(ms|s|m|h)?$/);
|
|
9349
|
+
if (!match) {
|
|
9350
|
+
throw new Error(`Invalid duration "${value}". Use values like 30s, 10m, 1h, or 5000ms.`);
|
|
9351
|
+
}
|
|
9352
|
+
const amount = Number(match[1]);
|
|
9353
|
+
const unit = match[2] ?? "ms";
|
|
9354
|
+
const multiplier = unit === "h" ? 36e5 : unit === "m" ? 6e4 : unit === "s" ? 1e3 : 1;
|
|
9355
|
+
return Math.max(1, Math.round(amount * multiplier));
|
|
9356
|
+
}
|
|
9357
|
+
function parsePositiveMs(value, fallback) {
|
|
9358
|
+
return parseDurationMs(value) ?? fallback;
|
|
9359
|
+
}
|
|
9333
9360
|
function createClient(config) {
|
|
9334
9361
|
return new TrainingClient({
|
|
9335
9362
|
accessToken: config.accessToken,
|
|
@@ -9429,12 +9456,14 @@ program2.command("projects:create").requiredOption("--name <name>").option("--or
|
|
|
9429
9456
|
printJson(await createClient(loadConfig()).projects.create({ name: options.name, organizationId: options.org }));
|
|
9430
9457
|
});
|
|
9431
9458
|
program2.command("datasets:validate").argument("<file>").description("Validate a local dataset file").action(async (file) => {
|
|
9432
|
-
|
|
9433
|
-
|
|
9434
|
-
|
|
9435
|
-
|
|
9436
|
-
|
|
9437
|
-
)
|
|
9459
|
+
const validation = await createClient(loadConfig()).datasets.validate({
|
|
9460
|
+
path: file,
|
|
9461
|
+
format: "chat_jsonl"
|
|
9462
|
+
});
|
|
9463
|
+
printJson(validation);
|
|
9464
|
+
if (!validation.valid) {
|
|
9465
|
+
process.exitCode = 1;
|
|
9466
|
+
}
|
|
9438
9467
|
});
|
|
9439
9468
|
program2.command("datasets:list").option("--project <projectId>").description("List datasets").action(async (options) => {
|
|
9440
9469
|
printJson(await createClient(loadConfig()).datasets.list(options.project));
|
|
@@ -9452,7 +9481,7 @@ program2.command("datasets:upload").argument("<file>").option("--project <projec
|
|
|
9452
9481
|
});
|
|
9453
9482
|
printJson(dataset);
|
|
9454
9483
|
});
|
|
9455
|
-
program2.command("runs:create").requiredOption("--project <projectId>").requiredOption("--dataset <datasetId>").requiredOption("--model <baseModel>").option("--eval <evalDatasetId>").option("--epochs <epochs>", "number of epochs", "3").option("--lr <lr>", "learning rate", "0.0002").option("--gpus <gpuCount>", "gpu count", "1").option("--nodes <nodeCount>", "node count", "1").option("--accelerator <acceleratorClass>", "optional hard accelerator constraint (for example: a10g, a100, h100)").option("--min-memory <gigabytes>", "minimum GPU memory in GB").option("--precision <precision>", "fp16_bf16, fp8, or fp32").option("--interconnect <interconnect>", "pcie or nvlink").option("--mode <mode>", "efficient, balanced, or power", "balanced").option("--repo <path>", "local repo path for runtime autodetect").option("--git <url>", "git repo URL for runtime metadata").option("--branch <branch>", "git branch for runtime metadata").option("--yes", "confirm that you reviewed pricing with runs:quote --summary and accept fluctuating realized usage").description("Create a training run").action(async (options) => {
|
|
9484
|
+
program2.command("runs:create").requiredOption("--project <projectId>").requiredOption("--dataset <datasetId>").requiredOption("--model <baseModel>").option("--eval <evalDatasetId>").option("--epochs <epochs>", "number of epochs", "3").option("--lr <lr>", "learning rate", "0.0002").option("--gpus <gpuCount>", "gpu count", "1").option("--nodes <nodeCount>", "node count", "1").option("--accelerator <acceleratorClass>", "optional hard accelerator constraint (for example: a10g, a100, h100)").option("--min-memory <gigabytes>", "minimum GPU memory in GB").option("--precision <precision>", "fp16_bf16, fp8, or fp32").option("--interconnect <interconnect>", "pcie or nvlink").option("--mode <mode>", "efficient, balanced, or power", "balanced").option("--quote <pricingQuoteId>", "launch the exact accepted quote id from runs:quote").option("--repo <path>", "local repo path for runtime autodetect").option("--git <url>", "git repo URL for runtime metadata").option("--branch <branch>", "git branch for runtime metadata").option("--yes", "confirm that you reviewed pricing with runs:quote --summary and accept fluctuating realized usage").description("Create a training run").action(async (options) => {
|
|
9456
9485
|
if (!options.yes) {
|
|
9457
9486
|
throw new Error("Refusing to launch without explicit cost acceptance. Run `trainfabric runs:quote --summary ...` first, then rerun `runs:create` with --yes.");
|
|
9458
9487
|
}
|
|
@@ -9498,11 +9527,16 @@ program2.command("runs:status").argument("<runId>").description("Fetch full run
|
|
|
9498
9527
|
program2.command("runs:logs").argument("<runId>").description("Fetch run logs").action(async (runId) => {
|
|
9499
9528
|
printJson(await createClient(loadConfig()).runs.logs(String(runId)));
|
|
9500
9529
|
});
|
|
9501
|
-
program2.command("runs:wait").argument("<runId>").description("Wait for a run to reach a terminal state").action(async (runId) => {
|
|
9502
|
-
printJson(
|
|
9530
|
+
program2.command("runs:wait").argument("<runId>").option("--timeout <duration>", "maximum wait duration, for example 30s, 10m, or 1h").option("--poll <duration>", "poll interval, for example 1s or 500ms").description("Wait for a run to reach a terminal state").action(async (runId, options) => {
|
|
9531
|
+
printJson(
|
|
9532
|
+
await createClient(loadConfig()).runs.wait(String(runId), {
|
|
9533
|
+
timeoutMs: parseDurationMs(options.timeout),
|
|
9534
|
+
pollMs: parsePositiveMs(options.poll, 1e3)
|
|
9535
|
+
})
|
|
9536
|
+
);
|
|
9503
9537
|
});
|
|
9504
|
-
program2.command("runs:watch").argument("<runId>").option("--json", "emit event output as JSON").description("Stream a run until it completes").action(async (runId, options) => {
|
|
9505
|
-
await watchRun(String(runId), Boolean(options.json));
|
|
9538
|
+
program2.command("runs:watch").argument("<runId>").option("--json", "emit event output as JSON").option("--timeout <duration>", "maximum watch duration, for example 30s, 10m, or 1h").description("Stream a run until it completes").action(async (runId, options) => {
|
|
9539
|
+
await watchRun(String(runId), Boolean(options.json), parseDurationMs(options.timeout));
|
|
9506
9540
|
});
|
|
9507
9541
|
program2.command("runs:usage").argument("<runId>").option("--summary", "print a human-readable cost summary instead of raw JSON").description("Fetch run usage summary").action(async (runId, options) => {
|
|
9508
9542
|
const usage = await createClient(loadConfig()).runs.usage(String(runId));
|
|
@@ -9553,7 +9587,7 @@ program2.command("models:export").argument("<modelId>").description("Fetch the e
|
|
|
9553
9587
|
program2.command("deployments:list").description("List deployments").action(async () => {
|
|
9554
9588
|
printJson(await createClient(loadConfig()).deployments.list());
|
|
9555
9589
|
});
|
|
9556
|
-
program2.command("deployments:create").requiredOption("--model <modelId>").description("Create a deployment for a model").action(async (options) => {
|
|
9590
|
+
program2.command("deployments:create").requiredOption("--model <modelId>").option("--project <projectId>", "accepted for consistency; deployment project is inferred from the model").description("Create a deployment for a model").action(async (options) => {
|
|
9557
9591
|
printJson(await createClient(loadConfig()).deployments.create({ modelId: options.model }));
|
|
9558
9592
|
});
|
|
9559
9593
|
program2.command("deployments:get").argument("<deploymentId>").description("Fetch a deployment").action(async (deploymentId) => {
|