veryfront 0.1.1128 → 0.1.1133

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 (127) hide show
  1. package/esm/cli/commands/eval/command.d.ts +13 -70
  2. package/esm/cli/commands/eval/command.d.ts.map +1 -1
  3. package/esm/cli/commands/eval/command.js +207 -550
  4. package/esm/cli/mcp/tools/deploy-tool.d.ts.map +1 -1
  5. package/esm/cli/mcp/tools/deploy-tool.js +4 -1
  6. package/esm/deno.js +2 -1
  7. package/esm/src/agent/ag-ui/browser-encoder.d.ts.map +1 -1
  8. package/esm/src/agent/ag-ui/browser-encoder.js +3 -1
  9. package/esm/src/agent/ag-ui/runtime-chat-stream-encoder.d.ts.map +1 -1
  10. package/esm/src/agent/ag-ui/runtime-chat-stream-encoder.js +11 -1
  11. package/esm/src/agent/conversation/run-events.js +3 -3
  12. package/esm/src/agent/hosted/chat-execution-runtime.d.ts.map +1 -1
  13. package/esm/src/agent/hosted/chat-execution-runtime.js +22 -33
  14. package/esm/src/agent/hosted/child-fork-stream-execution.d.ts.map +1 -1
  15. package/esm/src/agent/hosted/child-fork-stream-execution.js +15 -0
  16. package/esm/src/agent/hosted/child-fork-tool-sources.d.ts.map +1 -1
  17. package/esm/src/agent/hosted/child-fork-tool-sources.js +6 -38
  18. package/esm/src/agent/hosted/child-mirror.d.ts +1 -1
  19. package/esm/src/agent/hosted/child-mirror.d.ts.map +1 -1
  20. package/esm/src/agent/hosted/hosted-chat-finalization.d.ts +30 -0
  21. package/esm/src/agent/hosted/hosted-chat-finalization.d.ts.map +1 -0
  22. package/esm/src/agent/hosted/hosted-chat-finalization.js +249 -0
  23. package/esm/src/agent/hosted/project-remote-tool-source.d.ts.map +1 -1
  24. package/esm/src/agent/hosted/project-remote-tool-source.js +4 -23
  25. package/esm/src/agent/mcp-tool-policy.d.ts +19 -0
  26. package/esm/src/agent/mcp-tool-policy.d.ts.map +1 -0
  27. package/esm/src/agent/mcp-tool-policy.js +66 -0
  28. package/esm/src/agent/react/index.d.ts +1 -1
  29. package/esm/src/agent/react/index.d.ts.map +1 -1
  30. package/esm/src/agent/react/use-chat/index.d.ts +1 -1
  31. package/esm/src/agent/react/use-chat/index.d.ts.map +1 -1
  32. package/esm/src/agent/react/use-chat/streaming/handler.d.ts.map +1 -1
  33. package/esm/src/agent/react/use-chat/streaming/handler.js +53 -5
  34. package/esm/src/agent/react/use-chat/types.d.ts +2 -2
  35. package/esm/src/agent/react/use-chat/types.d.ts.map +1 -1
  36. package/esm/src/agent/runtime/mcp-server-tool-sources.d.ts.map +1 -1
  37. package/esm/src/agent/runtime/mcp-server-tool-sources.js +8 -44
  38. package/esm/src/agent/runtime/message-adapter.d.ts +3 -3
  39. package/esm/src/agent/runtime/message-adapter.d.ts.map +1 -1
  40. package/esm/src/agent/runtime/message-adapter.js +6 -0
  41. package/esm/src/agent/schemas/agent.schema.d.ts +44 -0
  42. package/esm/src/agent/schemas/agent.schema.d.ts.map +1 -1
  43. package/esm/src/agent/schemas/agent.schema.js +15 -0
  44. package/esm/src/agent/streaming/chat-ui-message-stream.d.ts.map +1 -1
  45. package/esm/src/agent/streaming/chat-ui-message-stream.js +74 -4
  46. package/esm/src/agent/streaming/fork-runtime-part-mapper.d.ts.map +1 -1
  47. package/esm/src/agent/streaming/fork-runtime-part-mapper.js +52 -0
  48. package/esm/src/agent/streaming/fork-runtime-types.d.ts +3 -1
  49. package/esm/src/agent/streaming/fork-runtime-types.d.ts.map +1 -1
  50. package/esm/src/agent/streaming/mirrored-tool-chunk-state.d.ts +1 -0
  51. package/esm/src/agent/streaming/mirrored-tool-chunk-state.d.ts.map +1 -1
  52. package/esm/src/agent/streaming/mirrored-tool-chunk-state.js +87 -20
  53. package/esm/src/chat/conversation.d.ts +12 -0
  54. package/esm/src/chat/conversation.d.ts.map +1 -1
  55. package/esm/src/chat/conversation.js +18 -1
  56. package/esm/src/chat/hosted-ui-chunk-mapping.d.ts.map +1 -1
  57. package/esm/src/chat/hosted-ui-chunk-mapping.js +16 -2
  58. package/esm/src/{agent/streaming → chat}/knowledge-source-document.d.ts +1 -1
  59. package/esm/src/chat/knowledge-source-document.d.ts.map +1 -0
  60. package/esm/src/{agent/streaming → chat}/knowledge-source-document.js +4 -2
  61. package/esm/src/chat/protocol.d.ts +25 -2
  62. package/esm/src/chat/protocol.d.ts.map +1 -1
  63. package/esm/src/discovery/import-rewriter.d.ts.map +1 -1
  64. package/esm/src/discovery/import-rewriter.js +7 -90
  65. package/esm/src/eval/run-report.d.ts +189 -0
  66. package/esm/src/eval/run-report.d.ts.map +1 -0
  67. package/esm/src/eval/run-report.js +603 -0
  68. package/esm/src/modules/server/ssr-import-rewriter.d.ts +2 -26
  69. package/esm/src/modules/server/ssr-import-rewriter.d.ts.map +1 -1
  70. package/esm/src/modules/server/ssr-import-rewriter.js +1 -206
  71. package/esm/src/react/components/chat/chat/utils/message-parts.d.ts +3 -2
  72. package/esm/src/react/components/chat/chat/utils/message-parts.d.ts.map +1 -1
  73. package/esm/src/react/components/chat/chat/utils/message-parts.js +14 -2
  74. package/esm/src/routing/api/module-loader/external-import-rewriter.d.ts +2 -7
  75. package/esm/src/routing/api/module-loader/external-import-rewriter.d.ts.map +1 -1
  76. package/esm/src/routing/api/module-loader/external-import-rewriter.js +10 -209
  77. package/esm/src/server/dev-server/file-watch-setup.d.ts.map +1 -1
  78. package/esm/src/server/dev-server/file-watch-setup.js +11 -1
  79. package/esm/src/server/dev-server/middleware.d.ts.map +1 -1
  80. package/esm/src/server/dev-server/middleware.js +45 -11
  81. package/esm/src/server/handlers/dev/framework-candidates.generated.d.ts.map +1 -1
  82. package/esm/src/server/handlers/dev/framework-candidates.generated.js +7 -3
  83. package/esm/src/server/runtime-handler/index.d.ts.map +1 -1
  84. package/esm/src/server/runtime-handler/index.js +66 -145
  85. package/esm/src/server/runtime-handler/project-runtime-context.d.ts +118 -0
  86. package/esm/src/server/runtime-handler/project-runtime-context.d.ts.map +1 -0
  87. package/esm/src/server/runtime-handler/project-runtime-context.js +181 -0
  88. package/esm/src/transforms/esm/import-attributes.d.ts.map +1 -1
  89. package/esm/src/transforms/esm/import-attributes.js +2 -9
  90. package/esm/src/transforms/import-rewriter/core.d.ts +8 -0
  91. package/esm/src/transforms/import-rewriter/core.d.ts.map +1 -0
  92. package/esm/src/transforms/import-rewriter/core.js +27 -0
  93. package/esm/src/transforms/import-rewriter/import-edit.d.ts +57 -0
  94. package/esm/src/transforms/import-rewriter/import-edit.d.ts.map +1 -0
  95. package/esm/src/transforms/import-rewriter/import-edit.js +151 -0
  96. package/esm/src/transforms/import-rewriter/package-resolution.d.ts +12 -0
  97. package/esm/src/transforms/import-rewriter/package-resolution.d.ts.map +1 -0
  98. package/esm/src/transforms/import-rewriter/package-resolution.js +79 -0
  99. package/esm/src/transforms/import-rewriter/parse-cache.d.ts +4 -42
  100. package/esm/src/transforms/import-rewriter/parse-cache.d.ts.map +1 -1
  101. package/esm/src/transforms/import-rewriter/parse-cache.js +3 -134
  102. package/esm/src/transforms/import-rewriter/route-adapter.d.ts +24 -0
  103. package/esm/src/transforms/import-rewriter/route-adapter.d.ts.map +1 -0
  104. package/esm/src/transforms/import-rewriter/route-adapter.js +235 -0
  105. package/esm/src/transforms/import-rewriter/ssr-adapter.d.ts +27 -0
  106. package/esm/src/transforms/import-rewriter/ssr-adapter.d.ts.map +1 -0
  107. package/esm/src/transforms/import-rewriter/ssr-adapter.js +206 -0
  108. package/esm/src/transforms/import-rewriter/unified-rewriter.d.ts +0 -4
  109. package/esm/src/transforms/import-rewriter/unified-rewriter.d.ts.map +1 -1
  110. package/esm/src/transforms/import-rewriter/unified-rewriter.js +2 -39
  111. package/esm/src/utils/version-constant.d.ts +1 -1
  112. package/esm/src/utils/version-constant.js +1 -1
  113. package/esm/src/workflow/executor/workflow-executor.d.ts +0 -20
  114. package/esm/src/workflow/executor/workflow-executor.d.ts.map +1 -1
  115. package/esm/src/workflow/executor/workflow-executor.js +41 -320
  116. package/esm/src/workflow/runtime/approval-manager.d.ts.map +1 -1
  117. package/esm/src/workflow/runtime/approval-manager.js +20 -61
  118. package/esm/src/workflow/runtime/workflow-run-control.d.ts +95 -0
  119. package/esm/src/workflow/runtime/workflow-run-control.d.ts.map +1 -0
  120. package/esm/src/workflow/runtime/workflow-run-control.js +529 -0
  121. package/esm/src/workflow/worker/run-manager.d.ts.map +1 -1
  122. package/esm/src/workflow/worker/run-manager.js +18 -92
  123. package/esm/src/workflow/worker/shared.d.ts +1 -1
  124. package/esm/src/workflow/worker/shared.d.ts.map +1 -1
  125. package/esm/src/workflow/worker/shared.js +17 -15
  126. package/package.json +5 -5
  127. package/esm/src/agent/streaming/knowledge-source-document.d.ts.map +0 -1
