"""Pretty printing for the Lint pass output. Called from scripts/10_run_lint.py after a run, and from scripts/11_show_contradictions.py for ad-hoc inspection of the state file without re-running classification. """ from __future__ import annotations from rich.console import Console from rich.panel import Panel from rich.table import Table from lint._state import LintState, LintStats, PairVerdict console = Console() def render_stats(stats: LintStats) -> None: t = Table(title="Lint pass stats", show_lines=False) t.add_column("Metric", style="bold") t.add_column("Count", justify="right") t.add_row("atoms seen", str(stats.atoms_seen)) t.add_row("candidate pairs", str(stats.candidates_generated)) t.add_row("[dim]skipped (cached)[/dim]", str(stats.pairs_skipped_cached)) t.add_row("pairs evaluated", str(stats.pairs_evaluated)) t.add_row("[red]CONTRADICTORY[/red]", str(stats.contradictory)) t.add_row("[yellow]EQUIVALENT[/yellow]", str(stats.equivalent)) t.add_row("[dim]INCOMPARABLE[/dim]", str(stats.incomparable)) t.add_row("errors", str(stats.errors)) console.print(t) def render_contradictions( state: LintState, *, top_n: int = 10, min_confidence: float = 0.7, ) -> None: contras = [v for v in state.all_contradictions if v.confidence >= min_confidence] contras.sort(key=lambda v: (-v.confidence, -v.similarity)) header = ( f"[bold red]CONTRADICTIONS[/bold red] " f"({len(contras)} with confidence >= {min_confidence:.2f}, showing top {top_n})" ) console.print(Panel.fit(header)) if not contras: console.print( "[dim]no contradictions above threshold. " "The current Wikipedia-only corpus is self-consistent by design, " "which is expected for a single well-curated source. " "Add diverse sources to surface real disagreement.[/dim]" ) return for i, v in enumerate(contras[:top_n], 1): console.print() console.print( f"[bold]#{i}[/bold] " f"confidence=[red]{v.confidence:.2f}[/red] " f"(embed sim {v.similarity:.2f})" ) console.print(f" [green]A:[/green] {v.atom_a_claim[:240]}") console.print(f" [dim]→ {v.atom_a_url}[/dim]") console.print(f" [red]B:[/red] {v.atom_b_claim[:240]}") console.print(f" [dim]→ {v.atom_b_url}[/dim]") def render_equivalents( state: LintState, *, top_n: int = 10, min_confidence: float = 0.85 ) -> None: equivs = [v for v in state.all_equivalents if v.confidence >= min_confidence] equivs.sort(key=lambda v: (-v.confidence, -v.similarity)) header = ( f"[bold yellow]PARAPHRASE CLUSTERS[/bold yellow] " f"({len(equivs)} with confidence >= {min_confidence:.2f}, showing top {top_n})" ) console.print() console.print(Panel.fit(header)) if not equivs: console.print("[dim]no paraphrase clusters above threshold[/dim]") return for i, v in enumerate(equivs[:top_n], 1): console.print() console.print(f"[bold]#{i}[/bold] confidence=[yellow]{v.confidence:.2f}[/yellow]") console.print(f" · {v.atom_a_claim[:200]}") console.print(f" · {v.atom_b_claim[:200]}")