From 70329bc3f4215c8109d44cafe727ab9efa9530d0 Mon Sep 17 00:00:00 2001 From: adalard Date: Wed, 16 Jul 2025 11:36:32 +0800 Subject: [PATCH 1/4] - fixed encoding errors. --- cli/main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/main.py b/cli/main.py index 64616ee1a..b1ee3ecfa 100644 --- a/cli/main.py +++ b/cli/main.py @@ -764,7 +764,7 @@ def wrapper(*args, **kwargs): func(*args, **kwargs) timestamp, message_type, content = obj.messages[-1] content = content.replace("\n", " ") # Replace newlines with spaces - with open(log_file, "a") as f: + with open(log_file, "a", encoding="utf-8") as f: f.write(f"{timestamp} [{message_type}] {content}\n") return wrapper @@ -775,7 +775,7 @@ def wrapper(*args, **kwargs): func(*args, **kwargs) timestamp, tool_name, args = obj.tool_calls[-1] args_str = ", ".join(f"{k}={v}" for k, v in args.items()) - with open(log_file, "a") as f: + with open(log_file, "a", encoding="utf-8") as f: f.write(f"{timestamp} [Tool Call] {tool_name}({args_str})\n") return wrapper @@ -788,7 +788,7 @@ def wrapper(section_name, content): content = obj.report_sections[section_name] if content: file_name = f"{section_name}.md" - with open(report_dir / file_name, "w") as f: + with open(report_dir / file_name, "w", encoding="utf-8") as f: f.write(content) return wrapper From b9a3a2dbcc22e197fe1de67126feaeb9e32168fe Mon Sep 17 00:00:00 2001 From: adalard Date: Wed, 16 Jul 2025 12:59:53 +0800 Subject: [PATCH 2/4] - enhance to set API Key in .env file instead. --- .env.sample | 4 ++++ cli/main.py | 4 ++++ tradingagents/default_config.py | 7 +++++++ 3 files changed, 15 insertions(+) create mode 100644 .env.sample diff --git a/.env.sample b/.env.sample new file mode 100644 index 000000000..b1810eaa0 --- /dev/null +++ b/.env.sample @@ -0,0 +1,4 @@ +# Copy this file to .env and fill in your API keys + +OPENAI_API_KEY= +FINNHUB_API_KEY= diff --git a/cli/main.py b/cli/main.py index b1ee3ecfa..f2f6b1bc6 100644 --- a/cli/main.py +++ b/cli/main.py @@ -19,6 +19,10 @@ from rich import box from rich.align import Align from rich.rule import Rule +from dotenv import load_dotenv + +# Load API keys from .env +load_dotenv() from tradingagents.graph.trading_graph import TradingAgentsGraph from tradingagents.default_config import DEFAULT_CONFIG diff --git a/tradingagents/default_config.py b/tradingagents/default_config.py index 089e9c240..b3e108f68 100644 --- a/tradingagents/default_config.py +++ b/tradingagents/default_config.py @@ -1,4 +1,8 @@ import os +from dotenv import load_dotenv + +# Load variables from .env +load_dotenv() DEFAULT_CONFIG = { "project_dir": os.path.abspath(os.path.join(os.path.dirname(__file__), ".")), @@ -19,4 +23,7 @@ "max_recur_limit": 100, # Tool settings "online_tools": True, + # API keys from environment + "openai_api_key": os.getenv("OPENAI_API_KEY", ""), + "finnhub_api_key": os.getenv("FINNHUB_API_KEY", ""), } From f350b3282530de3b9b7413eed2bcb27bb98d81ee Mon Sep 17 00:00:00 2001 From: adalard Date: Fri, 18 Jul 2025 11:09:06 +0800 Subject: [PATCH 3/4] - enable a new output option in HTML format. --- cli/main.py | 73 ++++++++++++++++++++++++++++++++++++++++++++++-- cli/utils.py | 24 ++++++++++++++++ requirements.txt | 1 + setup.py | 1 + 4 files changed, 96 insertions(+), 3 deletions(-) diff --git a/cli/main.py b/cli/main.py index f2f6b1bc6..3400f93b8 100644 --- a/cli/main.py +++ b/cli/main.py @@ -24,6 +24,40 @@ # Load API keys from .env load_dotenv() + + +# HTML output template and converter +HTML_TEMPLATE = """ + + + +{title} + + + +{content} + +""" + +def md_to_html(md_text: str) -> str: + """Convert Markdown text to HTML, dynamically importing markdown.""" + try: + import markdown + except ImportError: + raise RuntimeError( + "HTML output requires the 'markdown' package. Please install it with `pip install markdown`." + ) + return markdown.markdown(md_text, extensions=["tables", "fenced_code"]) + + from tradingagents.graph.trading_graph import TradingAgentsGraph from tradingagents.default_config import DEFAULT_CONFIG from cli.models import AnalystType @@ -484,6 +518,15 @@ def create_question_box(title, prompt, default=None): selected_shallow_thinker = select_shallow_thinking_agent(selected_llm_provider) selected_deep_thinker = select_deep_thinking_agent(selected_llm_provider) + console.print( + create_question_box( + "Step 7: Output Format", "Choose output format (Markdown or HTML)", "Markdown" + ) + ) + selected_output_format = select_output_format() + console.print(f"[green]Selected output format:[/green] {selected_output_format.upper()}") + + return { "ticker": selected_ticker, "analysis_date": analysis_date, @@ -493,6 +536,7 @@ def create_question_box(title, prompt, default=None): "backend_url": backend_url, "shallow_thinker": selected_shallow_thinker, "deep_thinker": selected_deep_thinker, + "output_format": selected_output_format, } @@ -738,6 +782,7 @@ def extract_content_string(content): def run_analysis(): # First get all user selections selections = get_user_selections() + output_format = selections["output_format"] # Create config with selected research depth config = DEFAULT_CONFIG.copy() @@ -791,9 +836,15 @@ def wrapper(section_name, content): if section_name in obj.report_sections and obj.report_sections[section_name] is not None: content = obj.report_sections[section_name] if content: - file_name = f"{section_name}.md" - with open(report_dir / file_name, "w", encoding="utf-8") as f: - f.write(content) + if output_format == "md": + file_name = f"{section_name}.md" + with open(report_dir / file_name, "w", encoding="utf-8") as f: + f.write(content) + else: + file_name = f"{section_name}.html" + html_body = md_to_html(content) + with open(report_dir / file_name, "w", encoding="utf-8") as f: + f.write(HTML_TEMPLATE.format(title=section_name.replace('_',' ').title(), content=html_body)) return wrapper message_buffer.add_message = save_message_decorator(message_buffer, "add_message") @@ -1099,6 +1150,22 @@ def wrapper(section_name, content): update_display(layout) + # Generate HTML index if HTML output selected + if output_format == "html": + # Convert final decision markdown to HTML + decision_md = message_buffer.report_sections.get("final_trade_decision", "") + decision_html = md_to_html(decision_md) + # Build links list + links = "" + for section, content in message_buffer.report_sections.items(): + if section != "final_trade_decision" and content: + filename = f"{section}.html" + display_name = section.replace("_", " ").title() + links += f'
  • {display_name}
  • ' + index_body = f"{decision_html}

    Other Reports

      {links}
    " + with open(report_dir / "index.html", "w", encoding="utf-8") as f: + f.write(HTML_TEMPLATE.format(title="TradingAgents Report", content=index_body)) + @app.command() def analyze(): diff --git a/cli/utils.py b/cli/utils.py index 7b9682a6b..3783756c0 100644 --- a/cli/utils.py +++ b/cli/utils.py @@ -1,4 +1,6 @@ import questionary +from rich.console import Console +console = Console() from typing import List, Optional, Tuple, Dict from cli.models import AnalystType @@ -239,6 +241,28 @@ def select_deep_thinking_agent(provider) -> str: return choice +def select_output_format() -> str: + """Prompt the user to choose output format (Markdown or HTML).""" + choice = questionary.select( + "Select output format:", + choices=[ + questionary.Choice("Markdown", "md"), + questionary.Choice("HTML", "html"), + ], + instruction="\n- Use arrow keys to navigate\n- Press Enter to select", + style=questionary.Style( + [ + ("selected", "fg:cyan noinherit"), + ("highlighted", "fg:cyan noinherit"), + ("pointer", "noinherit"), + ] + ), + ).ask() + if choice is None: + console.print("\n[red]No output format selected. Exiting...[/red]") + exit(1) + return choice + def select_llm_provider() -> tuple[str, str]: """Select the OpenAI api url using interactive selection.""" # Define OpenAI api options with their corresponding endpoints diff --git a/requirements.txt b/requirements.txt index a6154cd26..7c0ef1acb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -24,3 +24,4 @@ rich questionary langchain_anthropic langchain-google-genai +markdown diff --git a/setup.py b/setup.py index 793df3e69..bfc7880f1 100644 --- a/setup.py +++ b/setup.py @@ -25,6 +25,7 @@ "typer>=0.9.0", "rich>=13.0.0", "questionary>=2.0.1", + "markdown>=3.4.0", ], python_requires=">=3.10", entry_points={ From bfe1145ba8ae8f59136b61ba697b9c10ca2ec8a8 Mon Sep 17 00:00:00 2001 From: adalard Date: Fri, 18 Jul 2025 11:30:47 +0800 Subject: [PATCH 4/4] - enhance HTML styling. --- cli/main.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/cli/main.py b/cli/main.py index 3400f93b8..4dc3fe789 100644 --- a/cli/main.py +++ b/cli/main.py @@ -33,9 +33,14 @@ {title} +
    ← Back to Index
    +
    {content} +
    """