@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 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.21").option("-k, --api-key <key>", "API key (overrides stored key)").option(
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) => {