From 11ee69491d1189bc298d6d63de4ef7b4e02e1128 Mon Sep 17 00:00:00 2001 From: Akmal Date: Sat, 9 May 2026 16:57:48 +0500 Subject: [PATCH 1/7] refactor: extract markdown value formatting --- python/hyperion_diagnostics/report.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/python/hyperion_diagnostics/report.py b/python/hyperion_diagnostics/report.py index d7d5963..9d3b18e 100644 --- a/python/hyperion_diagnostics/report.py +++ b/python/hyperion_diagnostics/report.py @@ -14,6 +14,12 @@ import numpy as np +def _format_markdown_value(value: Any) -> str: + if isinstance(value, float): + return f"{value:.4f}" + return str(value) + + @dataclass class DiagnosticsReport: """Структурированный отчёт: модель, метод, конфиг, summary, convergence, варнинги, выводы.""" @@ -66,20 +72,13 @@ def to_markdown(self) -> str: headers = list(stats.keys()) lines.append("| Parameter | " + " | ".join(headers) + " |") lines.append("|" + "|".join(["---"] * (len(headers) + 1)) + "|") - vals = " | ".join( - f"{stats.get(h, 'N/A'):.4f}" if isinstance(stats.get(h), float) - else str(stats.get(h, 'N/A')) - for h in headers - ) + vals = " | ".join(_format_markdown_value(stats.get(h, "N/A")) for h in headers) lines.append(f"| {param} | {vals} |") lines.append("") lines.append("## Convergence Metrics") for k, v in self.convergence_metrics.items(): - if isinstance(v, float): - lines.append(f"- **{k}:** {v:.4f}") - else: - lines.append(f"- **{k}:** {v}") + lines.append(f"- **{k}:** {_format_markdown_value(v)}") if self.warnings: lines.append("") From ede50f75f1ce1ac29b18749040eaf0fd23992d6f Mon Sep 17 00:00:00 2001 From: Akmal Date: Sat, 9 May 2026 16:58:00 +0500 Subject: [PATCH 2/7] refactor: extract report configuration rendering --- python/hyperion_diagnostics/report.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/python/hyperion_diagnostics/report.py b/python/hyperion_diagnostics/report.py index 9d3b18e..6e51a22 100644 --- a/python/hyperion_diagnostics/report.py +++ b/python/hyperion_diagnostics/report.py @@ -20,6 +20,13 @@ def _format_markdown_value(value: Any) -> str: return str(value) +def _render_configuration(config: dict[str, Any]) -> list[str]: + lines = ["## Configuration"] + for key, value in config.items(): + lines.append(f"- **{key}:** {value}") + return lines + + @dataclass class DiagnosticsReport: """Структурированный отчёт: модель, метод, конфиг, summary, convergence, варнинги, выводы.""" @@ -56,10 +63,8 @@ def to_markdown(self) -> str: f"**Method:** {self.inference_method}", f"**Timestamp:** {self.timestamp}", "", - "## Configuration", + *_render_configuration(self.config), ] - for k, v in self.config.items(): - lines.append(f"- **{k}:** {v}") lines.append("") lines.append("## Parameter Summary") From 0ca3813c88005e0f8036d9c5dcb1935be1973632 Mon Sep 17 00:00:00 2001 From: Akmal Date: Sat, 9 May 2026 16:58:15 +0500 Subject: [PATCH 3/7] refactor: extract summary table rendering --- python/hyperion_diagnostics/report.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/python/hyperion_diagnostics/report.py b/python/hyperion_diagnostics/report.py index 6e51a22..b4bfc70 100644 --- a/python/hyperion_diagnostics/report.py +++ b/python/hyperion_diagnostics/report.py @@ -27,6 +27,19 @@ def _render_configuration(config: dict[str, Any]) -> list[str]: return lines +def _render_summary_table(summary_stats: dict[str, dict[str, float]]) -> list[str]: + lines = ["## Parameter Summary", ""] + headers = None + for param, stats in summary_stats.items(): + if headers is None: + headers = list(stats.keys()) + lines.append("| Parameter | " + " | ".join(headers) + " |") + lines.append("|" + "|".join(["---"] * (len(headers) + 1)) + "|") + vals = " | ".join(_format_markdown_value(stats.get(h, "N/A")) for h in headers) + lines.append(f"| {param} | {vals} |") + return lines + + @dataclass class DiagnosticsReport: """Структурированный отчёт: модель, метод, конфиг, summary, convergence, варнинги, выводы.""" @@ -67,18 +80,7 @@ def to_markdown(self) -> str: ] lines.append("") - lines.append("## Parameter Summary") - lines.append("") - - if self.summary_stats: - headers = None - for param, stats in self.summary_stats.items(): - if headers is None: - headers = list(stats.keys()) - lines.append("| Parameter | " + " | ".join(headers) + " |") - lines.append("|" + "|".join(["---"] * (len(headers) + 1)) + "|") - vals = " | ".join(_format_markdown_value(stats.get(h, "N/A")) for h in headers) - lines.append(f"| {param} | {vals} |") + lines.extend(_render_summary_table(self.summary_stats)) lines.append("") lines.append("## Convergence Metrics") From b35e904a0f7c18a1ad8af9536779a53a37855ed2 Mon Sep 17 00:00:00 2001 From: Akmal Date: Sat, 9 May 2026 16:58:26 +0500 Subject: [PATCH 4/7] refactor: extract convergence metric rendering --- python/hyperion_diagnostics/report.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/python/hyperion_diagnostics/report.py b/python/hyperion_diagnostics/report.py index b4bfc70..34bc2e1 100644 --- a/python/hyperion_diagnostics/report.py +++ b/python/hyperion_diagnostics/report.py @@ -40,6 +40,13 @@ def _render_summary_table(summary_stats: dict[str, dict[str, float]]) -> list[st return lines +def _render_convergence_metrics(metrics: dict[str, float]) -> list[str]: + lines = ["## Convergence Metrics"] + for key, value in metrics.items(): + lines.append(f"- **{key}:** {_format_markdown_value(value)}") + return lines + + @dataclass class DiagnosticsReport: """Структурированный отчёт: модель, метод, конфиг, summary, convergence, варнинги, выводы.""" @@ -83,9 +90,7 @@ def to_markdown(self) -> str: lines.extend(_render_summary_table(self.summary_stats)) lines.append("") - lines.append("## Convergence Metrics") - for k, v in self.convergence_metrics.items(): - lines.append(f"- **{k}:** {_format_markdown_value(v)}") + lines.extend(_render_convergence_metrics(self.convergence_metrics)) if self.warnings: lines.append("") From b8dfdbb4967284ee4992721bd0b8f40556ea8a28 Mon Sep 17 00:00:00 2001 From: Akmal Date: Sat, 9 May 2026 16:58:39 +0500 Subject: [PATCH 5/7] refactor: extract report bullet sections --- python/hyperion_diagnostics/report.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/python/hyperion_diagnostics/report.py b/python/hyperion_diagnostics/report.py index 34bc2e1..3462f43 100644 --- a/python/hyperion_diagnostics/report.py +++ b/python/hyperion_diagnostics/report.py @@ -47,6 +47,15 @@ def _render_convergence_metrics(metrics: dict[str, float]) -> list[str]: return lines +def _render_bullet_section(title: str, items: list[str], *, prefix: str = "") -> list[str]: + if not items: + return [] + lines = ["", f"## {title}"] + for item in items: + lines.append(f"- {prefix}{item}") + return lines + + @dataclass class DiagnosticsReport: """Структурированный отчёт: модель, метод, конфиг, summary, convergence, варнинги, выводы.""" @@ -92,17 +101,8 @@ def to_markdown(self) -> str: lines.append("") lines.extend(_render_convergence_metrics(self.convergence_metrics)) - if self.warnings: - lines.append("") - lines.append("## Warnings") - for w in self.warnings: - lines.append(f"- ⚠ {w}") - - if self.conclusions: - lines.append("") - lines.append("## Conclusions") - for c in self.conclusions: - lines.append(f"- {c}") + lines.extend(_render_bullet_section("Warnings", self.warnings, prefix="⚠ ")) + lines.extend(_render_bullet_section("Conclusions", self.conclusions)) return "\n".join(lines) From 0cd9f2ea4c901af2da6a049cba81f9ee16194c27 Mon Sep 17 00:00:00 2001 From: Akmal Date: Sat, 9 May 2026 16:58:54 +0500 Subject: [PATCH 6/7] test: cover diagnostics report section rendering --- .../test_diagnostics/test_report_rendering.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 python/tests/test_diagnostics/test_report_rendering.py diff --git a/python/tests/test_diagnostics/test_report_rendering.py b/python/tests/test_diagnostics/test_report_rendering.py new file mode 100644 index 0000000..9b8455f --- /dev/null +++ b/python/tests/test_diagnostics/test_report_rendering.py @@ -0,0 +1,36 @@ +from __future__ import annotations + +from hyperion_diagnostics.report import DiagnosticsReport + + +def test_report_markdown_preserves_section_order() -> None: + report = DiagnosticsReport( + model_name="ordered_model", + inference_method="nuts", + timestamp="2026-05-09T10:00:00", + config={"num_samples": 100}, + summary_stats={"mu": {"mean": 0.0, "std": 1.0}}, + convergence_metrics={"mu/ess": 120.0}, + warnings=["Low BFMI"], + conclusions=["Review diagnostics."], + ) + + markdown = report.to_markdown() + + assert markdown.index("## Configuration") < markdown.index("## Parameter Summary") + assert markdown.index("## Parameter Summary") < markdown.index("## Convergence Metrics") + assert markdown.index("## Convergence Metrics") < markdown.index("## Warnings") + assert markdown.index("## Warnings") < markdown.index("## Conclusions") + + +def test_report_markdown_omits_empty_optional_sections() -> None: + report = DiagnosticsReport( + model_name="minimal_model", + inference_method="laplace", + timestamp="2026-05-09T10:00:00", + ) + + markdown = report.to_markdown() + + assert "## Warnings" not in markdown + assert "## Conclusions" not in markdown From 7f8be0828d2b54e5bbe5ce6454cceb4a98be1110 Mon Sep 17 00:00:00 2001 From: Akmal Date: Sat, 9 May 2026 16:59:06 +0500 Subject: [PATCH 7/7] test: cover diagnostics markdown value formatting --- .../test_diagnostics/test_report_rendering.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/python/tests/test_diagnostics/test_report_rendering.py b/python/tests/test_diagnostics/test_report_rendering.py index 9b8455f..fe9a2b1 100644 --- a/python/tests/test_diagnostics/test_report_rendering.py +++ b/python/tests/test_diagnostics/test_report_rendering.py @@ -34,3 +34,19 @@ def test_report_markdown_omits_empty_optional_sections() -> None: assert "## Warnings" not in markdown assert "## Conclusions" not in markdown + + +def test_report_markdown_formats_numeric_and_text_values() -> None: + report = DiagnosticsReport( + model_name="format_model", + inference_method="smc", + timestamp="2026-05-09T10:00:00", + summary_stats={"theta": {"mean": 1.23456, "status": "ok"}}, + convergence_metrics={"particles": 256, "ess": 101.12345}, + ) + + markdown = report.to_markdown() + + assert "| theta | 1.2346 | ok |" in markdown + assert "- **particles:** 256" in markdown + assert "- **ess:** 101.1235" in markdown