trainfabric 0.1.45 → 0.1.46

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.
Files changed (2) hide show
  1. package/dist/index.cjs +68 -7
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -7648,6 +7648,7 @@ var runCreateSchema = external_exports.object({
7648
7648
  runtimeManifestId: external_exports.string().min(1).optional(),
7649
7649
  profileSessionId: external_exports.string().min(1).optional(),
7650
7650
  pricingQuoteId: external_exports.string().min(1).optional(),
7651
+ maxCostUsd: external_exports.number().positive().max(1e5).optional(),
7651
7652
  experimentTracking: experimentTrackingInputSchema.optional()
7652
7653
  });
7653
7654
  var jobProfileSchema = runCreateSchema;
@@ -7693,6 +7694,7 @@ var runQuoteSchema = external_exports.object({
7693
7694
  hyperparameters: runHyperparametersSchema.optional(),
7694
7695
  runtimeVersion: external_exports.string().min(1).optional(),
7695
7696
  mode: external_exports.enum(trainingModes).optional(),
7697
+ maxCostUsd: external_exports.number().positive().max(1e5).optional(),
7696
7698
  reservationClass: external_exports.enum(reservationClasses).optional(),
7697
7699
  slaClass: external_exports.enum(slaClasses).optional(),
7698
7700
  runtimeManifestId: external_exports.string().min(1).optional(),
@@ -7762,11 +7764,20 @@ var serviceAccountCreateSchema = external_exports.object({
7762
7764
  scopes: external_exports.array(external_exports.enum(apiKeyScopes)).min(1)
7763
7765
  });
7764
7766
  var billingCheckoutSessionCreateSchema = external_exports.object({
7765
- amountUsd: external_exports.number().positive().max(1e5).optional(),
7767
+ amountUsd: external_exports.number().min(5).max(1e5).optional(),
7766
7768
  mode: external_exports.enum(["subscription", "credits"]).default("subscription"),
7767
7769
  planId: external_exports.enum(["hobby", "pro"]).optional(),
7768
7770
  priceId: external_exports.string().min(1).optional()
7769
7771
  });
7772
+ var couponRedeemSchema = external_exports.object({
7773
+ code: external_exports.string().trim().min(1).max(64)
7774
+ });
7775
+ var creditAdjustmentCreateSchema = external_exports.object({
7776
+ amountUsd: external_exports.number().min(-1e5).max(1e5).refine((value) => value !== 0, "Credit adjustment amount cannot be zero."),
7777
+ kind: external_exports.enum(["manual_adjustment", "refund_credit", "refund_debit"]),
7778
+ reason: external_exports.string().trim().min(1).max(500),
7779
+ sourceId: external_exports.string().trim().min(1).max(128).optional()
7780
+ });
7770
7781
  var treasuryCounterpartyCreateSchema = external_exports.object({
7771
7782
  providerId: external_exports.enum(treasuryProviderIds).default("mercury"),
7772
7783
  name: external_exports.string().min(1),
@@ -8613,6 +8624,10 @@ function encodeRunId(runId) {
8613
8624
  }
8614
8625
  return encodeURIComponent(id);
8615
8626
  }
8627
+ function isTransientQuoteError(error) {
8628
+ const message = error instanceof Error ? error.message : String(error);
8629
+ return /status (502|503|504)\b/i.test(message);
8630
+ }
8616
8631
  var RunsClient = class extends ResourceClient {
8617
8632
  describeEmptyQuoteFailure(input) {
8618
8633
  const compute = input.compute && input.compute !== "auto" && input.compute !== "cpu-sim" ? input.compute : void 0;
@@ -8674,7 +8689,7 @@ var RunsClient = class extends ResourceClient {
8674
8689
  projectId: input.projectId ?? this.parent.projectId,
8675
8690
  profileSessionId: detail.session.id
8676
8691
  });
8677
- return this.requestPost("/v1/runs/quotes", payload);
8692
+ return this.requestQuoteWithRetry("/v1/runs/quotes", payload);
8678
8693
  }
8679
8694
  async quoteLegacy(input) {
8680
8695
  const detail = await this.ensureReadyProfile(input);
@@ -8683,7 +8698,23 @@ var RunsClient = class extends ResourceClient {
8683
8698
  projectId: input.projectId ?? this.parent.projectId,
8684
8699
  profileSessionId: detail.session.id
8685
8700
  });
8686
- return this.requestPost("/v1/runs/quote", payload);
8701
+ return this.requestQuoteWithRetry("/v1/runs/quote", payload);
8702
+ }
8703
+ async requestQuoteWithRetry(pathname, payload) {
8704
+ const attempts = Math.max(1, Number(process.env.TRAINFABRIC_QUOTE_RETRY_ATTEMPTS ?? 3));
8705
+ let lastError;
8706
+ for (let attempt = 1; attempt <= attempts; attempt += 1) {
8707
+ try {
8708
+ return await this.requestPost(pathname, payload);
8709
+ } catch (error) {
8710
+ lastError = error;
8711
+ if (!isTransientQuoteError(error) || attempt === attempts) {
8712
+ throw error;
8713
+ }
8714
+ await wait(500 * attempt);
8715
+ }
8716
+ }
8717
+ throw lastError instanceof Error ? lastError : new Error("Quote request failed.");
8687
8718
  }
8688
8719
  async create(input) {
8689
8720
  const projectId = input.projectId ?? this.parent.projectId;
@@ -9269,6 +9300,16 @@ function normalizeLearningRate(lr) {
9269
9300
  }
9270
9301
  return parsed;
9271
9302
  }
9303
+ function normalizeMaxCost(maxCost) {
9304
+ if (maxCost === void 0) {
9305
+ return void 0;
9306
+ }
9307
+ const parsed = Number(maxCost);
9308
+ if (!Number.isFinite(parsed) || parsed <= 0 || parsed > 1e5) {
9309
+ throw new Error("Max cost must be a positive USD amount up to 100000.");
9310
+ }
9311
+ return Number(parsed.toFixed(2));
9312
+ }
9272
9313
  function buildComputeSpec(options) {
9273
9314
  const gpuCount = normalizePositiveInteger(options.gpus, "GPU count", 1, 64);
9274
9315
  const nodeCount = normalizePositiveInteger(options.nodes, "Node count", 1, 16);
@@ -9310,7 +9351,7 @@ function buildComputeSpec(options) {
9310
9351
 
9311
9352
  // src/index.ts
9312
9353
  var DEFAULT_TRAINFABRIC_API_URL2 = "https://api.trainfabric.com";
9313
- var CLI_VERSION = "0.1.45";
9354
+ var CLI_VERSION = "0.1.46";
9314
9355
  var CONFIG_DIR = import_node_path3.default.join(import_node_os2.default.homedir(), ".trainfabric");
9315
9356
  var CONFIG_PATH = import_node_path3.default.join(CONFIG_DIR, "config.json");
9316
9357
  var FALLBACK_SECRET_PATH = import_node_path3.default.join(CONFIG_DIR, "session.enc");
@@ -9749,6 +9790,7 @@ function buildRunInput(options, config = loadConfig()) {
9749
9790
  pricingQuoteId: quoteId,
9750
9791
  evalDatasetId: normalizeOptionalId(options.eval, "dataset", "Eval dataset ID"),
9751
9792
  ...sourceOptions,
9793
+ maxCostUsd: normalizeMaxCost(options.maxCost),
9752
9794
  mode: normalizeTrainingMode(options.mode)
9753
9795
  };
9754
9796
  if (projectId) {
@@ -9843,7 +9885,8 @@ async function watchRun(runId, json = false, timeoutMs, pollMs) {
9843
9885
  });
9844
9886
  handle.on("log", (event) => {
9845
9887
  const payload = event.payload;
9846
- console.log(`[${payload.phase ?? "run"}:${payload.level ?? "info"}] ${payload.message ?? ""}`);
9888
+ const eventLabel = formatRunEventCode(payload.eventCode);
9889
+ console.log(`[${eventLabel ?? payload.phase ?? "run"}:${payload.level ?? "info"}] ${payload.message ?? ""}`);
9847
9890
  });
9848
9891
  handle.on("metric", (event) => {
9849
9892
  const payload = event.payload;
@@ -9874,6 +9917,24 @@ async function watchRun(runId, json = false, timeoutMs, pollMs) {
9874
9917
  );
9875
9918
  }
9876
9919
  }
9920
+ function formatRunEventCode(eventCode) {
9921
+ switch (eventCode) {
9922
+ case "provider_waiting_for_ssh":
9923
+ return "provider-waiting";
9924
+ case "relay_confirmation_timeout_nonfatal":
9925
+ return "relay-monitoring";
9926
+ case "dependency_installing":
9927
+ return "dependency-installing";
9928
+ case "runtime_bootstrap_fallback":
9929
+ return "runtime-bootstrap";
9930
+ case "training_started":
9931
+ return "training-started";
9932
+ case "artifact_syncing":
9933
+ return "artifact-syncing";
9934
+ default:
9935
+ return void 0;
9936
+ }
9937
+ }
9877
9938
  function parseDurationMs(value) {
9878
9939
  if (!value) {
9879
9940
  return void 0;
@@ -10082,7 +10143,7 @@ program2.command("datasets:upload").argument("<file>").option("--project <projec
10082
10143
  });
10083
10144
  printJson(dataset);
10084
10145
  });
10085
- 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; use only with --yes").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. When using --quote, do not pass project, dataset, model, compute, mode, source, or hyperparameter flags; launch with `trainfabric runs:create --quote <quoteId> --yes`.").action(async (options, command) => {
10146
+ 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("--max-cost <usd>", "maximum accepted quoted customer charge in USD").option("--quote <pricingQuoteId>", "launch the exact accepted quote id from runs:quote; use only with --yes").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. When using --quote, do not pass project, dataset, model, compute, mode, source, or hyperparameter flags; launch with `trainfabric runs:create --quote <quoteId> --yes`.").action(async (options, command) => {
10086
10147
  const config = loadConfig();
10087
10148
  assertQuoteCreateHasNoLaunchOverrides(options, command);
10088
10149
  const runInput = buildRunInput(options, config);
@@ -10129,7 +10190,7 @@ program2.command("runtime:build").option("--project <projectId>").requiredOption
10129
10190
  )
10130
10191
  );
10131
10192
  });
