trainfabric 0.1.30 → 0.1.32
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 +1 -1
- package/dist/index.cjs +93 -33
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ trainfabric projects:create --name <name>
|
|
|
30
30
|
trainfabric projects:delete <projectId>
|
|
31
31
|
trainfabric datasets:upload ./train.jsonl --project <projectId>
|
|
32
32
|
trainfabric runs:quote --summary --project <projectId> --dataset <datasetId> --model llama-3-8b
|
|
33
|
-
trainfabric runs:create --yes --quote <quoteId>
|
|
33
|
+
trainfabric runs:create --yes --quote <quoteId>
|
|
34
34
|
trainfabric runs:watch <runId> --timeout 30m
|
|
35
35
|
trainfabric runs:cost-breakdown <runId> --summary
|
|
36
36
|
```
|
package/dist/index.cjs
CHANGED
|
@@ -8478,15 +8478,6 @@ var RunHandle = class {
|
|
|
8478
8478
|
this.snapshot = detail.run;
|
|
8479
8479
|
if (["completed", "failed", "canceled", "terminated"].includes(detail.run.status)) {
|
|
8480
8480
|
this.close();
|
|
8481
|
-
if (detail.run.status === "failed") {
|
|
8482
|
-
throw new Error(detail.run.failureReason ?? "Run failed.");
|
|
8483
|
-
}
|
|
8484
|
-
if (detail.run.status === "canceled") {
|
|
8485
|
-
throw new Error("Run was canceled.");
|
|
8486
|
-
}
|
|
8487
|
-
if (detail.run.status === "terminated") {
|
|
8488
|
-
throw new Error("Run was terminated.");
|
|
8489
|
-
}
|
|
8490
8481
|
return detail;
|
|
8491
8482
|
}
|
|
8492
8483
|
if (timeoutMs !== void 0 && Date.now() - startedAt > timeoutMs) {
|
|
@@ -8677,7 +8668,7 @@ var RunsClient = class extends ResourceClient {
|
|
|
8677
8668
|
mode: selectedModeQuote.mode
|
|
8678
8669
|
};
|
|
8679
8670
|
}
|
|
8680
|
-
const parsed = runCreateSchema.parse(payload);
|
|
8671
|
+
const parsed = input.pricingQuoteId ? payload : runCreateSchema.parse(payload);
|
|
8681
8672
|
const run = await this.requestPost("/v1/runs", parsed);
|
|
8682
8673
|
return new RunHandle(this.parent, run);
|
|
8683
8674
|
}
|
|
@@ -8737,7 +8728,12 @@ var RuntimeClient = class extends ResourceClient {
|
|
|
8737
8728
|
projectId: input.projectId ?? this.parent.projectId,
|
|
8738
8729
|
source: input.source
|
|
8739
8730
|
});
|
|
8740
|
-
return this.requestPost("/v1/runtime/detect", payload)
|
|
8731
|
+
return this.requestPost("/v1/runtime/detect", payload).catch((error) => {
|
|
8732
|
+
if (input.source.kind === "git" && error instanceof Error && /Request failed with status 404/.test(error.message)) {
|
|
8733
|
+
throw new Error("Git repository could not be resolved. Check the repository URL, branch, and access permissions.");
|
|
8734
|
+
}
|
|
8735
|
+
throw error;
|
|
8736
|
+
});
|
|
8741
8737
|
}
|
|
8742
8738
|
build(input) {
|
|
8743
8739
|
const payload = runtimeBuildCreateSchema.parse({
|
|
@@ -8899,6 +8895,7 @@ var idPatterns = {
|
|
|
8899
8895
|
org: /^org_[A-Za-z0-9_-]+$/,
|
|
8900
8896
|
pool: /^pool_[A-Za-z0-9_-]+$/,
|
|
8901
8897
|
project: /^proj_[A-Za-z0-9_-]+$/,
|
|
8898
|
+
quote: /^quote_[A-Za-z0-9_-]+$/,
|
|
8902
8899
|
run: /^run_[A-Za-z0-9_-]+$/,
|
|
8903
8900
|
serviceAccount: /^svc_[A-Za-z0-9_-]+$/,
|
|
8904
8901
|
supplier: /^[A-Za-z][A-Za-z0-9_-]*$/
|
|
@@ -8926,7 +8923,17 @@ function collectRuntimeFiles(repoPath) {
|
|
|
8926
8923
|
if (found.length >= maxFiles) {
|
|
8927
8924
|
return;
|
|
8928
8925
|
}
|
|
8929
|
-
|
|
8926
|
+
let entries;
|
|
8927
|
+
try {
|
|
8928
|
+
entries = import_node_fs.default.readdirSync(currentPath, { withFileTypes: true });
|
|
8929
|
+
} catch (error) {
|
|
8930
|
+
if (error.code === "EACCES" || error.code === "EPERM") {
|
|
8931
|
+
const relativePath = import_node_path2.default.relative(repoPath, currentPath) || ".";
|
|
8932
|
+
throw new Error(`Permission denied while scanning repository path: ${relativePath}`);
|
|
8933
|
+
}
|
|
8934
|
+
throw new Error(`Failed to scan repository path: ${currentPath}`);
|
|
8935
|
+
}
|
|
8936
|
+
for (const entry of entries) {
|
|
8930
8937
|
const absolute = import_node_path2.default.join(currentPath, entry.name);
|
|
8931
8938
|
const relative = import_node_path2.default.relative(repoPath, absolute).split(import_node_path2.default.sep).join("/");
|
|
8932
8939
|
if (entry.isDirectory()) {
|
|
@@ -8938,13 +8945,30 @@ function collectRuntimeFiles(repoPath) {
|
|
|
8938
8945
|
if (!entry.isFile() || !shouldIncludeFile(entry.name)) {
|
|
8939
8946
|
continue;
|
|
8940
8947
|
}
|
|
8941
|
-
|
|
8948
|
+
let stat;
|
|
8949
|
+
try {
|
|
8950
|
+
stat = import_node_fs.default.statSync(absolute);
|
|
8951
|
+
} catch (error) {
|
|
8952
|
+
if (error.code === "EACCES" || error.code === "EPERM") {
|
|
8953
|
+
throw new Error(`Permission denied while scanning repository file: ${relative}`);
|
|
8954
|
+
}
|
|
8955
|
+
throw new Error(`Failed to inspect repository file: ${relative}`);
|
|
8956
|
+
}
|
|
8942
8957
|
if (stat.size > maxFileBytes) {
|
|
8943
8958
|
continue;
|
|
8944
8959
|
}
|
|
8960
|
+
let content;
|
|
8961
|
+
try {
|
|
8962
|
+
content = import_node_fs.default.readFileSync(absolute, "utf8");
|
|
8963
|
+
} catch (error) {
|
|
8964
|
+
if (error.code === "EACCES" || error.code === "EPERM") {
|
|
8965
|
+
throw new Error(`Permission denied while reading repository file: ${relative}`);
|
|
8966
|
+
}
|
|
8967
|
+
throw new Error(`Failed to read repository file: ${relative}`);
|
|
8968
|
+
}
|
|
8945
8969
|
found.push({
|
|
8946
8970
|
path: relative,
|
|
8947
|
-
content
|
|
8971
|
+
content
|
|
8948
8972
|
});
|
|
8949
8973
|
}
|
|
8950
8974
|
}
|
|
@@ -8988,7 +9012,22 @@ function validateGitBranch(value) {
|
|
|
8988
9012
|
function validateRepoPath(repoPath) {
|
|
8989
9013
|
const absolute = import_node_path2.default.resolve(repoPath);
|
|
8990
9014
|
const parsed = import_node_path2.default.parse(absolute);
|
|
8991
|
-
|
|
9015
|
+
const unsafeRoots = /* @__PURE__ */ new Set([
|
|
9016
|
+
parsed.root,
|
|
9017
|
+
import_node_os.default.homedir(),
|
|
9018
|
+
import_node_path2.default.resolve(import_node_os.default.tmpdir()),
|
|
9019
|
+
import_node_path2.default.resolve("/tmp"),
|
|
9020
|
+
import_node_path2.default.resolve("/private/tmp"),
|
|
9021
|
+
import_node_path2.default.resolve("/var"),
|
|
9022
|
+
import_node_path2.default.resolve("/Users"),
|
|
9023
|
+
import_node_path2.default.resolve("/System"),
|
|
9024
|
+
import_node_path2.default.resolve("/Library"),
|
|
9025
|
+
import_node_path2.default.resolve("/Applications"),
|
|
9026
|
+
import_node_path2.default.resolve("/etc"),
|
|
9027
|
+
import_node_path2.default.resolve("/bin"),
|
|
9028
|
+
import_node_path2.default.resolve("/usr")
|
|
9029
|
+
]);
|
|
9030
|
+
if (unsafeRoots.has(absolute)) {
|
|
8992
9031
|
throw new Error(`Refusing to package unsafe repository path: ${repoPath}`);
|
|
8993
9032
|
}
|
|
8994
9033
|
let stat;
|
|
@@ -9210,7 +9249,7 @@ function buildComputeSpec(options) {
|
|
|
9210
9249
|
|
|
9211
9250
|
// src/index.ts
|
|
9212
9251
|
var DEFAULT_TRAINFABRIC_API_URL2 = "https://api.trainfabric.com";
|
|
9213
|
-
var CLI_VERSION = "0.1.
|
|
9252
|
+
var CLI_VERSION = "0.1.32";
|
|
9214
9253
|
var CONFIG_DIR = import_node_path3.default.join(import_node_os2.default.homedir(), ".trainfabric");
|
|
9215
9254
|
var CONFIG_PATH = import_node_path3.default.join(CONFIG_DIR, "config.json");
|
|
9216
9255
|
var FALLBACK_SECRET_PATH = import_node_path3.default.join(CONFIG_DIR, "session.enc");
|
|
@@ -9557,23 +9596,43 @@ function requireProjectId(options, config) {
|
|
|
9557
9596
|
}
|
|
9558
9597
|
function buildRunInput(options, config = loadConfig()) {
|
|
9559
9598
|
const sourceOptions = buildSourceOptions(options);
|
|
9560
|
-
|
|
9561
|
-
|
|
9599
|
+
const quoteId = options.quote === void 0 ? void 0 : normalizeId(options.quote, "quote", "Pricing quote ID");
|
|
9600
|
+
if (!quoteId && !options.dataset) {
|
|
9601
|
+
throw new Error("Dataset is required unless --quote is provided.");
|
|
9602
|
+
}
|
|
9603
|
+
if (!quoteId && !options.model) {
|
|
9604
|
+
throw new Error("Model is required unless --quote is provided.");
|
|
9605
|
+
}
|
|
9606
|
+
const projectId = normalizeOptionalId(options.project ?? config.projectId, "project", "Project ID");
|
|
9607
|
+
if (!quoteId && !projectId) {
|
|
9608
|
+
throw new Error("Project is required. Pass --project <projectId> or run `trainfabric config:set-project <projectId>`.");
|
|
9609
|
+
}
|
|
9610
|
+
const input = {
|
|
9562
9611
|
task: "sft",
|
|
9563
9612
|
method: "lora",
|
|
9564
|
-
|
|
9565
|
-
datasetId: normalizeId(options.dataset, "dataset", "Dataset ID"),
|
|
9613
|
+
pricingQuoteId: quoteId,
|
|
9566
9614
|
evalDatasetId: normalizeOptionalId(options.eval, "dataset", "Eval dataset ID"),
|
|
9567
|
-
pricingQuoteId: options.quote,
|
|
9568
9615
|
...sourceOptions,
|
|
9569
|
-
mode: normalizeTrainingMode(options.mode)
|
|
9570
|
-
|
|
9571
|
-
|
|
9616
|
+
mode: normalizeTrainingMode(options.mode)
|
|
9617
|
+
};
|
|
9618
|
+
if (projectId) {
|
|
9619
|
+
input.projectId = projectId;
|
|
9620
|
+
}
|
|
9621
|
+
if (options.model) {
|
|
9622
|
+
input.baseModel = normalizeBaseModel(options.model);
|
|
9623
|
+
}
|
|
9624
|
+
if (options.dataset) {
|
|
9625
|
+
input.datasetId = normalizeId(options.dataset, "dataset", "Dataset ID");
|
|
9626
|
+
}
|
|
9627
|
+
if (!quoteId) {
|
|
9628
|
+
input.compute = buildComputeSpec(options);
|
|
9629
|
+
input.hyperparameters = {
|
|
9572
9630
|
epochs: normalizeEpochs(options.epochs),
|
|
9573
9631
|
lr: normalizeLearningRate(options.lr),
|
|
9574
9632
|
batchSize: "auto"
|
|
9575
|
-
}
|
|
9576
|
-
}
|
|
9633
|
+
};
|
|
9634
|
+
}
|
|
9635
|
+
return input;
|
|
9577
9636
|
}
|
|
9578
9637
|
function assertQuoteOptions(bundle, options) {
|
|
9579
9638
|
if (bundle.quotes.length > 0) {
|
|
@@ -9714,14 +9773,15 @@ function normalizeHttpBaseUrl(value) {
|
|
|
9714
9773
|
try {
|
|
9715
9774
|
url = new URL(value);
|
|
9716
9775
|
} catch {
|
|
9717
|
-
throw new Error(`Invalid base URL "${value}". Use an absolute
|
|
9776
|
+
throw new Error(`Invalid base URL "${value}". Use an absolute HTTPS origin such as https://api.trainfabric.com.`);
|
|
9777
|
+
}
|
|
9778
|
+
const isLocalDevHost = url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "::1";
|
|
9779
|
+
if (url.protocol !== "https:" && !(isLocalDevHost && url.protocol === "http:")) {
|
|
9780
|
+
throw new Error(`Invalid base URL "${value}". Use HTTPS, or http://localhost for local development.`);
|
|
9718
9781
|
}
|
|
9719
|
-
if (url.
|
|
9720
|
-
throw new Error(`Invalid base URL "${value}". Use
|
|
9782
|
+
if (url.pathname !== "/" || url.search || url.hash) {
|
|
9783
|
+
throw new Error(`Invalid base URL "${value}". Use the API origin without a path, query, or hash.`);
|
|
9721
9784
|
}
|
|
9722
|
-
url.pathname = url.pathname.replace(/\/+$/, "");
|
|
9723
|
-
url.search = "";
|
|
9724
|
-
url.hash = "";
|
|
9725
9785
|
return url.toString().replace(/\/+$/, "");
|
|
9726
9786
|
}
|
|
9727
9787
|
async function login(config) {
|
|
@@ -9872,7 +9932,7 @@ program2.command("datasets:upload").argument("<file>").option("--project <projec
|
|
|
9872
9932
|
});
|
|
9873
9933
|
printJson(dataset);
|
|
9874
9934
|
});
|
|
9875
|
-
program2.command("runs:create").option("--project <projectId>").
|
|
9935
|
+
program2.command("runs:create").option("--project <projectId>").option("--dataset <datasetId>").option("--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").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) => {
|
|
9876
9936
|
const config = loadConfig();
|
|
9877
9937
|
const runInput = buildRunInput(options, config);
|
|
9878
9938
|
if (!options.yes) {
|