@@ -2,16 +2,17 @@
2
2
  * Eval command - Discover and run eval definitions from the evals/ directory.
3
3
  */
4
4
  import * as dntShim from "../../../_dnt.shims.js";
5
- import { dirname, isAbsolute, join, relative, resolve } from "../../../deps/jsr.io/@std/path/1.1.4/mod.js";
5
+ import { dirname, isAbsolute, relative, resolve } from "../../../deps/jsr.io/@std/path/1.1.4/mod.js";
6
6
  import { isErroredToolExecutionResult, } from "../../../src/tool/index.js";
7
7
  import { orchestrateExtensions } from "../../../src/extensions/index.js";
8
8
  import { createEvalReportExporterRegistry, EvalReportExporterRegistryName, } from "../../../src/extensions/eval/index.js";
9
9
  import { createLLMProviderRegistry, LLMProviderRegistryName } from "../../../src/extensions/llm/index.js";
10
- import { compareEvalModelReports, compareEvalReports, createEvalModelComparisonMarkdown, createEvalRunId, EVAL_REPORT_SCHEMA_VERSION, exportEvalReport, resolveEvalRunProvenance, runEval, } from "../../../src/eval/index.js";
10
+ import { exportEvalReport, resolveEvalRunProvenance, runEval } from "../../../src/eval/index.js";
11
11
  import { getCurrentVeryfrontCloudContext, getVeryfrontCloudBootstrap, runWithVeryfrontCloudContextAsync, } from "../../../src/provider/index.js";
12
12
  import { applyRuntimeAuthContext } from "../../shared/runtime-auth.js";
13
13
  import { cliLogger, exitProcess, VERSION } from "../../utils/index.js";
14
14
  import { discoverProjectAgentRuntime, runWithProjectAgentRuntime, } from "../../../src/agent/project/agent-runtime.js";
15
+ import { runEvalReport } from "../../../src/eval/run-report.js";
15
16
  import { createErrorEnvelope, createSuccessEnvelope, isJsonMode, outputJson, } from "../../shared/json-output.js";
16
17
  import { withProjectSourceContext } from "../../shared/project-source-context.js";
17
18
  import { createEvalCliBuiltinExtensions } from "../../../src/extensions/builtin-extensions.js";
@@ -47,74 +48,14 @@ const MODEL_COMPARISON_METRICS = [
47
48
  ];
48
49
  const MODEL_COMPARISON_METRIC_SET = new Set(MODEL_COMPARISON_METRICS);
49
50
  const MODEL_COMPARISON_OBJECTIVE_DIRECTIONS = new Set(["minimize", "maximize"]);
50
- function xmlEscape(value) {
51
- return value
52
- .replaceAll("&", "&")
53
- .replaceAll("<", "&lt;")
54
- .replaceAll(">", "&gt;")
55
- .replaceAll('"', "&quot;")
56
- .replaceAll("'", "&apos;");
57
- }
58
51
  function stripFileProtocol(path) {
59
52
  if (!path.startsWith("file://"))
60
53
  return path;
61
54
  return decodeURIComponent(new URL(path).pathname);
62
55
  }
