"""Gradio Server app: custom HTML frontend + Gradio-managed API endpoint. The input UI lives in `index.html` and talks to the `generate_bulletin` endpoint below via `@gradio/client`. Report generation logic is unchanged from the original Blocks app. """ import os from pathlib import Path from fastapi.responses import HTMLResponse from gradio import Server from analyze import build_report, compute_stats, digest_all, get_client from dataset import fetch_sessions, list_sessions from extract import events_to_transcript, truncate_transcript from render import bulletin_html, empty_bulletin_html DEFAULT_REPO = "julien-c/pi-sessions" app = Server() _INDEX = Path(__file__).parent / "index.html" def _owner_from(repo_id: str) -> str: return repo_id.split("/")[0] if "/" in repo_id else repo_id @app.api(name="generate_bulletin", concurrency_limit=1) def generate_bulletin( repo_id: str, max_sessions: int ) -> tuple[str, str]: """Streams (status, html) updates; final tick carries the bulletin HTML.""" yield "Connecting…", empty_bulletin_html("Connecting…") try: client = get_client() except Exception as e: yield f"❌ {e}", empty_bulletin_html("HF_TOKEN missing") return try: yield "Listing sessions…", empty_bulletin_html("Listing sessions…") paths = list_sessions(repo_id) if not paths: yield ( "No sessions found in `sessions/**/*.jsonl`.", empty_bulletin_html("No sessions to roast."), ) return n = min(int(max_sessions), len(paths)) yield ( f"Fetching {n} of {len(paths)} sessions…", empty_bulletin_html(f"Fetching {n} sessions…"), ) sessions = fetch_sessions(repo_id, n) if not sessions: yield ( "Found session files but couldn't parse any.", empty_bulletin_html("Parse error."), ) return stats = compute_stats(sessions) transcripts = [ (path, truncate_transcript(events_to_transcript(evs), 40_000)) for path, evs in sessions ] yield ( f"Reading {len(transcripts)} sessions in parallel…", empty_bulletin_html("Consulting the traces…"), ) digests = digest_all(client, transcripts) if not digests: yield ( "Every per-session digest failed. Try again or lower max sessions.", empty_bulletin_html("Digest error."), ) return yield ( f"Drafting bulletin from {len(digests)} digests…", empty_bulletin_html("Drafting bulletin…"), ) owner = _owner_from(repo_id) try: report = build_report( client=client, digests=digests, user=owner, dataset_id=repo_id, stats=stats, ) except Exception as e: yield ( f"❌ Bulletin generation failed: {e}", empty_bulletin_html("The presses jammed."), ) return yield ( f"Bulletin issued for @{report['user']} — {report['archetype'][0]} {report['archetype'][1]}.", bulletin_html(report), ) except Exception as e: yield ( f"❌ {type(e).__name__}: {e}", empty_bulletin_html("Error."), ) @app.get("/", response_class=HTMLResponse) async def homepage(): return _INDEX.read_text(encoding="utf-8") if __name__ == "__main__": if not os.environ.get("HF_TOKEN"): print("warning: HF_TOKEN not set; the app will error on the first click.") app.launch(show_error=True)