feat(reports): add overall scores to strategy cards and average row in technical report

Why:
- Users need to see each strategy's overall score at a glance in the report
- Technical report should show average metrics across all strategies

Changes:
- Each strategy card now shows its overall score below the metric bars
- Technical report aggregate table includes an AVERAGE row across all strategies

Impact:
- Managerial report: strategy cards now show overall score
- Technical report: aggregate table has a new AVERAGE row at the bottom
This commit is contained in:
2026-07-29 17:54:26 +03:30
parent a277672444
commit b10a5706f4

View File

@@ -633,6 +633,9 @@ def _build_strategy_cards(rankings: list, aggregate: dict) -> str:
{_metric_bar("Similarity", m.get("avg_answer_similarity", 0), "accent")}
{_metric_bar("Faithfulness", m.get("avg_faithfulness", 0), "violet")}
{_metric_bar("No Hallucination", (1 - m.get("hallucination_rate", 0)) * 10, "teal" if m.get("hallucination_rate", 0) <= 0.1 else "rose")}
<div style="margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border); font-family: {FONT_DATA}; font-size: 13px; color: var(--text-muted);">
Overall: <span style="color: var(--accent); font-weight: 600;">{overall:.2f}</span>
</div>
</div>"""
return cards
@@ -690,7 +693,7 @@ def _generate_decision_insights(rankings: list, aggregate: dict) -> str:
def _build_technical_aggregate_rows(rankings: list, aggregate: dict) -> str:
"""Build technical aggregate table rows."""
"""Build technical aggregate table rows with an average row at the end."""
rows = ""
for rank, (strategy, overall) in enumerate(rankings, 1):
m = aggregate.get(strategy, {})
@@ -705,6 +708,28 @@ def _build_technical_aggregate_rows(rankings: list, aggregate: dict) -> str:
<td>{m.get('failed_questions', 0)}</td>
<td><strong style="font-family: {FONT_DATA};">{overall:.2f}</strong></td>
</tr>"""
# Average row across all strategies
n = len(rankings)
if n > 0:
avg_ctx = sum(aggregate.get(s, {}).get('avg_context_relevance', 0) for s, _ in rankings) / n
avg_sim = sum(aggregate.get(s, {}).get('avg_answer_similarity', 0) for s, _ in rankings) / n
avg_faith = sum(aggregate.get(s, {}).get('avg_faithfulness', 0) for s, _ in rankings) / n
avg_hall = sum(aggregate.get(s, {}).get('hallucination_rate', 0) for s, _ in rankings) / n
avg_total = sum(aggregate.get(s, {}).get('total_questions', 0) for s, _ in rankings) / n
avg_failed = sum(aggregate.get(s, {}).get('failed_questions', 0) for s, _ in rankings) / n
avg_overall = sum(overall for _, overall in rankings) / n
rows += f"""
<tr style="background: var(--surface); border-top: 2px solid var(--border);">
<td><strong style="color: var(--accent);">AVERAGE</strong></td>
<td>{_pill(avg_ctx)}</td>
<td>{_pill(avg_sim)}</td>
<td>{_pill(avg_faith)}</td>
<td>{_hall_pill(avg_hall)}</td>
<td>{avg_total:.0f}</td>
<td>{avg_failed:.0f}</td>
<td><strong style="font-family: {FONT_DATA}; color: var(--accent);">{avg_overall:.2f}</strong></td>
</tr>"""
return rows