Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions openagent_eval/reports/html.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from __future__ import annotations

from datetime import datetime, timezone
from datetime import UTC, datetime
from pathlib import Path
from typing import Any

Expand Down Expand Up @@ -58,7 +58,8 @@ def _load_template(self) -> str:

raise FileNotFoundError(
f"HTML template not found at {builtin_path}. "
"Install jinja2 and ensure the template file exists."
"Ensure the package is installed correctly and includes "
"openagent_eval/reports/templates/report.html, or provide a valid custom template path."
)

def _render_template(self, context: dict[str, Any]) -> str:
Expand Down Expand Up @@ -92,12 +93,17 @@ def generate(self, report: Any) -> str:
result = report.result
summary = report.summary
config = report.config
now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")
now = datetime.now(UTC).strftime("%Y-%m-%d %H:%M:%S UTC")

# Compute overall score
metrics = summary.get("metrics_summary", {})
numeric_metric_values = [
value for value in metrics.values()
if isinstance(value, (int, float)) and not isinstance(value, bool)
]
overall_score = (
sum(metrics.values()) / len(metrics) if metrics else None
sum(numeric_metric_values) / len(numeric_metric_values)
if numeric_metric_values else None
)

# Format results for template
Expand Down Expand Up @@ -142,8 +148,10 @@ def generate_to_file(self, report: Any, output_path: Path | str) -> Path:
Path to the written file.
"""
path = Path(output_path)
if not str(path).endswith(".html"):
if path.suffix == "":
path = path / "report.html"
elif path.suffix.lower() != ".html":
path = path.with_suffix(".html")
path = self._ensure_output_dir(path)
content = self.generate(report)
path.write_text(content, encoding="utf-8")
Expand Down
48 changes: 48 additions & 0 deletions tests/unit/test_reports/test_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,17 @@ def test_generate_to_file_adds_extension(
assert result_path.exists()
assert str(result_path).endswith(".html")

def test_generate_to_file_replaces_non_html_extension(
self, evaluation_report: Any, tmp_path: Path
) -> None:
"""generate_to_file() replaces non-.html suffixes with .html."""
report = HTMLReport()
output_path = tmp_path / "report.txt"
result_path = report.generate_to_file(evaluation_report, output_path)

assert result_path == tmp_path / "report.html"
assert result_path.exists()

def test_generate_to_file_creates_directory(
self, evaluation_report: Any, tmp_path: Path
) -> None:
Expand Down Expand Up @@ -142,3 +153,40 @@ def test_css_styles_present(self, evaluation_report: Any) -> None:
result = report.generate(evaluation_report)
assert "<style>" in result
assert "var(--primary)" in result

def test_generate_ignores_non_numeric_metrics_for_overall_score(
self, evaluation_report: Any
) -> None:
"""generate() computes overall score from numeric metric values only."""
report = HTMLReport()
evaluation_report.summary["metrics_summary"] = {
"precision": 0.8,
"count": 2,
"ignored_bool": True,
"ignored_text": "0.9",
"ignored_dict": {"value": 1.0},
}
captured_context: dict[str, Any] = {}

def _capture_context(context: dict[str, Any]) -> str:
captured_context.update(context)
return "ok"

report._render_template = _capture_context
report.generate(evaluation_report)

assert captured_context["overall_score"] == pytest.approx(1.4)

def test_load_template_missing_builtin_has_installation_guidance(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
"""_load_template() raises a packaging-focused guidance message."""
report = HTMLReport()
monkeypatch.setattr(Path, "exists", lambda _: False)

with pytest.raises(FileNotFoundError) as exc_info:
report._load_template()

message = str(exc_info.value)
assert "openagent_eval/reports/templates/report.html" in message
assert "Install jinja2" not in message
Loading