Skip to content

Commit 6cddd26

Browse files
committed
feat: multi-language output support for analyst reports and final decision (#472)
1 parent c61242a commit 6cddd26

9 files changed

Lines changed: 86 additions & 17 deletions

File tree

cli/main.py

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -519,43 +519,52 @@ def create_question_box(title, prompt, default=None):
519519
)
520520
analysis_date = get_analysis_date()
521521

522-
# Step 3: Select analysts
522+
# Step 3: Output language
523523
console.print(
524524
create_question_box(
525-
"Step 3: Analysts Team", "Select your LLM analyst agents for the analysis"
525+
"Step 3: Output Language",
526+
"Select the language for analyst reports and final decision"
527+
)
528+
)
529+
output_language = ask_output_language()
530+
531+
# Step 4: Select analysts
532+
console.print(
533+
create_question_box(
534+
"Step 4: Analysts Team", "Select your LLM analyst agents for the analysis"
526535
)
527536
)
528537
selected_analysts = select_analysts()
529538
console.print(
530539
f"[green]Selected analysts:[/green] {', '.join(analyst.value for analyst in selected_analysts)}"
531540
)
532541

533-
# Step 4: Research depth
542+
# Step 5: Research depth
534543
console.print(
535544
create_question_box(
536-
"Step 4: Research Depth", "Select your research depth level"
545+
"Step 5: Research Depth", "Select your research depth level"
537546
)
538547
)
539548
selected_research_depth = select_research_depth()
540549

541-
# Step 5: OpenAI backend
550+
# Step 6: LLM Provider
542551
console.print(
543552
create_question_box(
544-
"Step 5: OpenAI backend", "Select which service to talk to"
553+
"Step 6: LLM Provider", "Select your LLM provider"
545554
)
546555
)
547556
selected_llm_provider, backend_url = select_llm_provider()
548-
549-
# Step 6: Thinking agents
557+
558+
# Step 7: Thinking agents
550559
console.print(
551560
create_question_box(
552-
"Step 6: Thinking Agents", "Select your thinking agents for analysis"
561+
"Step 7: Thinking Agents", "Select your thinking agents for analysis"
553562
)
554563
)
555564
selected_shallow_thinker = select_shallow_thinking_agent(selected_llm_provider)
556565
selected_deep_thinker = select_deep_thinking_agent(selected_llm_provider)
557566

558-
# Step 7: Provider-specific thinking configuration
567+
# Step 8: Provider-specific thinking configuration
559568
thinking_level = None
560569
reasoning_effort = None
561570
anthropic_effort = None
@@ -564,23 +573,23 @@ def create_question_box(title, prompt, default=None):
564573
if provider_lower == "google":
565574
console.print(
566575
create_question_box(
567-
"Step 7: Thinking Mode",
576+
"Step 8: Thinking Mode",
568577
"Configure Gemini thinking mode"
569578
)
570579
)
571580
thinking_level = ask_gemini_thinking_config()
572581
elif provider_lower == "openai":
573582
console.print(
574583
create_question_box(
575-
"Step 7: Reasoning Effort",
584+
"Step 8: Reasoning Effort",
576585
"Configure OpenAI reasoning effort level"
577586
)
578587
)
579588
reasoning_effort = ask_openai_reasoning_effort()
580589
elif provider_lower == "anthropic":
581590
console.print(
582591
create_question_box(
583-
"Step 7: Effort Level",
592+
"Step 8: Effort Level",
584593
"Configure Claude effort level"
585594
)
586595
)
@@ -598,6 +607,7 @@ def create_question_box(title, prompt, default=None):
598607
"google_thinking_level": thinking_level,
599608
"openai_reasoning_effort": reasoning_effort,
600609
"anthropic_effort": anthropic_effort,
610+
"output_language": output_language,
601611
}
602612

603613

@@ -931,6 +941,7 @@ def run_analysis():
931941
config["google_thinking_level"] = selections.get("google_thinking_level")
932942
config["openai_reasoning_effort"] = selections.get("openai_reasoning_effort")
933943
config["anthropic_effort"] = selections.get("anthropic_effort")
944+
config["output_language"] = selections.get("output_language", "English")
934945

