diff --git a/cli/main.py b/cli/main.py index 2c724e06..256120ca 100644 --- a/cli/main.py +++ b/cli/main.py @@ -523,12 +523,16 @@ def get_analysis_date(): ) -def display_complete_report(final_state): - """Display the complete analysis report with team-based panels.""" +def display_complete_report(final_state, report_dir, selections): + """Display the complete analysis report with team-based panels and save to markdown file.""" console.print("\n[bold green]Complete Analysis Report[/bold green]\n") + # Create full markdown report content + md_content = ["# Complete Analysis Report"] + # I. Analyst Team Reports analyst_reports = [] + md_content.append("## I. Analyst Team Reports") # Market Analyst Report if final_state.get("market_report"): @@ -540,6 +544,10 @@ def display_complete_report(final_state): padding=(1, 2), ) ) + md_content.append("### Market Analyst") + md_content.append(adjust_markdown_headers( + final_state["market_report"])) + md_content.append("") # Empty line for spacing # Social Analyst Report if final_state.get("sentiment_report"): @@ -551,6 +559,10 @@ def display_complete_report(final_state): padding=(1, 2), ) ) + md_content.append("### Social Analyst") + md_content.append(adjust_markdown_headers( + final_state["sentiment_report"])) + md_content.append("") # Empty line for spacing # News Analyst Report if final_state.get("news_report"): @@ -562,6 +574,9 @@ def display_complete_report(final_state): padding=(1, 2), ) ) + md_content.append("### News Analyst") + md_content.append(adjust_markdown_headers(final_state["news_report"])) + md_content.append("") # Empty line for spacing # Fundamentals Analyst Report if final_state.get("fundamentals_report"): @@ -573,6 +588,10 @@ def display_complete_report(final_state): padding=(1, 2), ) ) + md_content.append("### Fundamentals Analyst") + md_content.append(adjust_markdown_headers( + final_state["fundamentals_report"])) + md_content.append("") # Empty line for spacing if analyst_reports: console.print( @@ -588,6 +607,7 @@ def display_complete_report(final_state): if final_state.get("investment_debate_state"): research_reports = [] debate_state = final_state["investment_debate_state"] + md_content.append("## II. Research Team Decision") # Bull Researcher Analysis if debate_state.get("bull_history"): @@ -599,6 +619,10 @@ def display_complete_report(final_state): padding=(1, 2), ) ) + md_content.append("### Bull Researcher") + md_content.append(adjust_markdown_headers( + debate_state["bull_history"])) + md_content.append("") # Empty line for spacing # Bear Researcher Analysis if debate_state.get("bear_history"): @@ -610,6 +634,10 @@ def display_complete_report(final_state): padding=(1, 2), ) ) + md_content.append("### Bear Researcher") + md_content.append(adjust_markdown_headers( + debate_state["bear_history"])) + md_content.append("") # Empty line for spacing # Research Manager Decision if debate_state.get("judge_decision"): @@ -621,6 +649,10 @@ def display_complete_report(final_state): padding=(1, 2), ) ) + md_content.append("### Research Manager Decision") + md_content.append(adjust_markdown_headers( + debate_state["judge_decision"])) + md_content.append("") # Empty line for spacing if research_reports: console.print( @@ -634,6 +666,12 @@ def display_complete_report(final_state): # III. Trading Team Reports if final_state.get("trader_investment_plan"): + md_content.append("## III. Trading Team Plan") + md_content.append("### Trader") + md_content.append(adjust_markdown_headers( + final_state["trader_investment_plan"])) + md_content.append("") # Empty line for spacing + console.print( Panel( Panel( @@ -652,6 +690,7 @@ def display_complete_report(final_state): if final_state.get("risk_debate_state"): risk_reports = [] risk_state = final_state["risk_debate_state"] + md_content.append("## IV. Risk Management Team Decision") # Aggressive (Risky) Analyst Analysis if risk_state.get("risky_history"): @@ -663,6 +702,10 @@ def display_complete_report(final_state): padding=(1, 2), ) ) + md_content.append("### Aggressive (Risky) Analyst") + md_content.append(adjust_markdown_headers( + risk_state["risky_history"])) + md_content.append("") # Empty line for spacing # Conservative (Safe) Analyst Analysis if risk_state.get("safe_history"): @@ -674,6 +717,10 @@ def display_complete_report(final_state): padding=(1, 2), ) ) + md_content.append("### Conservative (Safe) Analyst") + md_content.append(adjust_markdown_headers( + risk_state["safe_history"])) + md_content.append("") # Empty line for spacing # Neutral Analyst Analysis if risk_state.get("neutral_history"): @@ -685,6 +732,10 @@ def display_complete_report(final_state): padding=(1, 2), ) ) + md_content.append("### Neutral Analyst") + md_content.append(adjust_markdown_headers( + risk_state["neutral_history"])) + md_content.append("") # Empty line for spacing if risk_reports: console.print( @@ -698,6 +749,11 @@ def display_complete_report(final_state): # V. Portfolio Manager Decision if risk_state.get("judge_decision"): + md_content.append("## V. Portfolio Manager Decision") + md_content.append("### Portfolio Manager") + md_content.append(adjust_markdown_headers( + risk_state["judge_decision"])) + console.print( Panel( Panel( @@ -712,6 +768,12 @@ def display_complete_report(final_state): ) ) + # Write the full markdown report to file + complete_report_filename = f"complete_report_by_{selections.get('shallow_thinker')}__{selections.get('deep_thinker')}.md" + complete_report_path = report_dir / complete_report_filename + with open(complete_report_path, "w") as f: + f.write("\n".join(md_content)) + def update_research_team_status(status): """Update status for all research team members and trader.""" @@ -1102,7 +1164,7 @@ def run_analysis(): message_buffer.update_report_section(section, final_state[section]) # Display the complete final report - display_complete_report(final_state) + display_complete_report(final_state, report_dir, selections) update_display(layout) diff --git a/cli/utils.py b/cli/utils.py index 7b9682a6..8664fcdd 100644 --- a/cli/utils.py +++ b/cli/utils.py @@ -1,6 +1,6 @@ import questionary from typing import List, Optional, Tuple, Dict - +import re from cli.models import AnalystType ANALYST_ORDER = [ @@ -274,3 +274,23 @@ def select_llm_provider() -> tuple[str, str]: print(f"You selected: {display_name}\tURL: {url}") return display_name, url + + +def adjust_markdown_headers(markdown_text: str) -> str: + def replace_header(match): + hashes = match.group(1) + header_text = match.group(2) + header_level = len(hashes) + + if header_level <= 3: + new_hashes = "####" # Force at least h4 + else: + new_hashes = "#" * (header_level + 1) # Increase one # + + return f"{new_hashes} {header_text}" + + # Regex to match markdown headers from level 1 to 6 + adjusted_text = re.sub( + r"^(#{1,6})\s+(.*)", replace_header, markdown_text, flags=re.MULTILINE + ) + return adjusted_text \ No newline at end of file