from langgraph.graph import StateGraph, START, END from graph_states import FinalState from langgraph.checkpoint.memory import InMemorySaver from research_graph import create_research_graph from forum_graph import create_forum_graph from IPython.display import display,Image import base64 from IPython.display import display, Markdown, Image import uuid # Import uuid here as well for generating thread_id if needed elsewhere import base64 from IPython.display import display, Markdown, Image import uuid # Import uuid here as well for generating thread_id if needed elsewhere def render_bettafish_report(final_state): """ Renders the Final Report in the style of BettaFish using the structured data from your new 'Round Digest' architecture. """ # Extract data from the state # Note: In LangGraph, accessing values depends on if you have the dict or the object # We assume 'final_state' is the dictionary returned by .invoke() or .get_state().values report = final_state.get("final_report") digests = final_state.get("round_digests", []) raw_references = final_state.get("references", []) references = set(tuple(r) for r in raw_references) # If using the 'final_report' key from the graph output, it might be inside a dict # Adjust extraction if needed based on your specific return statement if isinstance(report, dict) and "structured_response" in report: report = report["structured_response"] # --- 1. Header & Executive Summary --- md_output = f""" # 🦈 Strategic Decision Matrix: {report.title} --- ### 📝 Executive Summary {report.executive_summary} --- ### 🥊 The Debate Scorecard (Round-by-Round Analysis) This matrix tracks the flow of dominance throughout the debate phases. | Round | **Agent A (Thesis)** | **Agent B (Antithesis)** | **Winner** | | :--- | :--- | :--- | :--- | """ # --- 2. The Battle Matrix (Iterating through Round Digests) --- for digest in digests: # Format the winner with an icon winner_display = "⚖️ Draw" if "Agent A" in digest.winner_of_round or "Bull" in digest.winner_of_round or "Pro" in digest.winner_of_round: winner_display = "🏆 **Agent A**" elif "Agent B" in digest.winner_of_round or "Bear" in digest.winner_of_round or "Con" in digest.winner_of_round: winner_display = "🏆 **Agent B**" # Join list arguments into bullet points for the table pro_args = "
".join([f"• {arg}" for arg in digest.key_arguments_pro]) con_args = "
".join([f"• {arg}" for arg in digest.key_arguments_con]) row = f"| **{digest.round_number}** | {pro_args} | {con_args} | {winner_display} |\n" md_output += row sorted_references = sorted(list(references)) # --- 3. Consensus & Unique Angles --- md_output += "\n\n### 🤝 Consensus & Novelty\n" md_output += "**Agreed Reality:**\n" for p in report.consensus_points: md_output += f"- {p}\n" md_output += "\n**Unique Perspectives Uncovered:**\n" for p in report.unique_perspectives: md_output += f"- {p}\n" md_output += "\n\n### 📚 Bibliography & Data Sources\n" if sorted_references: for idx, ref in enumerate(sorted_references, 1): # Check if it looks like a URL or just a string if ref[1].startswith("http"): md_output += f"{idx}. [{ref[0]}]({ref[1]})\n" else: md_output += f"{idx}. {ref[0]}\n" else: md_output += "*No specific data sources cited in the structured output.*" # # --- 4. Render Markdown --- # display(Markdown(md_output)) # # --- 5. Render The Argument Map (Mermaid) --- # print("\nVisualizing the Logic Tree...") # Combine the accumulated mermaid subgraphs into one main graph raw_mermaid = final_state.get("running_mermaid_graph", "") # If the accumulator didn't add the header, add it now if "graph TD" not in raw_mermaid and "flowchart" not in raw_mermaid: full_mermaid = f"graph TD\n{raw_mermaid}" else: full_mermaid = raw_mermaid # # Clean up any potential markdown code fences from the LLM # full_mermaid = full_mermaid.replace("```mermaid", "").replace("```", "").strip() # # Render via API # graphbytes = full_mermaid.encode("utf8") # base64_bytes = base64.b64encode(graphbytes) # base64_string = base64_bytes.decode("ascii") # url = "https://mermaid.ink/img/" + base64_string # display(Image(url=url)) md_output = f"{md_output}\n\n## Visual Logic Map\n```mermaid\n{full_mermaid}\n```" # --- 6. Save to File --- with open("strategic_report.md", "w") as f: f.write(md_output) # f.write("\n\n## Visual Logic Map\n```mermaid\n" + full_mermaid + "\n```") return md_output async def create_final_graph(): research_graph = await create_research_graph() forum_graph = await create_forum_graph() final_blueprint = StateGraph(FinalState) final_blueprint.add_node("Research Graph",research_graph) final_blueprint.add_node("Forum Graph",forum_graph) final_blueprint.add_edge(START,"Research Graph") final_blueprint.add_edge("Research Graph","Forum Graph") final_blueprint.add_edge("Forum Graph",END) final_graph = final_blueprint.compile(checkpointer=InMemorySaver()) display(Image(final_graph.get_graph().draw_mermaid_png())) return final_graph if __name__ == "__main__": import asyncio async def main(): final_graph = await create_final_graph() thread_id = str(uuid.uuid4()) config = {"configurable":{"thread_id":thread_id},"recursion_limit":100} initial_state = FinalState( query="Is AI investment in 2024 a bubble or undervalued opportunity?", vector_store="", query_limit=0, max_rounds=1, current_round=1, step=0, round_digests=[], running_mermaid_graph="", debate_history=[], final_report=None, messages = [], references = [] ) async for parent,child in final_graph.astream(initial_state,config=config,stream_mode="updates",subgraphs=True): print(child.keys()) snapshot = final_graph.get_state(config={"configurable":{"thread_id":thread_id}}) if snapshot.values: render_bettafish_report(snapshot.values) else: print("No state found. Did the graph finish?") asyncio.run(main())