10132
- program2.command("runs:quote").option("--project <projectId>").requiredOption("--dataset <datasetId>").requiredOption("--model <baseModel>").option("--eval <evalDatasetId>").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("--repo <path>").option("--git <url>").option("--branch <branch>").option("--json", "print raw JSON output; this is the default unless --summary is used").option("--summary", "print a human-readable price summary instead of raw JSON").description("Preview Efficient, Balanced, and Power launch options").action(async (options) => {
10193
+ program2.command("runs:quote").option("--project <projectId>").requiredOption("--dataset <datasetId>").requiredOption("--model <baseModel>").option("--eval <evalDatasetId>").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("--max-cost <usd>", "maximum accepted quoted customer charge in USD").option("--repo <path>").option("--git <url>").option("--branch <branch>").option("--json", "print raw JSON output; this is the default unless --summary is used").option("--summary", "print a human-readable price summary instead of raw JSON").description("Preview Efficient, Balanced, and Power launch options").action(async (options) => {
10133
10194
  const config = loadConfig();
10134
10195
  const bundle = filterQuoteBundleForRequestedMode(
10135
10196
  await createClient(config).runs.quote(buildRunInput(options, config)),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trainfabric",
3
- "version": "0.1.45",
3
+ "version": "0.1.46",
4
4
  "description": "Trainfabric CLI for launching GPU training jobs on the hosted Trainfabric backend.",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",