Spaces:
Running
Running
File size: 3,210 Bytes
056cefb 37a21b0 056cefb 3039836 4a9ccd7 056cefb 37a21b0 056cefb 3039836 4a9ccd7 3039836 056cefb 37a21b0 056cefb 37a21b0 056cefb 37a21b0 056cefb | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | """Typer CLI entrypoint: `python -m src.cli review "topic"`."""
from __future__ import annotations
import os
import time
import typer
from rich.console import Console
from rich.markdown import Markdown
from rich.panel import Panel
from . import utils
app = typer.Typer(add_completion=False, help="Personal research agent.")
console = Console()
@app.command()
def review(
topic: str = typer.Argument(..., help="Research topic to review."),
max_papers: int = typer.Option(15, "--max-papers", help="Papers in final review."),
depth: int = typer.Option(1, "--depth", help="Iterative gap-filling passes."),
model: str = typer.Option("", "--model", help="Override the model for this run."),
since: int = typer.Option(0, "--since", help="Only papers from this year onward."),
comprehensive: bool = typer.Option(
False, "--comprehensive", help="Longer, more detailed review (costs more)."
),
) -> None:
"""Search arXiv, read papers, and write a structured literature review."""
from .agent import build_graph
console.print(Panel.fit(f"[bold]Research topic:[/bold] {topic}", border_style="cyan"))
def progress(msg: str) -> None:
console.print(f"[dim]路[/dim] {msg}")
meter: dict = {}
t0 = time.time()
try:
agent = build_graph(
max_papers=max_papers,
progress=progress,
meter=meter,
model=model or None,
year_min=since,
style="comprehensive" if comprehensive else "concise",
)
result = agent.invoke(
{
"topic": topic,
"papers": [],
"review": "",
"gaps": [],
"depth": max(1, depth),
"iteration": 0,
}
)
except Exception as err:
console.print(f"[bold red]Run failed:[/bold red] {err}")
raise typer.Exit(code=1)
papers = result["papers"]
review_text = result["review"]
if not review_text.strip():
console.print("[bold red]No review produced.[/bold red]")
raise typer.Exit(code=1)
os.makedirs("reviews", exist_ok=True)
path = os.path.join("reviews", f"{utils.sanitize_filename(topic)}.md")
with open(path, "w", encoding="utf-8") as fh:
fh.write(f"# Literature Review: {topic}\n\n")
fh.write(review_text.rstrip() + "\n")
# Validation warnings (non-fatal).
report = utils.validate_review(review_text, papers[:max_papers])
if not report["valid"]:
console.print("[yellow]Validation warnings:[/yellow]")
for issue in report["issues"]:
console.print(f" [yellow]![/yellow] {issue}")
stats = {"elapsed_s": round(time.time() - t0, 1), **utils.cost_report(meter)}
console.print(Panel.fit(f"[green]Saved:[/green] {path}", border_style="green"))
console.print(
f"[dim]{stats['elapsed_s']}s 路 {len(papers)} papers 路 {stats['llm_calls']} "
f"LLM calls 路 {stats['in_tok']}+{stats['out_tok']} tok 路 "
f"~${stats['est_cost_usd']}[/dim]"
)
console.print(Markdown(review_text[:500] + ("..." if len(review_text) > 500 else "")))
if __name__ == "__main__":
app()
|