from __future__ import annotations import json import os import sys import textwrap from typing import List import gradio as gr # Ensure the local src/ directory is on sys.path (for Hugging Face Spaces) ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) SRC_DIR = os.path.join(ROOT_DIR, "src") if SRC_DIR not in sys.path: sys.path.append(SRC_DIR) from agentic_multiwriter.state import AgentState, ResearchSnippet from agentic_multiwriter.agents import ( researcher_node, aggregator_node, writer_node, critic_node, formatter_node, ) from agentic_multiwriter.tools import get_logger logger = get_logger() def _format_sources(snippets: List[ResearchSnippet]) -> str: if not snippets: return "No web sources were retrieved." lines = [] for s in snippets: title = s["title"] or s["url"] url = s["url"] snippet = s["snippet"] if url: lines.append(f"- [{title}]({url})\n \n > {snippet}") else: lines.append(f"- {title}\n \n > {snippet}") return "\n\n".join(lines) def generate(topic: str, mode: str, progress=gr.Progress()): """Gradio callback to run the pipeline step-by-step with progress.""" topic = topic.strip() if not topic: return ( "Please enter a topic.", "", "", "", "", ) # Initial state state: AgentState = { "topic": topic, "mode": mode, "research_snippets": [], "outline": [], "draft": "", "revised_draft": "", "final_output": "", "meta": {}, } # 1. Research progress(0.1, "Researching the web...") logger.info("UI: starting researcher_node") state = researcher_node(state) # 2. Aggregate progress(0.25, "Aggregating and cleaning snippets...") logger.info("UI: starting aggregator_node") state = aggregator_node(state) # 3. Write draft progress(0.5, "Writing first draft...") logger.info("UI: starting writer_node") state = writer_node(state) initial_draft = state.get("draft", "") or "" # 4. Critic / edit progress(0.7, "Reviewing and improving draft...") logger.info("UI: starting critic_node") state = critic_node(state) revised_draft = state.get("revised_draft", "") or initial_draft # 5. Format final output progress(0.9, f"Formatting final output as {mode}...") logger.info("UI: starting formatter_node") state = formatter_node(state) final_output = state.get("final_output", "") or revised_draft # 6. Prepare outline, meta, sources outline = state.get("outline", []) or [] meta = state.get("meta", {}) or {} snippets = state.get("research_snippets", []) or [] outline_text = "\n".join(f"- {item}" for item in outline) meta_text = json.dumps(meta, indent=2) sources_md = _format_sources(snippets) progress(1.0, "Done.") return final_output, initial_draft, revised_draft, sources_md, meta_text def build_interface() -> gr.Blocks: with gr.Blocks(title="Agentic Multiwriter") as demo: gr.Markdown( """ # 🧠 Agentic Multiwriter Multi-agent research & writing system built with **LangGraph**. 1. Researches your topic on the web 2. Aggregates and cleans snippets 3. Writes a draft 4. Critiques and improves it 5. Formats it as a blog, research summary, or LinkedIn-style post """ ) with gr.Row(): topic_input = gr.Textbox( label="Topic", placeholder="e.g. Future of agentic AI", lines=2, ) mode_input = gr.Radio( choices=["blog", "research_summary", "linkedin_post"], value="blog", label="Output mode", ) run_button = gr.Button("Generate", variant="primary") with gr.Tab("Final Output"): final_output_box = gr.Markdown(label="Final Output") with gr.Tab("Initial Draft (Writer)"): initial_draft_box = gr.Markdown(label="Initial Draft") with gr.Tab("Revised Draft (Critic)"): revised_draft_box = gr.Markdown(label="Revised Draft") with gr.Tab("Sources"): sources_box = gr.Markdown(label="Web Sources Used") with gr.Tab("Meta"): meta_box = gr.Textbox(label="Meta (timings, counts, etc.)", lines=10) run_button.click( fn=generate, inputs=[topic_input, mode_input], outputs=[ final_output_box, initial_draft_box, revised_draft_box, sources_box, meta_box, ], ) gr.Markdown( textwrap.dedent( """ --- ⚠️ **Note**: First run may take longer while the model loads or if you are using a local model. On Spaces, this app uses an open-source model via Hugging Face Inference API. """ ) ) return demo if __name__ == "__main__": demo = build_interface() demo.launch(server_name="0.0.0.0", server_port=7860)