935946
# Create stats callback handler for tracking LLM/tool calls
936947
stats_handler = StatsCallbackHandler()

cli/utils.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,3 +281,37 @@ def ask_gemini_thinking_config() -> str | None:
281281
("pointer", "fg:green noinherit"),
282282
]),
283283
).ask()
284+
285+
286+
def ask_output_language() -> str:
287+
"""Ask for report output language."""
288+
choice = questionary.select(
289+
"Select Output Language:",
290+
choices=[
291+
questionary.Choice("English (default)", "English"),
292+
questionary.Choice("Chinese (中文)", "Chinese"),
293+
questionary.Choice("Japanese (日本語)", "Japanese"),
294+
questionary.Choice("Korean (한국어)", "Korean"),
295+
questionary.Choice("Hindi (हिन्दी)", "Hindi"),
296+
questionary.Choice("Spanish (Español)", "Spanish"),
297+
questionary.Choice("Portuguese (Português)", "Portuguese"),
298+
questionary.Choice("French (Français)", "French"),
299+
questionary.Choice("German (Deutsch)", "German"),
300+
questionary.Choice("Arabic (العربية)", "Arabic"),
301+
questionary.Choice("Russian (Русский)", "Russian"),
302+
questionary.Choice("Custom language", "custom"),
303+
],
304+
style=questionary.Style([
305+
("selected", "fg:yellow noinherit"),
306+
("highlighted", "fg:yellow noinherit"),
307+
("pointer", "fg:yellow noinherit"),
308+
]),
309+
).ask()
310+
311+
if choice == "custom":
312+
return questionary.text(
313+
"Enter language name (e.g. Turkish, Vietnamese, Thai, Indonesian):",
314+
validate=lambda x: len(x.strip()) > 0 or "Please enter a language name.",
315+
).ask().strip()
316+
317+
return choice

tradingagents/agents/analysts/fundamentals_analyst.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
get_fundamentals,
99
get_income_statement,
1010
get_insider_transactions,
11+
get_language_instruction,
1112
)
1213
from tradingagents.dataflows.config import get_config
1314

@@ -27,7 +28,8 @@ def fundamentals_analyst_node(state):
2728
system_message = (
2829
"You are a researcher tasked with analyzing fundamental information over the past week about a company. Please write a comprehensive report of the company's fundamental information such as financial documents, company profile, basic company financials, and company financial history to gain a full view of the company's fundamental information to inform traders. Make sure to include as much detail as possible. Provide specific, actionable insights with supporting evidence to help traders make informed decisions."
2930
+ " Make sure to append a Markdown table at the end of the report to organize key points in the report, organized and easy to read."
30-
+ " Use the available tools: `get_fundamentals` for comprehensive company analysis, `get_balance_sheet`, `get_cashflow`, and `get_income_statement` for specific financial statements.",
31+
+ " Use the available tools: `get_fundamentals` for comprehensive company analysis, `get_balance_sheet`, `get_cashflow`, and `get_income_statement` for specific financial statements."
32+
+ get_language_instruction(),
3133
)
3234

