feat: output all analysis into one single .md file

as described in the title
This commit is contained in:
Taylor Mao 2025-07-21 13:31:20 -04:00
parent 0853aa7a06
commit 873fe41f1d
2 changed files with 86 additions and 4 deletions

View File

@ -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)

View File

@ -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