Skip to content

Commit 1b41470

Browse files
Tomkessclaude
andcommitted
chore(gooddata-eval): reconcile the five remaining open eval PRs
Rebuilt on master now that #1789 (loop exit_reason) and #1801 (anomaly detection) have landed. Carries #1797, #1798, #1816, #1831, #1839. Three reconciliations the individual PRs cannot make on their own: - Dispatch registration in cli/agentic_runner.py is additive across the evaluator PRs; each pair conflicts and each resolution is the union. - #1789's exit_reason/turns_used are now on master in the same item-detail dicts #1816 moves behind a per-run builder. Both land: the per-run fields go into _run_detail, and max_iterations stays at the item level because it is the same for every run. - #1816's structural guard requires every multi-run evaluator to call build_failed_runs. dashboard_summary, forecasting and anomaly_detection postdate it and had no attachment point, so each grew one. anomaly detection is now ON MASTER without it, so this gap stops being a merge artefact the day #1816 lands. Also supplies summary_input to #1816's failed-runs report test, which otherwise fails a dashboard-summary item on a missing fixture field before its evaluator is reached. 1524 passed, 1 skipped. ruff clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent f12d0ea commit 1b41470

4 files changed

Lines changed: 49 additions & 3 deletions

File tree

‎packages/gooddata-eval/src/gooddata_eval/core/agentic/anomaly_detection.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from dataclasses import dataclass, field
3232
from typing import Any
3333