3335
prompt = ChatPromptTemplate.from_messages(

tradingagents/agents/analysts/market_analyst.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from tradingagents.agents.utils.agent_utils import (
55
build_instrument_context,
66
get_indicators,
7+
get_language_instruction,
78
get_stock_data,
89
)
910
from tradingagents.dataflows.config import get_config
@@ -47,6 +48,7 @@ def market_analyst_node(state):
4748
4849
- Select indicators that provide diverse and complementary information. Avoid redundancy (e.g., do not select both rsi and stochrsi). Also briefly explain why they are suitable for the given market context. When you tool call, please use the exact name of the indicators provided above as they are defined parameters, otherwise your call will fail. Please make sure to call get_stock_data first to retrieve the CSV that is needed to generate indicators. Then use get_indicators with the specific indicator names. Write a very detailed and nuanced report of the trends you observe. Provide specific, actionable insights with supporting evidence to help traders make informed decisions."""
4950
+ """ Make sure to append a Markdown table at the end of the report to organize key points in the report, organized and easy to read."""
51+
+ get_language_instruction()
5052
)
5153

5254
prompt = ChatPromptTemplate.from_messages(

tradingagents/agents/analysts/news_analyst.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from tradingagents.agents.utils.agent_utils import (
55
build_instrument_context,
66
get_global_news,
7+
get_language_instruction,
78
get_news,
89
)
910
from tradingagents.dataflows.config import get_config
@@ -22,6 +23,7 @@ def news_analyst_node(state):
2223
system_message = (
2324
"You are a news researcher tasked with analyzing recent news and trends over the past week. Please write a comprehensive report of the current state of the world that is relevant for trading and macroeconomics. Use the available tools: get_news(query, start_date, end_date) for company-specific or targeted news searches, and get_global_news(curr_date, look_back_days, limit) for broader macroeconomic news. Provide specific, actionable insights with supporting evidence to help traders make informed decisions."
2425
+ """ Make sure to append a Markdown table at the end of the report to organize key points in the report, organized and easy to read."""
26+
+ get_language_instruction()
2527
)
2628

2729
prompt = ChatPromptTemplate.from_messages(

tradingagents/agents/analysts/social_media_analyst.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
22
import time
33
import json
4-
from tradingagents.agents.utils.agent_utils import build_instrument_context, get_news
4+
from tradingagents.agents.utils.agent_utils import build_instrument_context, get_language_instruction, get_news
55
from tradingagents.dataflows.config import get_config
66

77

@@ -17,6 +17,7 @@ def social_media_analyst_node(state):
1717
system_message = (
1818
"You are a social media and company specific news researcher/analyst tasked with analyzing social media posts, recent company news, and public sentiment for a specific company over the past week. You will be given a company's name your objective is to write a comprehensive long report detailing your analysis, insights, and implications for traders and investors on this company's current state after looking at social media and what people are saying about that company, analyzing sentiment data of what people feel each day about the company, and looking at recent company news. Use the get_news(query, start_date, end_date) tool to search for company-specific news and social media discussions. Try to look at all sources possible from social media to sentiment to news. Provide specific, actionable insights with supporting evidence to help traders make informed decisions."
1919
+ """ Make sure to append a Markdown table at the end of the report to organize key points in the report, organized and easy to read."""
20+
+ get_language_instruction()
2021
)
2122

2223
prompt = ChatPromptTemplate.from_messages(

tradingagents/agents/managers/portfolio_manager.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from tradingagents.agents.utils.agent_utils import build_instrument_context
1+
from tradingagents.agents.utils.agent_utils import build_instrument_context, get_language_instruction
22

33

44
def create_portfolio_manager(llm, memory):
@@ -50,7 +50,7 @@ def portfolio_manager_node(state) -> dict:
5050
5151
---
5252
53-
Be decisive and ground every conclusion in specific evidence from the analysts."""
53+
Be decisive and ground every conclusion in specific evidence from the analysts.{get_language_instruction()}"""
5454

5555
response = llm.invoke(prompt)
5656

tradingagents/agents/utils/agent_utils.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,20 @@
2020
)
2121

2222

23+
def get_language_instruction() -> str:
24+
"""Return a prompt instruction for the configured output language.
25+
26+
Returns empty string when English (default), so no extra tokens are used.
27+
Only applied to user-facing agents (analysts, portfolio manager).
28+
Internal debate agents stay in English for reasoning quality.
29+
"""
30+
from tradingagents.dataflows.config import get_config
31+
lang = get_config().get("output_language", "English")
32+
if lang.strip().lower() == "english":
33+
return ""
34+
return f" Write your entire response in {lang}."
35+
36+
2337
def build_instrument_context(ticker: str) -> str:
2438
"""Describe the exact instrument so agents preserve exchange-qualified tickers."""
2539
return (

tradingagents/default_config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
"google_thinking_level": None, # "high", "minimal", etc.
1717
"openai_reasoning_effort": None, # "medium", "high", "low"
1818
"anthropic_effort": None, # "high", "medium", "low"
19+
# Output language for analyst reports and final decision
20+
# Internal agent debate stays in English for reasoning quality
21+
"output_language": "English",
1922
# Debate and discussion settings
2023
"max_debate_rounds": 1,
2124
"max_risk_discuss_rounds": 1,

0 commit comments

Comments
 (0)