@tuned-tensor/cli 0.4.21 → 0.4.22
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/dist/index.js +161 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -625,6 +625,14 @@ function formatEvalScore(run) {
|
|
|
625
625
|
function formatPercent(rate) {
|
|
626
626
|
return rate == null ? void 0 : (rate * 100).toFixed(1) + "%";
|
|
627
627
|
}
|
|
628
|
+
function formatPointDelta(delta) {
|
|
629
|
+
if (delta == null) return void 0;
|
|
630
|
+
const points = delta * 100;
|
|
631
|
+
return `${points >= 0 ? "+" : ""}${points.toFixed(1)} pp`;
|
|
632
|
+
}
|
|
633
|
+
function formatScore(score) {
|
|
634
|
+
return score == null ? "\u2014" : score.toFixed(2);
|
|
635
|
+
}
|
|
628
636
|
function formatCountRate(count, total, rate) {
|
|
629
637
|
if (count == null || total == null || rate == null) return void 0;
|
|
630
638
|
return `${formatPercent(rate)} (${count}/${total})`;
|
|
@@ -762,6 +770,131 @@ function printEvalOutputDiagnostics(diagnostics) {
|
|
|
762
770
|
}
|
|
763
771
|
}
|
|
764
772
|
}
|
|
773
|
+
function compactWhitespace(value) {
|
|
774
|
+
return value.replace(/\s+/g, " ").trim();
|
|
775
|
+
}
|
|
776
|
+
function parseJsonish(value) {
|
|
777
|
+
if (!value) return value;
|
|
778
|
+
try {
|
|
779
|
+
return JSON.parse(value);
|
|
780
|
+
} catch {
|
|
781
|
+
return value;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
function formatJsonish(value, max = 360) {
|
|
785
|
+
const parsed = parseJsonish(value);
|
|
786
|
+
const rendered = typeof parsed === "string" ? compactWhitespace(parsed) : JSON.stringify(parsed);
|
|
787
|
+
return truncate(rendered ?? "\u2014", max);
|
|
788
|
+
}
|
|
789
|
+
function formatPromptSummary(prompt, max = 260) {
|
|
790
|
+
const subject = prompt.match(/^Subject:\s*(.+)$/m)?.[1]?.trim();
|
|
791
|
+
const body = prompt.match(/^Body:\s*([\s\S]+)$/m)?.[1];
|
|
792
|
+
if (subject) {
|
|
793
|
+
const bodySummary = body ? ` | Body: ${compactWhitespace(body)}` : "";
|
|
794
|
+
return truncate(`Subject: ${subject}${bodySummary}`, max);
|
|
795
|
+
}
|
|
796
|
+
return truncate(compactWhitespace(prompt), max);
|
|
797
|
+
}
|
|
798
|
+
function indexResults(results) {
|
|
799
|
+
return new Map((results ?? []).map((result) => [result.prompt, result]));
|
|
800
|
+
}
|
|
801
|
+
function printReportMetrics(label, split) {
|
|
802
|
+
if (!split) return;
|
|
803
|
+
const comparison = split.comparison;
|
|
804
|
+
const baseline = split.baseline;
|
|
805
|
+
const candidate = split.candidate;
|
|
806
|
+
console.log(`
|
|
807
|
+
${label} Metrics:`);
|
|
808
|
+
printDetail([
|
|
809
|
+
["Base Avg", formatPercent(baseline?.avg_score)],
|
|
810
|
+
["Tuned Avg", formatPercent(candidate?.avg_score)],
|
|
811
|
+
["Avg Delta", formatPointDelta(comparison?.avg_score_delta)],
|
|
812
|
+
["Base Pass", formatPercent(baseline?.pass_rate)],
|
|
813
|
+
["Tuned Pass", formatPercent(candidate?.pass_rate)],
|
|
814
|
+
["Pass Delta", formatPointDelta(comparison?.pass_rate_delta)],
|
|
815
|
+
["Regressions", comparison?.regressions == null ? void 0 : String(comparison.regressions)],
|
|
816
|
+
["Improvements", comparison?.improvements == null ? void 0 : String(comparison.improvements)],
|
|
817
|
+
["Eval Rows", candidate?.eval_examples_used == null ? candidate?.total == null ? void 0 : String(candidate.total) : String(candidate.eval_examples_used)]
|
|
818
|
+
]);
|
|
819
|
+
}
|
|
820
|
+
function printComparedExample(input) {
|
|
821
|
+
const { index, prompt, baseline, candidate, oldScore, newScore } = input;
|
|
822
|
+
const expected = candidate?.expected ?? baseline?.expected;
|
|
823
|
+
const scoreLine = oldScore != null || newScore != null ? oldScore == null ? `tuned score ${formatScore(newScore)}` : `score ${formatScore(oldScore)} -> ${formatScore(newScore)}` : `tuned score ${formatScore(candidate?.score)}`;
|
|
824
|
+
console.log(`
|
|
825
|
+
${index}. ${scoreLine}`);
|
|
826
|
+
console.log(` Prompt: ${formatPromptSummary(prompt)}`);
|
|
827
|
+
console.log(` Expected: ${formatJsonish(expected)}`);
|
|
828
|
+
if (baseline?.actual != null) {
|
|
829
|
+
console.log(` Base: ${formatJsonish(baseline.actual)}`);
|
|
830
|
+
}
|
|
831
|
+
if (candidate?.actual != null) {
|
|
832
|
+
console.log(` Tuned: ${formatJsonish(candidate.actual)}`);
|
|
833
|
+
}
|
|
834
|
+
const reasoning = candidate?.reasoning ?? baseline?.reasoning;
|
|
835
|
+
if (reasoning) {
|
|
836
|
+
console.log(` Judge: ${truncate(compactWhitespace(reasoning), 420)}`);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
function printReportExamples(label, split, mode, limit) {
|
|
840
|
+
if (!split || limit <= 0) return;
|
|
841
|
+
const baselineByPrompt = indexResults(split.baseline?.results);
|
|
842
|
+
const candidateByPrompt = indexResults(split.candidate?.results);
|
|
843
|
+
if (mode === "regressions") {
|
|
844
|
+
const regressions = split.comparison?.regressed_examples ?? [];
|
|
845
|
+
console.log(`
|
|
846
|
+
${label} Regressions:`);
|
|
847
|
+
if (!regressions.length) {
|
|
848
|
+
console.log(" No regressed examples in the report.");
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
regressions.slice(0, limit).forEach((example, i) => {
|
|
852
|
+
printComparedExample({
|
|
853
|
+
index: i + 1,
|
|
854
|
+
prompt: example.prompt,
|
|
855
|
+
baseline: baselineByPrompt.get(example.prompt),
|
|
856
|
+
candidate: candidateByPrompt.get(example.prompt),
|
|
857
|
+
oldScore: example.old_score,
|
|
858
|
+
newScore: example.new_score
|
|
859
|
+
});
|
|
860
|
+
});
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
const failures = (split.candidate?.results ?? []).filter((result) => result.passed === false || (result.score ?? 1) < 1).sort((a, b) => (a.score ?? 0) - (b.score ?? 0));
|
|
864
|
+
console.log(`
|
|
865
|
+
${label} Tuned Failures:`);
|
|
866
|
+
if (!failures.length) {
|
|
867
|
+
console.log(" No tuned failures in the report.");
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
failures.slice(0, limit).forEach((candidate, i) => {
|
|
871
|
+
printComparedExample({
|
|
872
|
+
index: i + 1,
|
|
873
|
+
prompt: candidate.prompt,
|
|
874
|
+
baseline: baselineByPrompt.get(candidate.prompt),
|
|
875
|
+
candidate,
|
|
876
|
+
newScore: candidate.score
|
|
877
|
+
});
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
function printRunReport(report, options) {
|
|
881
|
+
printDetail([
|
|
882
|
+
["Run", report.run_id ? shortId(report.run_id) : void 0],
|
|
883
|
+
["Status", report.status ? formatStatus(report.status) : void 0],
|
|
884
|
+
["Model", report.fine_tuned_model_id ? shortId(report.fine_tuned_model_id) : void 0]
|
|
885
|
+
]);
|
|
886
|
+
const splits = [];
|
|
887
|
+
if (options.split === "primary" || options.split === "all") {
|
|
888
|
+
splits.push(["Primary", report]);
|
|
889
|
+
}
|
|
890
|
+
if (options.split === "test" || options.split === "all") {
|
|
891
|
+
splits.push(["Test", report.test]);
|
|
892
|
+
}
|
|
893
|
+
for (const [label, split] of splits) {
|
|
894
|
+
printReportMetrics(label, split);
|
|
895
|
+
printReportExamples(label, split, options.mode, options.limit);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
765
898
|
function addRunConfigurationOptions(command) {
|
|
766
899
|
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");
|
|
767
900
|
}
|
|
@@ -959,6 +1092,33 @@ Eval Results (${data._evals.length}):`);
|
|
|
959
1092
|
if (isJsonMode()) return printJson(data);
|
|
960
1093
|
printDiagnostics(data);
|
|
961
1094
|
});
|
|
1095
|
+
runs.command("report").description("Show run metrics and side-by-side eval output insights").argument("<id>", "Run ID (full UUID or 8+ char prefix)").option("--split <split>", "Split to show: primary, test, or all", "primary").option("--limit <n>", "Number of examples to show", "5").option("--mode <mode>", "Example mode: regressions or failures", "regressions").action(async (id, cmdOpts) => {
|
|
1096
|
+
const opts = parent.opts();
|
|
1097
|
+
const fullId = await resolveRunId(id, opts);
|
|
1098
|
+
const { data } = await get(
|
|
1099
|
+
`/runs/${fullId}/report`,
|
|
1100
|
+
void 0,
|
|
1101
|
+
opts
|
|
1102
|
+
);
|
|
1103
|
+
if (isJsonMode()) return printJson(data);
|
|
1104
|
+
const split = String(cmdOpts.split);
|
|
1105
|
+
if (!["primary", "test", "all"].includes(split)) {
|
|
1106
|
+
throw new Error("--split must be one of: primary, test, all");
|
|
1107
|
+
}
|
|
1108
|
+
const mode = String(cmdOpts.mode);
|
|
1109
|
+
if (!["regressions", "failures"].includes(mode)) {
|
|
1110
|
+
throw new Error("--mode must be one of: regressions, failures");
|
|
1111
|
+
}
|
|
1112
|
+
const limit = Number(cmdOpts.limit);
|
|
1113
|
+
if (!Number.isFinite(limit) || limit < 0) {
|
|
1114
|
+
throw new Error("--limit must be a non-negative number");
|
|
1115
|
+
}
|
|
1116
|
+
printRunReport(data, {
|
|
1117
|
+
split,
|
|
1118
|
+
mode,
|
|
1119
|
+
limit: Math.floor(limit)
|
|
1120
|
+
});
|
|
1121
|
+
});
|
|
962
1122
|
}
|
|
963
1123
|
|
|
964
1124
|
// src/commands/datasets.ts
|
|
@@ -3963,7 +4123,7 @@ function registerPushCommand(parent) {
|
|
|
3963
4123
|
|
|
3964
4124
|
// src/index.ts
|
|
3965
4125
|
var program = new Command();
|
|
3966
|
-
program.name("tt").description("Tuned Tensor CLI \u2014 fine-tune and evaluate LLMs").version("0.4.
|
|
4126
|
+
program.name("tt").description("Tuned Tensor CLI \u2014 fine-tune and evaluate LLMs").version("0.4.22").option("-k, --api-key <key>", "API key (overrides stored key)").option(
|
|
3967
4127
|
"-u, --base-url <url>",
|
|
3968
4128
|
"API base URL (default: https://tunedtensor.com)"
|
|
3969
4129
|
).option("--json", "Output raw JSON").option("--no-color", "Disable colors").hook("preAction", (_thisCommand, actionCommand) => {
|