"""Shared evaluation utilities to reduce code duplication.""" from __future__ import annotations import json import logging from collections.abc import Callable from datetime import datetime from pathlib import Path from typing import Any logger = logging.getLogger(__name__) def calculate_pass_rate(results: dict) -> float: """Calculate pass rate from results dictionary.""" total = results.get("total", 0) if total == 0: return 0.0 return results.get("passed", 0) / total def format_summary(results: dict) -> str: """Format results as summary string.""" total = results.get("total", 0) passed = results.get("passed", 0) pass_rate = calculate_pass_rate(results) return f"{passed}/{total} passed ({pass_rate:.1%})" def collect_test_cases( mode_data: dict, subgroups: list[str] | None = None, sample_size: int | None = None ) -> list[dict]: """Collect test cases from mode data, optionally sampling from subgroups.""" test_cases = [] if subgroups: for subgroup_name in subgroups: if subgroup_name in mode_data and isinstance(mode_data[subgroup_name], list): subgroup_cases = mode_data[subgroup_name] sampled = subgroup_cases[:sample_size] if sample_size else subgroup_cases test_cases.extend(sampled) logger.info(f" {subgroup_name}: {len(sampled)} test cases") else: for subgroup_name, subgroup_cases in mode_data.items(): if isinstance(subgroup_cases, list): sampled = subgroup_cases[:sample_size] if sample_size else subgroup_cases test_cases.extend(sampled) logger.info(f" {subgroup_name}: {len(sampled)} test cases") return test_cases def run_evaluation_mode( mode_name: str, test_cases: list[dict], handler_func: Callable[[dict], str], ) -> dict[str, str]: """Run evaluation for a specific mode.""" logger.info("=" * 80) logger.info(mode_name.upper()) logger.info("=" * 80) logger.info(f"\nTotal test cases: {len(test_cases)}\n") responses = {} for i, test_case in enumerate(test_cases, 1): test_id = test_case["test_id"] logger.info(f"[{i}/{len(test_cases)}] {test_id}") responses[test_id] = handler_func(test_case["input"]) logger.info(f"\nāœ“ {mode_name} evaluation complete") return responses def save_json_responses(output_dir: Path, responses: dict[str, Any], filename: str) -> None: """Save responses to JSON file.""" output_dir.mkdir(parents=True, exist_ok=True) with open(output_dir / filename, "w", encoding="utf-8") as f: json.dump(responses, f, indent=2, ensure_ascii=False) def save_evaluation_results( output_dir: Path, results_data: dict[str, Any], markdown_report: str, ) -> None: """Save evaluation results and markdown report.""" output_dir.mkdir(parents=True, exist_ok=True) with open(output_dir / "results.json", "w") as f: json.dump(results_data, f, indent=2) with open(output_dir / "evaluation_report.md", "w") as f: f.write(markdown_report) logger.info(f"āœ“ Results saved to {output_dir}") def format_mode_section(mode_name: str, results: dict) -> str: """Format a single mode section for markdown report.""" total = results.get("total", 0) passed = results.get("passed", 0) failed = results.get("failed", 0) pass_rate = calculate_pass_rate(results) return f"""### {mode_name} - **Total Test Cases:** {total} - **Passed:** {passed} - **Failed:** {failed} - **Pass Rate:** {pass_rate:.1%}""" def generate_overall_summary(all_results: list[dict]) -> tuple[int, int, float]: """Calculate overall metrics from list of results.""" overall_total = sum(r.get("total", 0) for r in all_results) overall_passed = sum(r.get("passed", 0) for r in all_results) overall_pass_rate = overall_passed / overall_total if overall_total > 0 else 0.0 return overall_total, overall_passed, overall_pass_rate def create_metadata(model_name: str, agent_name: str) -> dict[str, str]: """Create metadata dictionary for results.""" return { "model": model_name, "evaluation_date": datetime.now().isoformat(), "agent": agent_name, }