34+
from gooddata_eval.core.agentic._failed_runs import build_failed_runs
3435
from gooddata_eval.core.agentic._gate import (
3536
DEFAULT_GATE,
3637
EvalGate,
@@ -611,6 +612,13 @@ def _write_scores(ctx: RunTraceContext) -> None:
611612
best = summary.best
612613
ev = best.evaluation
613614
detail = _detail(best)
615+
# Same predicate runs_passed is taken over, so an item's failed_runs and its counts
616+
# cannot disagree about which runs failed.
617+
failed_runs = build_failed_runs(
618+
summary.run_results,
619+
passed=lambda r: r.evaluation.strict_pass,
620+
detail=_detail,
621+
)
614622
runs_passed = sum(1 for r in summary.run_results if r.evaluation.strict_pass)
615623

616624
if not gate_passed(gate, pass_at_k=summary.pass_at_k, pass_power_k=summary.pass_power_k):
@@ -630,6 +638,7 @@ def _write_scores(ctx: RunTraceContext) -> None:
630638
exc.detail = detail
631639
exc.runs_passed = runs_passed
632640
exc.runs_effective = len(summary.run_results)
641+
exc.failed_runs = failed_runs
633642
raise exc
634643

635644
return AgenticEvalOutcome(
@@ -639,4 +648,5 @@ def _write_scores(ctx: RunTraceContext) -> None:
639648
conversation_id=best.conversation_id,
640649
response_id=best.response_id,
641650
detail=detail,
651+
failed_runs=failed_runs,
642652
)

‎packages/gooddata-eval/src/gooddata_eval/core/agentic/dashboard_summary.py‎

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import httpx
3131
from gooddata_sdk import GoodDataSdk
3232

33+
from gooddata_eval.core.agentic._failed_runs import build_failed_runs
3334
from gooddata_eval.core.agentic._trace_linker import (
3435
RunIdentity,
3536
RunTraceContext,
@@ -406,8 +407,14 @@ def run_agentic_dashboard_summary(
406407
)
407408

408409

409-
def _detail(summary: AgenticDashboardSummarySummary) -> dict:
410-
best = summary.best
410+
def _run_detail(run: DashboardSummaryRunResult) -> dict:
411+
"""The diagnostic fields for ONE run, shared by the best run and every failing one.
412+
413+
Extracted so a failing run is described by exactly the same keys as the winning run --
414+
for this kind that means the criteria breakdown and how much of the dashboard the run
415+
managed to execute, which is what tells a bad summary apart from a half-loaded one.
416+
"""
417+
best = run
411418
detail = dict(best.evaluation.detail)
412419
# How much of the dashboard the summary actually covered. A summary graded against a
413420
# rubric that mentions a widget which never executed fails for a reason that has
@@ -420,6 +427,10 @@ def _detail(summary: AgenticDashboardSummarySummary) -> dict:
420427
return detail
421428

422429

430+
def _detail(summary: AgenticDashboardSummarySummary) -> dict:
431+
return _run_detail(summary.best)
432+
433+
423434
class DashboardSummaryAssertionError(AgenticAssertionError):
424435
"""Raised when an agentic dashboard-summary evaluation fails."""
425436

@@ -517,6 +528,9 @@ def _write_scores(ctx: RunTraceContext) -> None:
517528
for run in summary.run_results:
518529
timings = timings + run.timings
519530
runs_passed = sum(1 for r in summary.run_results if r.passed)
531+
# Same predicate runs_passed is taken over, so an item's failed_runs and its counts
532+
# cannot disagree about which runs failed.
533+
failed_runs = build_failed_runs(summary.run_results, passed=lambda r: r.passed, detail=_run_detail)
520534

521535
if not summary.pass_at_k:
522536
error = DashboardSummaryAssertionError(
@@ -531,6 +545,7 @@ def _write_scores(ctx: RunTraceContext) -> None:
531545
error.timings = timings
532546
error.runs_passed = runs_passed
533547
error.runs_effective = len(summary.run_results)
548+
error.failed_runs = failed_runs
534549
raise error
535550

536551
return AgenticEvalOutcome(
@@ -541,4 +556,5 @@ def _write_scores(ctx: RunTraceContext) -> None:
541556
timings=timings,
542557
runs_passed=runs_passed,
543558
runs_effective=len(summary.run_results),
559+
failed_runs=failed_runs,
544560
)

‎packages/gooddata-eval/src/gooddata_eval/core/agentic/forecasting.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from dataclasses import dataclass, field
2323
from typing import Any
2424

25+
from gooddata_eval.core.agentic._failed_runs import build_failed_runs
2526
from gooddata_eval.core.agentic._gate import (
2627
DEFAULT_GATE,
2728
EvalGate,
@@ -588,6 +589,13 @@ def _write_scores(ctx: RunTraceContext) -> None:
588589
best = summary.best
589590
ev = best.evaluation
590591
detail = _detail(best)
592+
# Same predicate runs_passed is taken over, so an item's failed_runs and its counts
593+
# cannot disagree about which runs failed.
594+
failed_runs = build_failed_runs(
595+
summary.run_results,
596+
passed=lambda r: r.evaluation.strict_pass,
597+
detail=_detail,
598+
)
591599
runs_passed = sum(1 for r in summary.run_results if r.evaluation.strict_pass)
592600

593601
if not gate_passed(gate, pass_at_k=summary.pass_at_k, pass_power_k=summary.pass_power_k):
@@ -608,6 +616,7 @@ def _write_scores(ctx: RunTraceContext) -> None:
608616
exc.detail = detail
609617
exc.runs_passed = runs_passed
610618
exc.runs_effective = len(summary.run_results)
619+
exc.failed_runs = failed_runs
611620
raise exc
612621

613622
return AgenticEvalOutcome(
@@ -617,4 +626,5 @@ def _write_scores(ctx: RunTraceContext) -> None:
617626
conversation_id=best.conversation_id,
618627
response_id=best.response_id,
619628
detail=detail,
629+
failed_runs=failed_runs,
620630
)

‎packages/gooddata-eval/tests/test_agentic_runner.py‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -849,7 +849,17 @@ def test_failed_runs_reaches_the_report_for_every_kind(kind, expected_output, ta
849849
field empty on every item (confirmed live: 135 agentic_guardrail results, all with
850850
`failed_runs: []`, including 16 items that passed 1 of 3 runs). A kind whose evaluator
851851
stops attaching them fails here rather than quietly reporting nothing."""
852-
item = DatasetItem(id="q1", dataset_name="ds", test_kind=kind, question="q", expected_output=expected_output)
852+
item = DatasetItem(
853+
id="q1",
854+
dataset_name="ds",
855+
test_kind=kind,
856+
question="q",
857+
expected_output=expected_output,
858+
# Dispatch rejects a dashboard-summary item without it before the evaluator is ever
859+
# called, so the item would fail for a missing fixture field rather than for the
860+
# thing under test.
861+
summary_input={"dashboard_id": "dash-1"} if kind in _SUMMARY_INPUT_KINDS else None,
862+
)
853863
canned = AgenticEvalOutcome(reasoning_steps=["x"], detail={"k": "v"}, failed_runs=[_A_FAILED_RUN])
854864
with patch(f"gooddata_eval.cli.agentic_runner.{target}", return_value=canned):
855865
report = run_agentic_items([item], host="http://host", token="tok", workspace_id="ws1", run_ts="2026-01-01")

0 commit comments

Comments
 (0)