@tuned-tensor/cli 0.4.18 → 0.4.19
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 +4 -2
- package/dist/index.js +89 -43
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,6 +34,7 @@ tt init --name "Customer Support Bot" --model Qwen/Qwen3.5-2B
|
|
|
34
34
|
# Edit tunedtensor.json, then:
|
|
35
35
|
tt eval
|
|
36
36
|
tt push
|
|
37
|
+
tt runs estimate <spec-id>
|
|
37
38
|
tt runs start <spec-id>
|
|
38
39
|
tt runs watch <run-id>
|
|
39
40
|
```
|
|
@@ -72,8 +73,9 @@ This wraps llama.cpp's `convert_hf_to_gguf.py` + `llama-quantize` and Ollama's
|
|
|
72
73
|
system prompt is embedded as the Modelfile `SYSTEM` block.
|
|
73
74
|
|
|
74
75
|
For the full command reference, including dataset-backed runs, long-example
|
|
75
|
-
policies, eval token budgets, continued fine-tuning,
|
|
76
|
-
configuration, and billing, see the
|
|
76
|
+
policies, eval token budgets, preflight run estimates, continued fine-tuning,
|
|
77
|
+
evaluation caps, local model serving, configuration, and billing, see the
|
|
78
|
+
[CLI docs](https://tunedtensor.com/docs/cli).
|
|
77
79
|
|
|
78
80
|
## Development
|
|
79
81
|
|
package/dist/index.js
CHANGED
|
@@ -617,6 +617,29 @@ function formatMinutes(minutes) {
|
|
|
617
617
|
if (minutes < 60) return `${Math.max(1, Math.round(minutes))}m`;
|
|
618
618
|
return `${(minutes / 60).toFixed(1)}h`;
|
|
619
619
|
}
|
|
620
|
+
function formatCents(cents) {
|
|
621
|
+
const dollars = cents / 100;
|
|
622
|
+
if (Math.abs(dollars) >= 100) return `$${dollars.toFixed(0)}`;
|
|
623
|
+
return `$${dollars.toFixed(2)}`;
|
|
624
|
+
}
|
|
625
|
+
function formatEstimateRange(estimate) {
|
|
626
|
+
const duration = estimate.duration;
|
|
627
|
+
return `${formatMinutes(duration.estimated_minutes)} (${formatMinutes(duration.range_minutes.low)}-${formatMinutes(duration.range_minutes.high)})`;
|
|
628
|
+
}
|
|
629
|
+
function printRunEstimate(estimate) {
|
|
630
|
+
printDetail([
|
|
631
|
+
["Estimated Time", formatEstimateRange(estimate)],
|
|
632
|
+
["Confidence", estimate.duration.confidence],
|
|
633
|
+
["History Samples", String(estimate.duration.sample_count)],
|
|
634
|
+
["Basis", estimate.duration.basis.replaceAll("_", " ")],
|
|
635
|
+
["Estimated Cost", formatCents(estimate.estimated_cost_cents)],
|
|
636
|
+
["Training Tokens", `${(estimate.estimated_training_tokens / 1e3).toFixed(1)}k`],
|
|
637
|
+
["Epochs", String(estimate.estimated_epochs)]
|
|
638
|
+
]);
|
|
639
|
+
console.log(
|
|
640
|
+
"\nDuration is a rough historical range. Final cost uses provider-reported training tokens."
|
|
641
|
+
);
|
|
642
|
+
}
|
|
620
643
|
function formatEpochProgress(diagnostics) {
|
|
621
644
|
const curve = diagnostics.training.curve;
|
|
622
645
|
if (curve.latest_epoch == null) return void 0;
|
|
@@ -722,6 +745,46 @@ function printEvalOutputDiagnostics(diagnostics) {
|
|
|
722
745
|
}
|
|
723
746
|
}
|
|
724
747
|
}
|
|
748
|
+
function addRunConfigurationOptions(command) {
|
|
749
|
+
return command.option("--no-augment", "Disable data augmentation").option("--no-llm-judge", "Disable LLM judging").option("--epochs <n>", "Number of training epochs").option("--lr <rate>", "Learning rate").option("--batch-size <n>", "Batch size").option("--dataset <id>", "Dataset ID to use instead of inline spec examples (full UUID or 4+ char prefix)").option("--parent-model <id>", "Fine-tuned model ID to continue training from (full UUID or 4+ char prefix)").option("--train-ratio <ratio>", "Dataset training split ratio (default: 0.8 when any split ratio is set)").option("--validation-ratio <ratio>", "Dataset validation split ratio (default: 0.1 when any split ratio is set)").option("--test-ratio <ratio>", "Dataset test split ratio (default: 0.1 when any split ratio is set)").option("--lora-rank <n>", "LoRA rank").option("--lora-alpha <n>", "LoRA alpha").option("--max-eval-examples <n>", "Max examples for the primary eval pass").option("--max-test-eval-examples <n>", "Max examples for the secondary test eval pass").option("--long-examples <policy>", "Long training row policy: error, truncate, or skip").option("--max-seq-length <tokens>", "Maximum training sequence length in tokens").option("--max-output-tokens <tokens>", "Desired evaluation output budget in tokens").option("--eval-reserved-output-tokens <tokens>", "Minimum evaluation output tokens reserved per row");
|
|
750
|
+
}
|
|
751
|
+
async function buildRunRequestBody(cmdOpts, opts) {
|
|
752
|
+
const body = {};
|
|
753
|
+
if (cmdOpts.augment === false) body.augment = false;
|
|
754
|
+
if (cmdOpts.dataset) body.dataset_id = await resolveDatasetId(String(cmdOpts.dataset), opts);
|
|
755
|
+
if (cmdOpts.parentModel) {
|
|
756
|
+
body.parent_model_id = await resolveModelId(String(cmdOpts.parentModel), opts);
|
|
757
|
+
}
|
|
758
|
+
const splitRatioOptions = [
|
|
759
|
+
cmdOpts.trainRatio,
|
|
760
|
+
cmdOpts.validationRatio,
|
|
761
|
+
cmdOpts.testRatio
|
|
762
|
+
];
|
|
763
|
+
if (splitRatioOptions.some((value) => value != null)) {
|
|
764
|
+
body.split_ratios = {
|
|
765
|
+
train: cmdOpts.trainRatio != null ? Number(cmdOpts.trainRatio) : 0.8,
|
|
766
|
+
validation: cmdOpts.validationRatio != null ? Number(cmdOpts.validationRatio) : 0.1,
|
|
767
|
+
test: cmdOpts.testRatio != null ? Number(cmdOpts.testRatio) : 0.1
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
const hp = {};
|
|
771
|
+
if (cmdOpts.epochs) hp.n_epochs = Number(cmdOpts.epochs);
|
|
772
|
+
if (cmdOpts.lr) hp.learning_rate = Number(cmdOpts.lr);
|
|
773
|
+
if (cmdOpts.batchSize) hp.batch_size = Number(cmdOpts.batchSize);
|
|
774
|
+
if (cmdOpts.loraRank) hp.lora_rank = Number(cmdOpts.loraRank);
|
|
775
|
+
if (cmdOpts.loraAlpha) hp.lora_alpha = Number(cmdOpts.loraAlpha);
|
|
776
|
+
if (cmdOpts.maxEvalExamples) hp.max_eval_examples = Number(cmdOpts.maxEvalExamples);
|
|
777
|
+
if (cmdOpts.maxTestEvalExamples) hp.max_test_eval_examples = Number(cmdOpts.maxTestEvalExamples);
|
|
778
|
+
if (cmdOpts.longExamples) hp.long_examples = parseLongExamplesPolicy(String(cmdOpts.longExamples));
|
|
779
|
+
if (cmdOpts.maxSeqLength) hp.max_seq_length = Number(cmdOpts.maxSeqLength);
|
|
780
|
+
if (cmdOpts.maxOutputTokens) hp.max_output_tokens = Number(cmdOpts.maxOutputTokens);
|
|
781
|
+
if (cmdOpts.evalReservedOutputTokens) {
|
|
782
|
+
hp.eval_reserved_output_tokens = Number(cmdOpts.evalReservedOutputTokens);
|
|
783
|
+
}
|
|
784
|
+
if (cmdOpts.llmJudge === false) body.use_llm_judge = false;
|
|
785
|
+
if (Object.keys(hp).length) body.hyperparameters = hp;
|
|
786
|
+
return body;
|
|
787
|
+
}
|
|
725
788
|
function registerRunsCommands(parent) {
|
|
726
789
|
const runs = parent.command("runs").description("Manage runs");
|
|
727
790
|
runs.command("list").description("List runs").option("-s, --spec <id>", "Filter by spec ID (full UUID or 8+ char prefix)").option("-p, --page <n>", "Page number", "1").option("--per-page <n>", "Results per page", "20").action(async (cmdOpts) => {
|
|
@@ -801,42 +864,25 @@ Eval Results (${data._evals.length}):`);
|
|
|
801
864
|
);
|
|
802
865
|
}
|
|
803
866
|
});
|
|
804
|
-
|
|
867
|
+
addRunConfigurationOptions(
|
|
868
|
+
runs.command("estimate").description("Estimate run cost and duration before starting").argument("<spec-id>", "Behaviour spec ID (full UUID or 8+ char prefix)")
|
|
869
|
+
).action(async (specId, cmdOpts) => {
|
|
805
870
|
const opts = parent.opts();
|
|
806
|
-
const body =
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
body
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
test: cmdOpts.testRatio != null ? Number(cmdOpts.testRatio) : 0.1
|
|
822
|
-
};
|
|
823
|
-
}
|
|
824
|
-
const hp = {};
|
|
825
|
-
if (cmdOpts.epochs) hp.n_epochs = Number(cmdOpts.epochs);
|
|
826
|
-
if (cmdOpts.lr) hp.learning_rate = Number(cmdOpts.lr);
|
|
827
|
-
if (cmdOpts.batchSize) hp.batch_size = Number(cmdOpts.batchSize);
|
|
828
|
-
if (cmdOpts.loraRank) hp.lora_rank = Number(cmdOpts.loraRank);
|
|
829
|
-
if (cmdOpts.loraAlpha) hp.lora_alpha = Number(cmdOpts.loraAlpha);
|
|
830
|
-
if (cmdOpts.maxEvalExamples) hp.max_eval_examples = Number(cmdOpts.maxEvalExamples);
|
|
831
|
-
if (cmdOpts.maxTestEvalExamples) hp.max_test_eval_examples = Number(cmdOpts.maxTestEvalExamples);
|
|
832
|
-
if (cmdOpts.longExamples) hp.long_examples = parseLongExamplesPolicy(cmdOpts.longExamples);
|
|
833
|
-
if (cmdOpts.maxSeqLength) hp.max_seq_length = Number(cmdOpts.maxSeqLength);
|
|
834
|
-
if (cmdOpts.maxOutputTokens) hp.max_output_tokens = Number(cmdOpts.maxOutputTokens);
|
|
835
|
-
if (cmdOpts.evalReservedOutputTokens) {
|
|
836
|
-
hp.eval_reserved_output_tokens = Number(cmdOpts.evalReservedOutputTokens);
|
|
837
|
-
}
|
|
838
|
-
if (cmdOpts.llmJudge === false) body.use_llm_judge = false;
|
|
839
|
-
if (Object.keys(hp).length) body.hyperparameters = hp;
|
|
871
|
+
const body = await buildRunRequestBody(cmdOpts, opts);
|
|
872
|
+
const fullSpecId = await resolveSpecId(specId, opts);
|
|
873
|
+
const { data } = await post(
|
|
874
|
+
`/behavior-specs/${fullSpecId}/runs/estimate`,
|
|
875
|
+
body,
|
|
876
|
+
opts
|
|
877
|
+
);
|
|
878
|
+
if (isJsonMode()) return printJson(data);
|
|
879
|
+
printRunEstimate(data);
|
|
880
|
+
});
|
|
881
|
+
addRunConfigurationOptions(
|
|
882
|
+
runs.command("start").description("Start a new run for a behaviour spec").argument("<spec-id>", "Behaviour spec ID (full UUID or 8+ char prefix)")
|
|
883
|
+
).action(async (specId, cmdOpts) => {
|
|
884
|
+
const opts = parent.opts();
|
|
885
|
+
const body = await buildRunRequestBody(cmdOpts, opts);
|
|
840
886
|
const fullSpecId = await resolveSpecId(specId, opts);
|
|
841
887
|
const { data } = await post(
|
|
842
888
|
`/behavior-specs/${fullSpecId}/runs`,
|
|
@@ -2927,15 +2973,15 @@ var KIND_LABELS = {
|
|
|
2927
2973
|
refund: "Refund",
|
|
2928
2974
|
adjustment: "Adjustment"
|
|
2929
2975
|
};
|
|
2930
|
-
function
|
|
2976
|
+
function formatCents2(cents) {
|
|
2931
2977
|
const sign = cents < 0 ? "-" : "";
|
|
2932
2978
|
const abs = Math.abs(cents);
|
|
2933
2979
|
return `${sign}$${(abs / 100).toFixed(2)}`;
|
|
2934
2980
|
}
|
|
2935
2981
|
function formatSigned(cents) {
|
|
2936
|
-
if (cents > 0) return chalk3.green(`+${
|
|
2937
|
-
if (cents < 0) return chalk3.red(
|
|
2938
|
-
return
|
|
2982
|
+
if (cents > 0) return chalk3.green(`+${formatCents2(cents)}`);
|
|
2983
|
+
if (cents < 0) return chalk3.red(formatCents2(cents));
|
|
2984
|
+
return formatCents2(cents);
|
|
2939
2985
|
}
|
|
2940
2986
|
function registerBalanceCommands(parent) {
|
|
2941
2987
|
parent.command("balance").description("Show credit balance and recent transactions").option("-n, --limit <n>", "Number of transactions to show (default 10)", "10").action(async (options) => {
|
|
@@ -2949,10 +2995,10 @@ function registerBalanceCommands(parent) {
|
|
|
2949
2995
|
return printJson({ balance, transactions });
|
|
2950
2996
|
}
|
|
2951
2997
|
const lowBalance = balance.balance_cents < 100;
|
|
2952
|
-
const balanceLine = lowBalance ? chalk3.red(
|
|
2998
|
+
const balanceLine = lowBalance ? chalk3.red(formatCents2(balance.balance_cents)) + chalk3.dim(" (low)") : chalk3.bold.green(formatCents2(balance.balance_cents));
|
|
2953
2999
|
printDetail([
|
|
2954
3000
|
["Credits", balanceLine],
|
|
2955
|
-
["Lifetime top-ups",
|
|
3001
|
+
["Lifetime top-ups", formatCents2(balance.lifetime_topup_cents)]
|
|
2956
3002
|
]);
|
|
2957
3003
|
if (transactions && transactions.length > 0) {
|
|
2958
3004
|
console.log(`
|
|
@@ -2963,7 +3009,7 @@ ${chalk3.bold("Recent transactions")}`);
|
|
|
2963
3009
|
formatDate(row.created_at),
|
|
2964
3010
|
KIND_LABELS[row.kind] ?? row.kind,
|
|
2965
3011
|
formatSigned(row.amount_cents),
|
|
2966
|
-
|
|
3012
|
+
formatCents2(row.balance_after_cents),
|
|
2967
3013
|
row.reference_id ? shortId(row.reference_id) : chalk3.dim("\u2014")
|
|
2968
3014
|
])
|
|
2969
3015
|
);
|
|
@@ -3294,7 +3340,7 @@ function registerPushCommand(parent) {
|
|
|
3294
3340
|
|
|
3295
3341
|
// src/index.ts
|
|
3296
3342
|
var program = new Command();
|
|
3297
|
-
program.name("tt").description("Tuned Tensor CLI \u2014 fine-tune and evaluate LLMs").version("0.4.
|
|
3343
|
+
program.name("tt").description("Tuned Tensor CLI \u2014 fine-tune and evaluate LLMs").version("0.4.19").option("-k, --api-key <key>", "API key (overrides stored key)").option(
|
|
3298
3344
|
"-u, --base-url <url>",
|
|
3299
3345
|
"API base URL (default: https://tunedtensor.com)"
|
|
3300
3346
|
).option("--json", "Output raw JSON").option("--no-color", "Disable colors").hook("preAction", (_thisCommand, actionCommand) => {
|