RSCE / src /presentation /cli_report.py
LP1205's picture
Deploy RSCE backend to Hugging Face Space
5e952bb
Raw
History Blame Contribute Delete
6.94 kB
from rich.console import Console
from rich.table import Table
from rich.panel import Panel
from rich.text import Text
from rich.box import ROUNDED, DOUBLE
from src.models.report import SynthesisReport
console = Console()
def format_claim_details(claim) -> Text:
"""Format claim details with colors for CLI panels."""
text = Text()
text.append("Claim: ", style="bold cyan")
text.append(f"{claim.text}\n", style="white")
authors_str = ", ".join(claim.authors) if claim.authors else "Unknown"
text.append("Source: ", style="bold green")
text.append(f"{authors_str} ({claim.year}) [PMID: {claim.paper_id}]\n", style="dim")
text.append("Population: ", style="bold yellow")
text.append(f"{claim.population}\n", style="yellow")
text.append("Context: ", style="bold magenta")
text.append(f"{claim.context}\n", style="magenta")
text.append("Quote Anchor: ", style="bold blue")
text.append(f'"{claim.quote_anchor}"', style="italic dim blue")
return text
def print_cli_report(
report: SynthesisReport,
query: str,
time_elapsed: float = 0.0,
cost_estimate: float = 0.0,
):
"""Render a beautiful terminal report of the synthesis results using rich."""
# 1. Header Title Panel
title_text = Text()
title_text.append("RESEARCH SYNTHESIS & CONTRADICTION ENGINE\n", style="bold magenta")
title_text.append("Query: ", style="bold cyan")
title_text.append(query, style="italic cyan")
console.print(Panel(
title_text,
border_style="magenta",
box=DOUBLE,
padding=(1, 2)
))
console.print()
# 2. Stats Panel
stats_table = Table(box=ROUNDED, border_style="cyan")
stats_table.add_column("Ingested Papers", justify="center", style="bold green")
stats_table.add_column("Extracted Claims", justify="center", style="bold green")
stats_table.add_column("Detected Contradictions", justify="center", style="bold yellow")
stats_table.add_column("Genuine Contradictions", justify="center", style="bold green")
stats_table.add_column("Time Elapsed", justify="center", style="bold blue")
stats_table.add_column("Estimated Cost", justify="center", style="bold red")
genuine_count = sum(1 for c in report.contradictions if c.is_genuine)
cost = cost_estimate if cost_estimate > 0 else report.metadata.get("cost_estimate", 0.0)
duration = time_elapsed if time_elapsed > 0 else report.metadata.get("time_elapsed", 0.0)
stats_table.add_row(
str(report.total_papers),
str(report.total_claims),
str(len(report.contradictions)),
str(genuine_count),
f"{duration:.2f}s",
f"${cost:.4f}"
)
console.print(Panel(stats_table, title="[bold cyan]Pipeline Execution Statistics[/bold cyan]", border_style="cyan"))
console.print()
# 3. Grounded Summary
summary_text = report.summary if report.summary else "No grounded summary generated yet. (Phase 3 Synthesis)"
console.print(Panel(
summary_text,
title="[bold green]Grounded Synthesis Narrative[/bold green]",
border_style="green",
box=ROUNDED,
padding=(1, 1)
))
console.print()
# 4. Contradiction Summary Table
if not report.contradictions:
console.print("[yellow]No contradictions were found in the analyzed papers.[/yellow]")
return
table = Table(title="[bold yellow]Ranked Contradictions Overview[/bold yellow]", box=ROUNDED, border_style="yellow")
table.add_column("#", justify="center", style="dim")
table.add_column("Claim A", ratio=3, overflow="ellipsis")
table.add_column("Claim B", ratio=3, overflow="ellipsis")
table.add_column("Score", justify="center")
table.add_column("Type", justify="center")
table.add_column("Genuineness", justify="center")
for i, c in enumerate(report.contradictions, 1):
gen_text = "[green]Genuine[/green]" if c.is_genuine else "[yellow]Scope Mismatch[/yellow]"
table.add_row(
str(i),
c.claim_a.text,
c.claim_b.text,
f"{c.contradiction_score:.2f}",
c.contradiction_type.name,
gen_text
)
console.print(table)
console.print()
# 5. Detailed Contradiction Displays (Side-by-Side)
console.print("[bold cyan]=========================== Contradiction Drill-Down ===========================[/bold cyan]")
console.print()
for i, c in enumerate(report.contradictions[:5], 1):
# Header for this contradiction
gen_badge = "[bold green]GENUINE CONTRADICTION[/bold green]" if c.is_genuine else "[bold yellow]SCOPE MISMATCH / PSEUDO-CONTRADICTION[/bold yellow]"
console.print(f"[bold cyan]Contradiction #{i}:[/bold cyan] {gen_badge} | Type: [magenta]{c.contradiction_type.name}[/magenta] | Score: [bold green]{c.contradiction_score:.2f}[/bold green]")
console.print("-" * 100)
# Format both claims
claim_a_text = format_claim_details(c.claim_a)
claim_b_text = format_claim_details(c.claim_b)
# Put them in panels
panel_a = Panel(claim_a_text, title=f"Claim A [dim]({c.claim_a.authors[0] if c.claim_a.authors else 'Unknown'}, {c.claim_a.year})[/dim]", border_style="cyan")
panel_b = Panel(claim_b_text, title=f"Claim B [dim]({c.claim_b.authors[0] if c.claim_b.authors else 'Unknown'}, {c.claim_b.year})[/dim]", border_style="cyan")
# Display side-by-side using an borderless table (works better than Columns on narrow screens)
layout_table = Table.grid(expand=True)
layout_table.add_column(ratio=1)
layout_table.add_column(width=2) # spacer
layout_table.add_column(ratio=1)
layout_table.add_row(panel_a, "", panel_b)
console.print(layout_table)
# Judge analysis explanation panel
judge_explanation = Text()
judge_explanation.append("Analysis: ", style="bold green")
judge_explanation.append(f"{c.explanation}\n", style="white")
if c.scope_note:
judge_explanation.append("Scope Difference: ", style="bold yellow")
judge_explanation.append(f"{c.scope_note}", style="white")
console.print(Panel(
judge_explanation,
title="[bold green]LLM Judge Analysis[/bold green]",
border_style="green",
box=ROUNDED
))
console.print()
if len(report.contradictions) > 5:
console.print(f"[dim]... and {len(report.contradictions) - 5} more contradictions. See exported JSON report for full details.[/dim]\n")
# 6. Footer
console.print(Panel(
Text("Data sources: PubMed Central & NCBI E-utilities | Model: Gemini API & Sentence Transformers\n"
"Local cross-encoder: cross-encoder/nli-deberta-v3-large", justify="center", style="dim"),
border_style="magenta",
box=ROUNDED
))