63
- function createEvalReportDirTimestamp(runId) {
64
- return runId.startsWith("evalrun_") ? runId.slice("evalrun_".length) : runId;
65
- }
66
- function sanitizeEvalReportDirLabel(label) {
67
- const normalized = label.startsWith("eval:") ? label.slice("eval:".length) : label;
68
- return normalized.trim().replace(/[^A-Za-z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^[-._]+|[-._]+$/g, "");
69
- }
70
- export function createDefaultEvalReportDir(runId, label) {
71
- const timestamp = createEvalReportDirTimestamp(runId);
72
- const suffix = label ? sanitizeEvalReportDirLabel(label) : "";
73
- return join(".veryfront", "evals", suffix ? `${timestamp}-${suffix}` : timestamp);
74
- }
75
- export function createEvalArtifactPaths(reportDir) {
76
- return {
77
- directory: reportDir,
78
- summary: join(reportDir, "summary.json"),
79
- results: join(reportDir, "results.jsonl"),
80
- reportMarkdown: join(reportDir, "report.md"),
81
- };
82
- }
83
- function createEvalSuiteArtifactPaths(reportDir) {
84
- return {
85
- directory: reportDir,
86
- summary: join(reportDir, "summary.json"),
87
- results: join(reportDir, "results.jsonl"),
88
- reportMarkdown: join(reportDir, "report.md"),
89
- };
90
- }
91
56
  function sortEvals(evals) {
92
57
  return [...evals].sort((left, right) => left.id.localeCompare(right.id) || left.filePath.localeCompare(right.filePath));
93
58
  }
94
- function createEvalSuiteChildDirectory(suiteDirectory, index, evalId) {
95
- return join(suiteDirectory, `${String(index + 1).padStart(3, "0")}-${sanitizeEvalReportDirLabel(evalId)}`);
96
- }
97
- function sanitizeModelIdForPath(model) {
98
- return model.trim().replace(/[^A-Za-z0-9._-]+/g, "__").replace(/^_+|_+$/g, "") || "model";
99
- }
100
- export function createEvalModelArtifactPaths(reportDir, model) {
101
- const directory = join(reportDir, "models", sanitizeModelIdForPath(model));
102
- return {
103
- directory,
104
- summary: join(directory, "summary.json"),
105
- results: join(directory, "results.jsonl"),
106
- reportMarkdown: join(directory, "report.md"),
107
- junit: join(directory, "junit.xml"),
108
- };
109
- }
110
- function createEvalModelComparisonArtifactPaths(reportDir, models) {
111
- return {
112
- directory: reportDir,
113
- comparisonJson: join(reportDir, "comparison.json"),
114
- comparisonMarkdown: join(reportDir, "comparison.md"),
115
- models: Object.fromEntries(models.map((model) => [model, createEvalModelArtifactPaths(reportDir, model)])),
116
- };
117
- }
118
59
  function displaySourcePath(filePath, projectDir) {
119
60
  const normalized = stripFileProtocol(filePath);
120
61
  if (normalized.startsWith(projectDir)) {
@@ -122,26 +63,6 @@ function displaySourcePath(filePath, projectDir) {
122
63
  }
123
64
  return normalized;
124
65
  }
125
- function blockingResults(record) {
126
- return [...(record.metrics ?? []), ...(record.checks ?? [])].filter((result) => !result.skipped && result.pass === false &&
127
- (result.severity === "gate" || result.severity === "budget"));
128
- }
129
- function skippedResults(record) {
130
- return [...(record.metrics ?? []), ...(record.checks ?? [])].filter((result) => result.skipped);
131
- }
132
- function testcaseName(record) {
133
- return `${record.exampleId}#${record.repetition}`;
134
- }
135
- function failureMessage(result) {
136
- return `${result.name} failed`;
137
- }
138
- function failureBody(result) {
139
- if (result.explanation)
140
- return result.explanation;
141
- if (result.evidence)
142
- return JSON.stringify(result.evidence);
143
- return failureMessage(result);
144
- }
145
66
  export function normalizeEvalCliId(id) {
146
67
  return id.startsWith("eval:") ? id : `eval:${id}`;
147
68
  }
@@ -168,35 +89,6 @@ export function normalizeEvalInputForAgent(input) {
168
89
  }
169
90
  return JSON.stringify(input);
170
91
  }
171
- export function summarizeReportForCli(report) {
172
- return {
173
- runId: report.runId,
174
- evalId: report.definitionId,
175
- target: report.target,
176
- records: report.summary.records,
177
- passed: report.summary.passed,
178
- failed: report.summary.failed,
179
- passRate: report.summary.passRate,
180
- metrics: report.summary.metrics,
181
- };
182
- }
183
- export function createSummaryArtifact(report, baseline) {
184
- return {
185
- kind: "eval-summary",
186
- schemaVersion: report.schemaVersion ?? EVAL_REPORT_SCHEMA_VERSION,
187
- runId: report.runId,
188
- definitionId: report.definitionId,
189
- targetKind: report.targetKind,
190
- target: report.target,
191
- ...(report.dataset ? { dataset: report.dataset } : {}),
192
- startedAt: report.startedAt,
193
- endedAt: report.endedAt,
194
- summary: report.summary,
195
- ...(report.metadata ? { metadata: report.metadata } : {}),
196
- ...(report.exports ? { exports: report.exports } : {}),
197
- ...(baseline ? { baseline } : {}),
198
- };
199
- }
200
92
  function createEvalBaselineComparisonPolicy(options) {
201
93
  return {
202
94
  ...(options.baselinePassRateDropThreshold !== undefined
@@ -490,199 +382,12 @@ export async function createResolvedEvalModelComparisonConfig(projectDir, option
490
382
  const policy = await loadEvalModelComparisonPolicy(projectDir, config.comparisonPolicy);
491
383
  return { config, policy };
492
384
  }
493
- export function createEvalModelComparisonArtifact(reports, baselineModel, policy = {}) {
494
- return compareEvalModelReports(reports, { ...policy, baselineModel });
495
- }
496
- export function createEvalModelComparisonExitCode(reports, exportRequired = false) {
497
- return reports.some((report) => createEvalExitCode(report, undefined, exportRequired) !== 0)
498
- ? 1
499
- : 0;
500
- }
501
- export function createResultsJsonl(report) {
502
- if (report.records.length === 0)
503
- return "";
504
- return `${report.records.map((record) => JSON.stringify(record)).join("\n")}\n`;
505
- }
506
- function markdownCell(value) {
507
- return value.replaceAll("|", "\\|").replaceAll("\n", " ");
508
- }
509
- function numberCell(value) {
510
- return value === undefined ? "-" : String(Math.round(value));
511
- }
512
- function decimalCell(value) {
513
- if (value === undefined)
514
- return "-";
515
- if (Number.isInteger(value))
516
- return String(value);
517
- return value.toFixed(4).replace(/0+$/, "").replace(/\.$/, "");
518
- }
519
- function percentCell(value) {
520
- return `${Math.round(value * 100)}%`;
521
- }
522
- function durationCell(valueMs) {
523
- return valueMs === undefined ? "-" : `${(valueMs / 1000).toFixed(3)}s`;
524
- }
525
- function usdCell(value) {
526
- if (value === undefined)
527
- return "-";
528
- const absolute = Math.abs(value);
529
- if (absolute >= 0.01)
530
- return `$${value.toFixed(2)}`;
531
- return `$${value.toFixed(6).replace(/0+$/, "").replace(/\.$/, "")}`;
532
- }
533
- function isBlockingEvalResultFailure(result) {
534
- return !result.skipped && result.pass === false &&
535
- (result.severity === "gate" || result.severity === "budget");
536
- }
537
- function examplePassed(record) {
538
- if (!record.completed || record.error)
539
- return false;
540
- return [...(record.metrics ?? []), ...(record.checks ?? [])].every((result) => !isBlockingEvalResultFailure(result));
541
- }
542
- function usageRows(usage) {
543
- if (!usage)
544
- return [];
545
- const rows = [
546
- ["Input tokens", numberCell(usage.inputTokens)],
547
- ["Output tokens", numberCell(usage.outputTokens)],
548
- ["Total tokens", numberCell(usage.totalTokens)],
549
- ["Billable input tokens", numberCell(usage.billableInputTokens)],
550
- ["Billable output tokens", numberCell(usage.billableOutputTokens)],
551
- ["Provider input cost USD", usdCell(usage.providerInputCostUsd)],
552
- ["Provider output cost USD", usdCell(usage.providerOutputCostUsd)],
553
- ["Provider cost USD", usdCell(usage.providerCostUsd ?? usage.costUsd)],
554
- ["Veryfront input charge USD", usdCell(usage.veryfrontInputChargeUsd)],
555
- ["Veryfront output charge USD", usdCell(usage.veryfrontOutputChargeUsd)],
556
- ["Veryfront charge USD", usdCell(usage.veryfrontChargeUsd)],
557
- ["Veryfront billed USD", usdCell(usage.veryfrontBilledUsd)],
558
- ["Cost credits", decimalCell(usage.costCredits)],
559
- ["Cost source", usage.costSource ?? "-"],
560
- ["Billing mode", usage.billingMode ?? "-"],
561
- ["Usage capture status", usage.usageCaptureStatus ?? "-"],
562
- ];
563
- return rows.filter(([, value]) => value !== "-");
564
- }
565
- /** Render a human-reviewable markdown report for a single eval run. */
566
- export function createEvalMarkdownReport(report, baseline) {
567
- const lines = [
568
- `# Eval report: ${markdownCell(report.definitionId)}`,
569
- "",
570
- `Run: \`${markdownCell(report.runId)}\``,
571
- `Target: \`${markdownCell(report.target)}\``,
572
- ...(report.metadata?.model ? [`Model: \`${markdownCell(report.metadata.model)}\``] : []),
573
- `Result: \`${report.summary.passed}/${report.summary.records} passed (${percentCell(report.summary.passRate)})\``,
574
- "",
575
- "## Metrics",
576
- "",
577
- "| Metric | Severity | Passed | Failed | Pass rate |",
578
- "| --- | --- | ---: | ---: | ---: |",
579
- ];
580
- for (const metric of report.summary.metrics) {
581
- lines.push(`| \`${markdownCell(metric.name)}\` | ${metric.severity} | ${metric.passed} | ${metric.failed} | ${percentCell(metric.passRate)} |`);
582
- }
583
- const rows = usageRows(report.summary.usage);
584
- if (rows.length > 0) {
585
- lines.push("", "## Usage", "", "| Usage | Value |", "| --- | ---: |");
586
- for (const [label, value] of rows) {
587
- lines.push(`| ${label} | ${value.startsWith("$") ? `\`${value}\`` : value} |`);
588
- }
589
- }
590
- lines.push("", "## Examples", "", "| Example | Result | Duration | Tokens | Billed USD | Credits |", "| --- | ---: | ---: | ---: | ---: | ---: |");
591
- for (const record of report.records) {
592
- lines.push(`| \`${markdownCell(record.id)}\` | ${examplePassed(record) ? "PASS" : "FAIL"} | ${durationCell(record.durationMs)} | ${numberCell(record.usage.totalTokens)} | ${record.usage.veryfrontBilledUsd === undefined
593
- ? "-"
594
- : `\`${usdCell(record.usage.veryfrontBilledUsd)}\``} | ${decimalCell(record.usage.costCredits)} |`);
595
- }
596
- if (baseline) {
597
- const direction = baseline.passRateDelta >= 0 ? "+" : "";
598
- lines.push("", "## Baseline", "", `Status: \`${baseline.regressed ? "regressed" : "ok"}\``, `Pass rate delta: \`${direction}${Math.round(baseline.passRateDelta * 100)} pp\``);
599
- if (baseline.newFailedExamples.length > 0) {
600
- lines.push(`New failed examples: ${baseline.newFailedExamples.map(markdownCell).join(", ")}`);
601
- }
602
- }
603
- if (report.exports?.length) {
604
- lines.push("", "## Exports", "");
605
- for (const result of report.exports) {
606
- lines.push(`- \`${markdownCell(result.exporterId)}\`: ${result.ok ? "ok" : `failed, ${markdownCell(result.error)}`}`);
607
- }
608
- }
609
- lines.push("");
610
- return lines.join("\n");
611
- }
612
- export function createJunitXml(report) {
613
- const skipped = report.records.reduce((count, record) => count + skippedResults(record).length, 0);
614
- const lines = [
615
- '<?xml version="1.0" encoding="UTF-8"?>',
616
- `<testsuite name="${xmlEscape(report.definitionId)}" tests="${report.summary.records}" failures="${report.summary.failed}" skipped="${skipped}">`,
617
- ];
618
- for (const record of report.records) {
619
- const failures = blockingResults(record);
620
- const skips = skippedResults(record);
621
- const attrs = `classname="${xmlEscape(report.definitionId)}" name="${xmlEscape(testcaseName(record))}" time="${(record.durationMs / 1000).toFixed(3)}"`;
622
- if (failures.length === 0 && skips.length === 0) {
623
- lines.push(` <testcase ${attrs} />`);
624
- continue;
625
- }
626
- lines.push(` <testcase ${attrs}>`);
627
- for (const failure of failures) {
628
- lines.push(` <failure message="${xmlEscape(failureMessage(failure))}">${xmlEscape(failureBody(failure))}</failure>`);
629
- }
630
- for (const skip of skips) {
631
- lines.push(` <skipped message="${xmlEscape(skip.explanation ?? `${skip.name} skipped`)}" />`);
632
- }
633
- lines.push(" </testcase>");
634
- }
635
- lines.push("</testsuite>");
636
- return `${lines.join("\n")}\n`;
637
- }
638
- function createEvalSuiteResultsJsonl(summary) {
639
- return summary.results.map((result) => JSON.stringify(result)).join("\n") +
640
- (summary.results.length > 0 ? "\n" : "");
641
- }
642
- export function createEvalSuiteJunitXml(summary) {
643
- const lines = [
644
- '<?xml version="1.0" encoding="UTF-8"?>',
645
- `<testsuites tests="${summary.total}" failures="${summary.failed}" skipped="0">`,
646
- ` <testsuite name="veryfront eval suite" tests="${summary.total}" failures="${summary.failed}" skipped="0">`,
647
- ];
648
- for (const result of summary.results) {
649
- const attrs = `classname="eval" name="${xmlEscape(result.id)}"`;
650
- if (result.status === "passed") {
651
- lines.push(` <testcase ${attrs} />`);
652
- continue;
653
- }
654
- const message = result.error ??
655
- (result.summary?.failed
656
- ? `${result.summary.failed} record(s) failed`
657
- : "A required eval export failed.");
658
- lines.push(` <testcase ${attrs}>`);
659
- lines.push(` <failure message="${xmlEscape(message)}">${xmlEscape(message)}</failure>`);
660
- lines.push(" </testcase>");
661
- }
662
- lines.push(" </testsuite>");
663
- lines.push("</testsuites>");
664
- return `${lines.join("\n")}\n`;
665
- }
666
385
  async function writeTextFileEnsuringDir(path, content) {
667
386
  const dir = dirname(path);
668
387
  if (dir && dir !== ".")
669
388
  await dntShim.Deno.mkdir(dir, { recursive: true });
670
389
  await dntShim.Deno.writeTextFile(path, content);
671
390
  }
672
- export async function writeEvalArtifacts(report, paths, baseline) {
673
- await dntShim.Deno.mkdir(paths.directory, { recursive: true });
674
- await dntShim.Deno.writeTextFile(paths.summary, JSON.stringify(createSummaryArtifact(report, baseline), null, 2));
675
- await dntShim.Deno.writeTextFile(paths.results, createResultsJsonl(report));
676
- await dntShim.Deno.writeTextFile(paths.reportMarkdown, createEvalMarkdownReport(report, baseline));
677
- }
678
- async function readEvalReport(path) {
679
- return JSON.parse(await dntShim.Deno.readTextFile(path));
680
- }
681
- export function createEvalExitCode(report, baseline, exportRequired = false) {
682
- const exportFailed = exportRequired &&
683
- (!(report.exports?.length) || report.exports.some((result) => !result.ok));
684
- return report.summary.failed === 0 && baseline?.regressed !== true && !exportFailed ? 0 : 1;
685
- }
686
391
  function resolveAgentTargetId(target) {
687
392
  return target.startsWith("agent:") ? target.slice("agent:".length) : target;
688
393
  }
@@ -894,38 +599,6 @@ function unsupportedEvalSuiteOption(options) {
894
599
  return undefined;
895
600
  return `${flags.join(", ")} require a named eval. Run \`veryfront eval <eval-id>\`.`;
896
601
  }
897
- function createEvalSuiteSummary(runId, startedAt, results) {
898
- const passed = results.filter((result) => result.status === "passed").length;
899
- return {
900
- kind: "eval-suite-summary",
901
- runId,
902
- startedAt: startedAt.toISOString(),
903
- endedAt: new Date().toISOString(),
904
- total: results.length,
905
- passed,
906
- failed: results.length - passed,
907
- results,
908
- };
909
- }
910
- function createEvalSuiteMarkdown(summary) {
911
- const rows = summary.results.map((result) => `| ${markdownCell(result.id)} | ${result.status} | ${result.summary ? `${result.summary.passed}/${result.summary.records}` : "n/a"} | ${result.error ? markdownCell(result.error) : ""} |`);
912
- return [
913
- "# Eval suite report",
914
- "",
915
- `Run: \`${markdownCell(summary.runId)}\``,
916
- `Result: \`${summary.passed}/${summary.total} passed\``,
917
- "",
918
- "| Eval | Status | Records | Error |",
919
- "| --- | --- | --- | --- |",
920
- ...rows,
921
- "",
922
- ].join("\n");
923
- }
924
- async function writeEvalSuiteArtifacts(summary, artifacts) {
925
- await writeTextFileEnsuringDir(artifacts.summary, JSON.stringify(summary, null, 2));
926
- await writeTextFileEnsuringDir(artifacts.results, createEvalSuiteResultsJsonl(summary));
927
- await writeTextFileEnsuringDir(artifacts.reportMarkdown, createEvalSuiteMarkdown(summary));
928
- }
929
602
  function getDiscoveredEvals(runtime) {
930
603
  return [...runtime.evals.entries()].map(([id, definition]) => {
931
604
  if (!definition.source) {
@@ -963,7 +636,7 @@ function printReport(report, baseline) {
963
636
  }
964
637
  }
965
638
  }
966
- export function createEvalCliExportConfig(evalItem, options, projectDir, artifactPaths, registry, config) {
639
+ function createEvalCliBaseExportConfig(options, registry, config) {
967
640
  const exporterIds = resolveEvalExporterIds(options);
968
641
  if (exporterIds.length === 0)
969
642
  return undefined;
@@ -973,12 +646,7 @@ export function createEvalCliExportConfig(evalItem, options, projectDir, artifac
973
646
  exporterIds,
974
647
  required: resolveEvalExportRequired(options),
975
648
  context: {
976
- evalId: evalItem.definition.id,
977
649
  ...(projectReference ? { projectReference } : {}),
978
- sourcePath: displaySourcePath(evalItem.filePath, projectDir),
979
- reportPath: options.report ?? artifactPaths.summary,
980
- tags: evalItem.definition.tags,
981
- metadata: evalItem.definition.metadata,
982
650
  redaction: resolveEvalExportRedactionFromEnv(),
983
651
  },
984
652
  };
@@ -1095,75 +763,6 @@ function resolveEvalModelComparisonConfig(options) {
1095
763
  models: uniqueValues([baselineModel, ...candidateModels]),
1096
764
  };
1097
765
  }
1098
- function createAgentAdapterForModel(agent, options, model) {
1099
- return createAgentAdapter(agent, { ...options, model });
1100
- }
1101
- async function writeEvalModelComparisonArtifacts(comparison, paths) {
1102
- await dntShim.Deno.mkdir(paths.directory, { recursive: true });
1103
- await dntShim.Deno.writeTextFile(paths.comparisonJson, JSON.stringify(comparison, null, 2));
1104
- await dntShim.Deno.writeTextFile(paths.comparisonMarkdown, createEvalModelComparisonMarkdown(comparison));
1105
- }
1106
- async function runEvalModelComparison(input) {
1107
- const runId = createEvalRunId();
1108
- const reportDir = input.options.reportDir ??
1109
- createDefaultEvalReportDir(runId, input.evalItem.id);
1110
- const paths = createEvalModelComparisonArtifactPaths(reportDir, input.config.models);
1111
- const provenance = await resolveEvalRunProvenance({
1112
- projectDir: input.projectDir,
1113
- frameworkVersion: VERSION,
1114
- });
1115
- const reports = [];
1116
- for (const model of input.config.models) {
1117
- const modelPaths = paths.models[model];
1118
- const modelOptions = { ...input.options, model, report: undefined };
1119
- const modelRunId = `${runId}_${sanitizeModelIdForPath(model)}`;
1120
- const exportConfig = createEvalCliExportConfig(input.evalItem, modelOptions, input.projectDir, modelPaths, input.exporterRegistry, input.projectReference ? { projectSlug: input.projectReference } : undefined);
1121
- const finalizedReport = await runEvalWithGatewayBillingGroup(modelRunId, () => runEval(input.evalItem.definition, {
1122
- baseDir: input.options.datasetBase ?? input.projectDir,
1123
- runId: modelRunId,
1124
- adapters: {
1125
- agent: createAgentAdapterForModel(input.agent, input.options, model),
1126
- },
1127
- metadata: {
1128
- model,
1129
- provenance,
1130
- },
1131
- }));
1132
- const report = await exportEvalReportForCli(finalizedReport, exportConfig);
1133
- reports.push(report);
1134
- await writeEvalArtifacts(report, modelPaths);
1135
- await writeTextFileEnsuringDir(modelPaths.junit, createJunitXml(report));
1136
- }
1137
- const comparison = createEvalModelComparisonArtifact(reports, input.config.baselineModel, input.policy);
1138
- await writeEvalModelComparisonArtifacts(comparison, paths);
1139
- if (input.options.report) {
1140
- await writeTextFileEnsuringDir(input.options.report, JSON.stringify(comparison, null, 2));
1141
- }
1142
- if (isJsonMode()) {
1143
- await outputJson(createSuccessEnvelope("eval", {
1144
- reports,
1145
- comparison,
1146
- artifacts: paths,
1147
- }));
1148
- }
1149
- else {
1150
- for (const report of reports) {
1151
- const model = report.metadata?.model ?? report.runId;
1152
- cliLogger.info(`Model: ${model}`);
1153
- printReport(report);
1154
- }
1155
- cliLogger.info(`Recommendation: ${comparison.recommendation.decision}${comparison.recommendation.model ? ` (${comparison.recommendation.model})` : ""}`);
1156
- for (const reason of comparison.recommendation.reasons) {
1157
- cliLogger.info(` - ${reason}`);
1158
- }
1159
- cliLogger.info(`Report directory: ${paths.directory}`);
1160
- cliLogger.info(`Comparison: ${paths.comparisonJson}`);
1161
- cliLogger.info(`Comparison markdown: ${paths.comparisonMarkdown}`);
1162
- if (input.options.report)
1163
- cliLogger.info(`Report: ${input.options.report}`);
1164
- }
1165
- return createEvalModelComparisonExitCode(reports, resolveEvalExportRequired(input.options));
1166
- }
1167
766
  async function outputEvalNotFound(id, evals) {
1168
767
  if (isJsonMode()) {
1169
768
  await outputJson(createErrorEnvelope("eval", {
@@ -1225,88 +824,57 @@ async function outputEvalUsageError(message) {
1225
824
  }
1226
825
  return 2;
1227
826
  }
1228
- async function runEvalSuite(input) {
1229
- const runId = createEvalRunId();
1230
- const startedAt = new Date();
1231
- const artifacts = createEvalSuiteArtifactPaths(input.options.reportDir ?? createDefaultEvalReportDir(runId));
1232
- const provenance = await resolveEvalRunProvenance({
1233
- projectDir: input.projectDir,
1234
- frameworkVersion: VERSION,
1235
- });
1236
- const results = [];
1237
- for (const [index, evalItem] of sortEvals(input.evals).entries()) {
1238
- const evalArtifacts = createEvalArtifactPaths(createEvalSuiteChildDirectory(artifacts.directory, index, evalItem.id));
1239
- try {
1240
- const agentId = evalItem.definition.targetKind === "agent"
1241
- ? resolveAgentTargetId(evalItem.definition.target)
1242
- : undefined;
1243
- const toolId = evalItem.definition.targetKind === "tool"
1244
- ? resolveToolTargetId(evalItem.definition.target)
1245
- : undefined;
1246
- const agent = agentId ? input.projectRuntime.agents.get(agentId) : undefined;
1247
- const tool = toolId ? input.projectRuntime.tools.get(toolId) : undefined;
1248
- if (agentId && !agent)
1249
- throw new Error(`Agent "${agentId}" not found for eval target.`);
1250
- if (toolId && !tool)
1251
- throw new Error(`Tool "${toolId}" not found for eval target.`);
1252
- const evalRunId = `${runId}_${String(index + 1).padStart(3, "0")}`;
1253
- const exportConfig = createEvalCliExportConfig(evalItem, input.options, input.projectDir, evalArtifacts, input.exporterRegistry, input.config);
1254
- const finalizedReport = await runEvalWithGatewayBillingGroup(evalRunId, () => runEval(evalItem.definition, {
1255
- baseDir: input.options.datasetBase ?? input.projectDir,
1256
- runId: evalRunId,
1257
- adapters: evalItem.definition.targetKind === "tool"
1258
- ? { tool: createToolAdapter(tool, createEvalToolExecutionContext(input.config)) }
1259
- : { agent: createAgentAdapter(agent, input.options) },
1260
- metadata: { provenance },
1261
- }));
1262
- const report = await exportEvalReportForCli(finalizedReport, exportConfig);
1263
- await writeEvalArtifacts(report, evalArtifacts);
1264
- const status = createEvalExitCode(report, undefined, exportConfig?.required) === 0
1265
- ? "passed"
1266
- : "failed";
1267
- results.push({
1268
- id: evalItem.id,
1269
- name: evalItem.name,
1270
- target: evalItem.definition.target,
1271
- status,
1272
- artifacts: evalArtifacts,
1273
- summary: summarizeReportForCli(report),
1274
- });
1275
- if (!isJsonMode()) {
1276
- printReport(report);
1277
- cliLogger.info(`Report directory: ${evalArtifacts.directory}`);
1278
- }
1279
- }
1280
- catch (error) {
1281
- const message = error instanceof Error ? error.message : String(error);
1282
- results.push({
1283
- id: evalItem.id,
1284
- name: evalItem.name,
1285
- target: evalItem.definition.target,
1286
- status: "error",
1287
- artifacts: evalArtifacts,
1288
- error: message,
1289
- });
1290
- if (!isJsonMode())
1291
- cliLogger.error(`Eval ${evalItem.id}: ${message}`);
1292
- }
1293
- }
1294
- const summary = createEvalSuiteSummary(runId, startedAt, results);
1295
- await writeEvalSuiteArtifacts(summary, artifacts);
1296
- if (input.options.junit) {
1297
- await writeTextFileEnsuringDir(input.options.junit, createEvalSuiteJunitXml(summary));
1298
- }
1299
- if (isJsonMode()) {
1300
- await outputJson(createSuccessEnvelope("eval", { suite: summary, artifacts }));
1301
- }
1302
- else {
1303
- cliLogger.info(`Eval suite: ${summary.passed}/${summary.total} passed`);
1304
- cliLogger.info(`Report directory: ${artifacts.directory}`);
1305
- cliLogger.info(`Suite report: ${artifacts.reportMarkdown}`);
1306
- if (input.options.junit)
1307
- cliLogger.info(`JUnit: ${input.options.junit}`);
1308
- }
1309
- return summary.failed === 0 ? 0 : 1;
827
+ function createEvalReportCommandAdapters(input) {
828
+ return {
829
+ targets: {
830
+ runEval: (evalItem, options) => runEval(evalItem.definition, {
831
+ baseDir: options.baseDir,
832
+ runId: options.runId,
833
+ adapters: options.targetKind === "tool"
834
+ ? { tool: options.targetAdapter }
835
+ : { agent: options.targetAdapter },
836
+ metadata: options.metadata,
837
+ }),
838
+ resolveTarget: (evalItem) => {
839
+ const agentId = evalItem.definition.targetKind === "agent"
840
+ ? resolveAgentTargetId(evalItem.definition.target)
841
+ : undefined;
842
+ const toolId = evalItem.definition.targetKind === "tool"
843
+ ? resolveToolTargetId(evalItem.definition.target)
844
+ : undefined;
845
+ const agent = agentId ? input.projectRuntime.agents.get(agentId) : undefined;
846
+ const tool = toolId ? input.projectRuntime.tools.get(toolId) : undefined;
847
+ if (agentId && !agent)
848
+ throw new Error(`Agent "${agentId}" not found for eval target.`);
849
+ if (toolId && !tool)
850
+ throw new Error(`Tool "${toolId}" not found for eval target.`);
851
+ return {
852
+ targetKind: evalItem.definition.targetKind,
853
+ target: evalItem.definition.target,
854
+ targetAdapter: evalItem.definition.targetKind === "tool"
855
+ ? createToolAdapter(tool, createEvalToolExecutionContext(input.config))
856
+ : createAgentAdapter(agent, input.options),
857
+ };
858
+ },
859
+ createModelTargetAdapter: (model) => {
860
+ const options = { ...input.options, model };
861
+ if (!input.modelComparisonAgent) {
862
+ throw new Error("Model comparison agent is not configured.");
863
+ }
864
+ return createAgentAdapter(input.modelComparisonAgent, options);
865
+ },
866
+ },
867
+ artifacts: {
868
+ readTextFile: (path) => dntShim.Deno.readTextFile(path),
869
+ writeTextFileEnsuringDir,
870
+ },
871
+ billing: {
872
+ runWithGatewayBillingGroup: runEvalWithGatewayBillingGroup,
873
+ },
874
+ exporters: {
875
+ exportReport: (report, config) => exportEvalReportForCli(report, config),
876
+ },
877
+ };
1310
878
  }
1311
879
  export async function runEvalCommand(options, dependencies = {}) {
1312
880
  const projectDir = options.projectDir ?? dntShim.Deno.cwd();
@@ -1375,14 +943,52 @@ export async function runEvalCommand(options, dependencies = {}) {
1375
943
  const selectedExporterIds = resolveEvalExporterIds(options);
1376
944
  const extensionSetup = await setupEvalCliExtensions(projectDir, config, selectedExporterIds);
1377
945
  try {
1378
- return await runWithProjectAgentRuntime(projectRuntime, () => runEvalSuite({
1379
- evals,
1380
- options,
1381
- projectDir,
1382
- projectRuntime,
1383
- config,
1384
- exporterRegistry: extensionSetup.exporterRegistry,
1385
- }));
946
+ return await runWithProjectAgentRuntime(projectRuntime, async () => {
947
+ const outcome = await runEvalReport({
948
+ kind: "suite",
949
+ projectDir,
950
+ frameworkVersion: VERSION,
951
+ ...(options.datasetBase ? { datasetBase: options.datasetBase } : {}),
952
+ ...(options.reportDir ? { reportDir: options.reportDir } : {}),
953
+ ...(options.junit ? { junit: options.junit } : {}),
954
+ exportConfig: createEvalCliBaseExportConfig(options, extensionSetup.exporterRegistry, config),
955
+ provenance: await resolveEvalRunProvenance({
956
+ projectDir,
957
+ frameworkVersion: VERSION,
958
+ }),
959
+ evalItems: evals,
960
+ }, createEvalReportCommandAdapters({
961
+ options,
962
+ config,
963
+ projectRuntime,
964
+ }));
965
+ if (outcome.kind !== "suite") {
966
+ throw new Error(`Unexpected eval report outcome: ${outcome.kind}`);
967
+ }
968
+ if (isJsonMode()) {
969
+ await outputJson(createSuccessEnvelope("eval", {
970
+ suite: outcome.suite,
971
+ artifacts: outcome.artifacts,
972
+ }));
973
+ }
974
+ else {
975
+ for (const child of outcome.outputHints.children ?? []) {
976
+ if (child.kind === "report") {
977
+ printReport(child.report);
978
+ cliLogger.info(`Report directory: ${child.reportDirectory}`);
979
+ }
980
+ else {
981
+ cliLogger.error(`Eval ${child.evalId}: ${child.error}`);
982
+ }
983
+ }
984
+ cliLogger.info(`Eval suite: ${outcome.suite.passed}/${outcome.suite.total} passed`);
985
+ cliLogger.info(`Report directory: ${outcome.outputHints.reportDirectory}`);
986
+ cliLogger.info(`Suite report: ${outcome.outputHints.reportMarkdown}`);
987
+ if (outcome.outputHints.junit)
988
+ cliLogger.info(`JUnit: ${outcome.outputHints.junit}`);
989
+ }
990
+ return outcome.exitCode;
991
+ });
1386
992
  }
1387
993
  finally {
1388
994
  await extensionSetup.loader.teardownAll();
@@ -1426,72 +1032,123 @@ export async function runEvalCommand(options, dependencies = {}) {
1426
1032
  return await outputToolNotFound(toolId);
1427
1033
  }
1428
1034
  if (modelComparisonConfig) {
1429
- return await runWithProjectAgentRuntime(projectRuntime, () => runEvalModelComparison({
1430
- evalItem,
1431
- agent: agent,
1432
- options,
1433
- projectDir,
1434
- config: modelComparisonConfig.config,
1435
- policy: modelComparisonConfig.policy,
1436
- exporterRegistry: extensionSetup.exporterRegistry,
1437
- projectReference: resolveEvalRuntimeProjectSlug(config),
1438
- }));
1035
+ return await runWithProjectAgentRuntime(projectRuntime, async () => {
1036
+ const outcome = await runEvalReport({
1037
+ kind: "model-comparison",
1038
+ projectDir,
1039
+ frameworkVersion: VERSION,
1040
+ ...(options.datasetBase ? { datasetBase: options.datasetBase } : {}),
1041
+ ...(options.reportDir ? { reportDir: options.reportDir } : {}),
1042
+ ...(options.report ? { report: options.report } : {}),
1043
+ exportConfig: createEvalCliBaseExportConfig(options, extensionSetup.exporterRegistry, config),
1044
+ provenance: await resolveEvalRunProvenance({
1045
+ projectDir,
1046
+ frameworkVersion: VERSION,
1047
+ }),
1048
+ evalItem,
1049
+ target: evalItem.definition.target,
1050
+ baselineModel: modelComparisonConfig.config.baselineModel,
1051
+ candidateModels: modelComparisonConfig.config.candidateModels,
1052
+ comparisonPolicy: modelComparisonConfig.policy,
1053
+ ...(options.maxOutputTokens !== undefined
1054
+ ? { maxOutputTokens: options.maxOutputTokens }
1055
+ : {}),
1056
+ }, createEvalReportCommandAdapters({
1057
+ options,
1058
+ config,
1059
+ projectRuntime,
1060
+ modelComparisonAgent: agent,
1061
+ }));
1062
+ if (outcome.kind !== "model-comparison") {
1063
+ throw new Error(`Unexpected eval report outcome: ${outcome.kind}`);
1064
+ }
1065
+ if (isJsonMode()) {
1066
+ await outputJson(createSuccessEnvelope("eval", {
1067
+ reports: outcome.reports,
1068
+ comparison: outcome.comparison,
1069
+ artifacts: outcome.artifacts,
1070
+ }));
1071
+ }
1072
+ else {
1073
+ for (const model of outcome.outputHints.models ?? []) {
1074
+ cliLogger.info(`Model: ${model.model}`);
1075
+ printReport(model.report);
1076
+ }
1077
+ const recommendation = outcome.comparison.recommendation;
1078
+ cliLogger.info(`Recommendation: ${recommendation.decision}${recommendation.model ? ` (${recommendation.model})` : ""}`);
1079
+ for (const reason of recommendation.reasons) {
1080
+ cliLogger.info(` - ${reason}`);
1081
+ }
1082
+ cliLogger.info(`Report directory: ${outcome.outputHints.reportDirectory}`);
1083
+ if (outcome.outputHints.comparisonJson) {
1084
+ cliLogger.info(`Comparison: ${outcome.outputHints.comparisonJson}`);
1085
+ }
1086
+ if (outcome.outputHints.comparisonMarkdown) {
1087
+ cliLogger.info(`Comparison markdown: ${outcome.outputHints.comparisonMarkdown}`);
1088
+ }
1089
+ if (outcome.outputHints.report) {
1090
+ cliLogger.info(`Report: ${outcome.outputHints.report}`);
1091
+ }
1092
+ }
1093
+ return outcome.exitCode;
1094
+ });
1439
1095
  }
1440
- const runId = createEvalRunId();
1441
- const artifactPaths = createEvalArtifactPaths(options.reportDir ?? createDefaultEvalReportDir(runId, evalItem.id));
1442
- const provenance = await resolveEvalRunProvenance({
1096
+ const targetAdapter = evalItem.definition.targetKind === "tool"
1097
+ ? createToolAdapter(tool, createEvalToolExecutionContext(config))
1098
+ : createAgentAdapter(agent, options);
1099
+ const outcome = await runWithProjectAgentRuntime(projectRuntime, async () => await runEvalReport({
1100
+ kind: "single",
1443
1101
  projectDir,
1444
1102
  frameworkVersion: VERSION,
1445
- });
1446
- const billingGroupId = options.model
1447
- ? `${runId}_${sanitizeModelIdForPath(options.model)}`
1448
- : runId;
1449
- const exportConfig = createEvalCliExportConfig(evalItem, options, projectDir, artifactPaths, extensionSetup.exporterRegistry, config);
1450
- const finalizedReport = await runWithProjectAgentRuntime(projectRuntime, () => runEvalWithGatewayBillingGroup(billingGroupId, () => runEval(evalItem.definition, {
1451
- baseDir: options.datasetBase ?? projectDir,
1452
- runId,
1453
- adapters: evalItem.definition.targetKind === "tool"
1454
- ? { tool: createToolAdapter(tool, createEvalToolExecutionContext(config)) }
1455
- : { agent: createAgentAdapter(agent, options) },
1456
- metadata: {
1457
- provenance,
1458
- ...(options.model ? { model: options.model } : {}),
1459
- },
1103
+ ...(options.datasetBase ? { datasetBase: options.datasetBase } : {}),
1104
+ ...(options.reportDir ? { reportDir: options.reportDir } : {}),
1105
+ ...(options.report ? { report: options.report } : {}),
1106
+ ...(options.junit ? { junit: options.junit } : {}),
1107
+ ...(options.baseline ? { baseline: options.baseline } : {}),
1108
+ ...(options.writeBaseline ? { writeBaseline: options.writeBaseline } : {}),
1109
+ baselinePolicy: createEvalBaselineComparisonPolicy(options),
1110
+ exportConfig: createEvalCliBaseExportConfig(options, extensionSetup.exporterRegistry, config),
1111
+ provenance: await resolveEvalRunProvenance({
1112
+ projectDir,
1113
+ frameworkVersion: VERSION,
1114
+ }),
1115
+ evalItem,
1116
+ targetKind: evalItem.definition.targetKind,
1117
+ target: evalItem.definition.target,
1118
+ targetAdapter,
1119
+ ...(options.model ? { selectedModel: options.model } : {}),
1120
+ ...(options.maxOutputTokens !== undefined
1121
+ ? { maxOutputTokens: options.maxOutputTokens }
1122
+ : {}),
1123
+ }, createEvalReportCommandAdapters({
1124
+ options,
1125
+ config,
1126
+ projectRuntime,
1460
1127
  })));
1461
- const report = await exportEvalReportForCli(finalizedReport, exportConfig);
1462
- const baseline = options.baseline
1463
- ? compareEvalReports(report, await readEvalReport(options.baseline), createEvalBaselineComparisonPolicy(options))
1464
- : undefined;
1465
- await writeEvalArtifacts(report, artifactPaths, baseline);
1466
- if (options.report) {
1467
- await writeTextFileEnsuringDir(options.report, JSON.stringify(report, null, 2));
1468
- }
1469
- if (options.junit) {
1470
- await writeTextFileEnsuringDir(options.junit, createJunitXml(report));
1471
- }
1472
- if (options.writeBaseline) {
1473
- await writeTextFileEnsuringDir(options.writeBaseline, JSON.stringify(report, null, 2));
1128
+ if (outcome.kind !== "single") {
1129
+ throw new Error(`Unexpected eval report outcome: ${outcome.kind}`);
1474
1130
  }
1475
1131
  if (isJsonMode()) {
1476
1132
  await outputJson(createSuccessEnvelope("eval", {
1477
- report,
1478
- summary: summarizeReportForCli(report),
1479
- baseline,
1480
- artifacts: artifactPaths,
1133
+ report: outcome.report,
1134
+ summary: outcome.summary,
1135
+ baseline: outcome.baseline,
1136
+ artifacts: outcome.artifacts,
1481
1137
  }));
1482
1138
  }
1483
1139
  else {
1484
- printReport(report, baseline);
1485
- cliLogger.info(`Report directory: ${artifactPaths.directory}`);
1486
- cliLogger.info(`Report markdown: ${artifactPaths.reportMarkdown}`);
1487
- if (options.report)
1488
- cliLogger.info(`Report: ${options.report}`);
1489
- if (options.junit)
1490
- cliLogger.info(`JUnit: ${options.junit}`);
1491
- if (options.writeBaseline)
1492
- cliLogger.info(`Baseline written: ${options.writeBaseline}`);
1140
+ printReport(outcome.report, outcome.baseline);
1141
+ cliLogger.info(`Report directory: ${outcome.outputHints.reportDirectory}`);
1142
+ cliLogger.info(`Report markdown: ${outcome.outputHints.reportMarkdown}`);
1143
+ if (outcome.outputHints.report)
1144
+ cliLogger.info(`Report: ${outcome.outputHints.report}`);
1145
+ if (outcome.outputHints.junit)
1146
+ cliLogger.info(`JUnit: ${outcome.outputHints.junit}`);
1147
+ if (outcome.outputHints.baselineWritten) {
1148
+ cliLogger.info(`Baseline written: ${outcome.outputHints.baselineWritten}`);
1149
+ }
1493
1150
  }
1494
- return createEvalExitCode(report, baseline, exportConfig?.required);
1151
+ return outcome.exitCode;
1495
1152
  }
1496
1153
  finally {
1497
1154
  await extensionSetup.loader.teardownAll();