@tuned-tensor/cli 0.4.16 → 0.4.17
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 +3 -3
- package/dist/index.js +99 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -55,9 +55,9 @@ tt models base
|
|
|
55
55
|
tt balance
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
For the full command reference, including dataset-backed runs,
|
|
59
|
-
fine-tuning, evaluation caps, local model serving,
|
|
60
|
-
[CLI docs](https://tunedtensor.com/docs/cli).
|
|
58
|
+
For the full command reference, including dataset-backed runs, long-example
|
|
59
|
+
policies, continued fine-tuning, evaluation caps, local model serving,
|
|
60
|
+
configuration, and billing, see the [CLI docs](https://tunedtensor.com/docs/cli).
|
|
61
61
|
|
|
62
62
|
## Development
|
|
63
63
|
|
package/dist/index.js
CHANGED
|
@@ -443,7 +443,8 @@ var SPEC_BODY_KEYS = /* @__PURE__ */ new Set([
|
|
|
443
443
|
"system_prompt",
|
|
444
444
|
"guidelines",
|
|
445
445
|
"constraints",
|
|
446
|
-
"examples"
|
|
446
|
+
"examples",
|
|
447
|
+
"eval_cases"
|
|
447
448
|
]);
|
|
448
449
|
var RUN_INPUT_KEYS = ["run_id", "behavior_spec_id", "spec_snapshot", "run_number"];
|
|
449
450
|
var SpecBodyError = class extends Error {
|
|
@@ -583,6 +584,15 @@ function registerSpecsCommands(parent) {
|
|
|
583
584
|
|
|
584
585
|
// src/commands/runs.ts
|
|
585
586
|
import ora from "ora";
|
|
587
|
+
var LONG_EXAMPLE_POLICIES = ["error", "truncate", "skip"];
|
|
588
|
+
function parseLongExamplesPolicy(value) {
|
|
589
|
+
if (LONG_EXAMPLE_POLICIES.includes(value)) {
|
|
590
|
+
return value;
|
|
591
|
+
}
|
|
592
|
+
throw new Error(
|
|
593
|
+
`--long-examples must be one of: ${LONG_EXAMPLE_POLICIES.join(", ")}`
|
|
594
|
+
);
|
|
595
|
+
}
|
|
586
596
|
function formatProgress(run) {
|
|
587
597
|
if (run.progress_pct == null && !run.stage_label) return void 0;
|
|
588
598
|
const label = run.stage_label ?? run.current_stage ?? "Progress";
|
|
@@ -791,7 +801,7 @@ Eval Results (${data._evals.length}):`);
|
|
|
791
801
|
);
|
|
792
802
|
}
|
|
793
803
|
});
|
|
794
|
-
runs.command("start").description("Start a new run for a behaviour spec").argument("<spec-id>", "Behaviour spec ID (full UUID or 8+ char prefix)").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").action(async (specId, cmdOpts) => {
|
|
804
|
+
runs.command("start").description("Start a new run for a behaviour spec").argument("<spec-id>", "Behaviour spec ID (full UUID or 8+ char prefix)").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").action(async (specId, cmdOpts) => {
|
|
795
805
|
const opts = parent.opts();
|
|
796
806
|
const body = {};
|
|
797
807
|
if (cmdOpts.augment === false) body.augment = false;
|
|
@@ -819,6 +829,8 @@ Eval Results (${data._evals.length}):`);
|
|
|
819
829
|
if (cmdOpts.loraAlpha) hp.lora_alpha = Number(cmdOpts.loraAlpha);
|
|
820
830
|
if (cmdOpts.maxEvalExamples) hp.max_eval_examples = Number(cmdOpts.maxEvalExamples);
|
|
821
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);
|
|
822
834
|
if (cmdOpts.llmJudge === false) body.use_llm_judge = false;
|
|
823
835
|
if (Object.keys(hp).length) body.hyperparameters = hp;
|
|
824
836
|
const fullSpecId = await resolveSpecId(specId, opts);
|
|
@@ -2784,11 +2796,90 @@ function validateSpec(spec) {
|
|
|
2784
2796
|
message: violations.length === 0 ? void 0 : violations.join("; ")
|
|
2785
2797
|
});
|
|
2786
2798
|
}
|
|
2799
|
+
const evalCaseErrors = validateEvalCases(spec);
|
|
2800
|
+
checks.push({
|
|
2801
|
+
name: "Eval cases valid",
|
|
2802
|
+
passed: evalCaseErrors.length === 0,
|
|
2803
|
+
message: evalCaseErrors.length === 0 ? spec.eval_cases?.length ? `${spec.eval_cases.length} executable eval case(s)` : void 0 : evalCaseErrors.join("; ")
|
|
2804
|
+
});
|
|
2787
2805
|
return {
|
|
2788
2806
|
valid: checks.every((c) => c.passed),
|
|
2789
2807
|
checks
|
|
2790
2808
|
};
|
|
2791
2809
|
}
|
|
2810
|
+
function validateEvalCases(spec) {
|
|
2811
|
+
const errors = [];
|
|
2812
|
+
const cases = spec.eval_cases ?? [];
|
|
2813
|
+
for (const [caseIndex, evalCase] of cases.entries()) {
|
|
2814
|
+
const label = `eval_cases[${caseIndex}]`;
|
|
2815
|
+
if (!evalCase || typeof evalCase !== "object") {
|
|
2816
|
+
errors.push(`${label} must be an object`);
|
|
2817
|
+
continue;
|
|
2818
|
+
}
|
|
2819
|
+
if (typeof evalCase.input !== "string" || evalCase.input.trim().length === 0) {
|
|
2820
|
+
errors.push(`${label}.input must be a non-empty string`);
|
|
2821
|
+
}
|
|
2822
|
+
if (evalCase.runtime !== "python") {
|
|
2823
|
+
errors.push(`${label}.runtime must be "python"`);
|
|
2824
|
+
}
|
|
2825
|
+
if (!Array.isArray(evalCase.tests) || evalCase.tests.length === 0) {
|
|
2826
|
+
errors.push(`${label}.tests must contain at least one test`);
|
|
2827
|
+
continue;
|
|
2828
|
+
}
|
|
2829
|
+
for (const [testIndex, test] of evalCase.tests.entries()) {
|
|
2830
|
+
const testLabel = `${label}.tests[${testIndex}]`;
|
|
2831
|
+
if (!test || typeof test !== "object") {
|
|
2832
|
+
errors.push(`${testLabel} must be an object`);
|
|
2833
|
+
continue;
|
|
2834
|
+
}
|
|
2835
|
+
if (test.name !== void 0 && (typeof test.name !== "string" || test.name.trim().length === 0)) {
|
|
2836
|
+
errors.push(`${testLabel}.name must be a non-empty string when provided`);
|
|
2837
|
+
}
|
|
2838
|
+
if (test.args !== void 0 && (!Array.isArray(test.args) || !test.args.every((arg) => typeof arg === "string"))) {
|
|
2839
|
+
errors.push(`${testLabel}.args must be an array of strings`);
|
|
2840
|
+
}
|
|
2841
|
+
if (test.stdin !== void 0 && typeof test.stdin !== "string") {
|
|
2842
|
+
errors.push(`${testLabel}.stdin must be a string`);
|
|
2843
|
+
}
|
|
2844
|
+
if (test.expected_exit_code !== void 0 && (typeof test.expected_exit_code !== "number" || !Number.isInteger(test.expected_exit_code) || test.expected_exit_code < 0 || test.expected_exit_code > 255)) {
|
|
2845
|
+
errors.push(`${testLabel}.expected_exit_code must be an integer between 0 and 255`);
|
|
2846
|
+
}
|
|
2847
|
+
if (test.expected_stdout !== void 0 && typeof test.expected_stdout !== "string") {
|
|
2848
|
+
errors.push(`${testLabel}.expected_stdout must be a string`);
|
|
2849
|
+
}
|
|
2850
|
+
validateFiles(test.files, `${testLabel}.files`, errors);
|
|
2851
|
+
validateFiles(test.expected_files, `${testLabel}.expected_files`, errors);
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
return errors;
|
|
2855
|
+
}
|
|
2856
|
+
function validateFiles(files, label, errors) {
|
|
2857
|
+
if (files === void 0) return;
|
|
2858
|
+
if (!Array.isArray(files)) {
|
|
2859
|
+
errors.push(`${label} must be an array`);
|
|
2860
|
+
return;
|
|
2861
|
+
}
|
|
2862
|
+
for (const [index, file] of files.entries()) {
|
|
2863
|
+
const fileLabel = `${label}[${index}]`;
|
|
2864
|
+
if (!file || typeof file !== "object") {
|
|
2865
|
+
errors.push(`${fileLabel} must be an object`);
|
|
2866
|
+
continue;
|
|
2867
|
+
}
|
|
2868
|
+
const row = file;
|
|
2869
|
+
if (typeof row.path !== "string" || !isSafeRelativePath(row.path)) {
|
|
2870
|
+
errors.push(`${fileLabel}.path must be a safe relative path`);
|
|
2871
|
+
}
|
|
2872
|
+
if (row.content !== void 0 && typeof row.content !== "string") {
|
|
2873
|
+
errors.push(`${fileLabel}.content must be a string`);
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
function isSafeRelativePath(value) {
|
|
2878
|
+
if (value.length === 0 || value.length > 240) return false;
|
|
2879
|
+
if (value.includes("\0") || value.startsWith("/")) return false;
|
|
2880
|
+
const normalized = value.replace(/\\/g, "/");
|
|
2881
|
+
return !normalized.split("/").some((part) => part === "" || part === "." || part === "..");
|
|
2882
|
+
}
|
|
2792
2883
|
function checkExamplesAgainstConstraints(spec) {
|
|
2793
2884
|
const violations = [];
|
|
2794
2885
|
for (const [i, ex] of spec.examples.entries()) {
|
|
@@ -2862,7 +2953,11 @@ function registerPushCommand(parent) {
|
|
|
2862
2953
|
const opts = parent.opts();
|
|
2863
2954
|
const filePath = resolve3(cmdOpts.file);
|
|
2864
2955
|
const spec = loadSpec(cmdOpts.file);
|
|
2865
|
-
const
|
|
2956
|
+
const evalCaseErrors = validateEvalCases(spec);
|
|
2957
|
+
if (evalCaseErrors.length > 0) {
|
|
2958
|
+
throw new Error(`Invalid eval_cases: ${evalCaseErrors.join("; ")}`);
|
|
2959
|
+
}
|
|
2960
|
+
const { id, ...rawBody } = spec;
|
|
2866
2961
|
const body = canonicalizeSpecBaseModel(rawBody);
|
|
2867
2962
|
let data;
|
|
2868
2963
|
if (id) {
|
|
@@ -2884,7 +2979,7 @@ function registerPushCommand(parent) {
|
|
|
2884
2979
|
|
|
2885
2980
|
// src/index.ts
|
|
2886
2981
|
var program = new Command();
|
|
2887
|
-
program.name("tt").description("Tuned Tensor CLI \u2014 fine-tune and evaluate LLMs").version("0.4.
|
|
2982
|
+
program.name("tt").description("Tuned Tensor CLI \u2014 fine-tune and evaluate LLMs").version("0.4.17").option("-k, --api-key <key>", "API key (overrides stored key)").option(
|
|
2888
2983
|
"-u, --base-url <url>",
|
|
2889
2984
|
"API base URL (default: https://tunedtensor.com)"
|
|
2890
2985
|
).option("--json", "Output raw JSON").option("--no-color", "Disable colors").hook("preAction", (_thisCommand, actionCommand) => {
|