From 9f397fee75fcc7f58830505929d5bfdbefe6cfaf Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 18 Mar 2026 07:00:37 +0100 Subject: [PATCH] Sync fork with upstream TauricResearch/TradingAgents (v0.2.1) (#12) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: remove unused chainlit dependency (CVE-2026-22218) * fix: pass debate round config to ConditionalLogic (#361) * fix: pass max_debate_rounds and max_risk_discuss_rounds config to ConditionalLogic * use config values * fix: add explicit UTF-8 encoding to all file open() calls Prevents UnicodeEncodeError on Windows where the default encoding (cp1252/gbk) cannot handle Unicode characters in LLM output. Closes #77, closes #114, closes #126, closes #215, closes #332 * fix: initialize all debate state fields in propagation.py InvestDebateState was missing bull_history, bear_history, judge_decision. RiskDebateState was missing aggressive_history, conservative_history, neutral_history, latest_speaker, judge_decision. This caused KeyError in _log_state() and reflection, especially with edge-case config values. * fix: handle comma-separated indicators in get_indicators tool LLMs (especially smaller models) sometimes pass multiple indicator names as a single comma-separated string instead of making separate tool calls. Split and process each individually at the tool boundary. * fix: add missing console import to cli/utils.py Seven error-handling paths used console.print() but console was never imported, causing NameError on invalid user input. * fix: harden stock data parsing against malformed CSV and NaN values Add _clean_dataframe() to normalize stock DataFrames before stockstats: coerce invalid dates/prices, drop rows missing Close, fill price gaps. Also add on_bad_lines="skip" to all cached CSV reads. * chore: update model lists, bump to v0.2.1, fix package build - OpenAI: add GPT-5.4, GPT-5.4 Pro; remove o-series and legacy GPT-4o - Anthropic: add Claude Opus 4.6, Sonnet 4.6; remove legacy 4.1/4.0/3.x - Google: add Gemini 3.1 Pro, 3.1 Flash Lite; remove deprecated gemini-3-pro-preview and Gemini 2.0 series - xAI: clean up model list to match current API - Simplify UnifiedChatOpenAI GPT-5 temperature handling - Add missing tradingagents/__init__.py (fixes pip install building) * docs: add v0.2.1 release note to README * fix: add http_client support for SSL certificate customization - Add http_client and http_async_client parameters to all LLM clients - OpenAIClient, GoogleClient, AnthropicClient now support custom httpx clients - Fixes SSL certificate verification errors on Windows Conda environments - Users can now pass custom httpx.Client with verify=False or custom certs Fixes #369 * Initial plan --------- Co-authored-by: Yijia-Xiao Co-authored-by: makk9 <117951691+makk9@users.noreply.github.com> Co-authored-by: 阳虎 Co-authored-by: Yijia Xiao <48253104+Yijia-Xiao@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: ahmet guzererler --- README.md | 1 + cli/utils.py | 72 +++++++++---------- pyproject.toml | 3 +- requirements.txt | 1 - tradingagents/__init__.py | 0 .../utils/technical_indicators_tools.py | 14 +++- tradingagents/dataflows/stockstats_utils.py | 17 ++++- tradingagents/dataflows/y_finance.py | 24 +++---- tradingagents/graph/propagation.py | 14 +++- tradingagents/graph/trading_graph.py | 1 + tradingagents/llm_clients/anthropic_client.py | 2 +- tradingagents/llm_clients/factory.py | 6 ++ tradingagents/llm_clients/google_client.py | 2 +- tradingagents/llm_clients/openai_client.py | 26 ++++--- tradingagents/llm_clients/validators.py | 37 +++------- 15 files changed, 121 insertions(+), 99 deletions(-) create mode 100644 tradingagents/__init__.py diff --git a/README.md b/README.md index 34310010..8cf085e8 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ # TradingAgents: Multi-Agents LLM Financial Trading Framework ## News +- [2026-03] **TradingAgents v0.2.1** released with GPT-5.4, Gemini 3.1, Claude 4.6 model coverage and improved system stability. - [2026-02] **TradingAgents v0.2.0** released with multi-provider LLM support (GPT-5.x, Gemini 3.x, Claude 4.x, Grok 4.x) and improved system architecture. - [2026-01] **Trading-R1** [Technical Report](https://arxiv.org/abs/2509.11420) released, with [Terminal](https://github.com/TauricResearch/Trading-R1) expected to land soon. diff --git a/cli/utils.py b/cli/utils.py index f02febfd..77d2f56f 100644 --- a/cli/utils.py +++ b/cli/utils.py @@ -1,6 +1,7 @@ import questionary import requests from typing import List, Optional, Tuple, Dict + from rich.console import Console from cli.models import AnalystType @@ -154,30 +155,30 @@ def select_shallow_thinking_agent(provider) -> str: return model.strip() options = ollama_models else: + # Ordering: medium → light → heavy (balanced first for quick tasks) + # Within same tier, newer models first SHALLOW_AGENT_OPTIONS = { "openai": [ - ("GPT-5 Mini - Cost-optimized reasoning", "gpt-5-mini"), - ("GPT-5 Nano - Ultra-fast, high-throughput", "gpt-5-nano"), - ("GPT-5.2 - Latest flagship", "gpt-5.2"), - ("GPT-5.1 - Flexible reasoning", "gpt-5.1"), - ("GPT-4.1 - Smartest non-reasoning, 1M context", "gpt-4.1"), + ("GPT-5 Mini - Balanced speed, cost, and capability", "gpt-5-mini"), + ("GPT-5 Nano - High-throughput, simple tasks", "gpt-5-nano"), + ("GPT-5.4 - Latest frontier, 1M context", "gpt-5.4"), + ("GPT-4.1 - Smartest non-reasoning model", "gpt-4.1"), ], "anthropic": [ - ("Claude Haiku 4.5 - Fast + extended thinking", "claude-haiku-4-5"), - ("Claude Sonnet 4.5 - Best for agents/coding", "claude-sonnet-4-5"), - ("Claude Sonnet 4 - High-performance", "claude-sonnet-4-20250514"), + ("Claude Sonnet 4.6 - Best speed and intelligence balance", "claude-sonnet-4-6"), + ("Claude Haiku 4.5 - Fast, near-instant responses", "claude-haiku-4-5"), + ("Claude Sonnet 4.5 - Agents and coding", "claude-sonnet-4-5"), ], "google": [ ("Gemini 3 Flash - Next-gen fast", "gemini-3-flash-preview"), - ("Gemini 2.5 Flash - Balanced, recommended", "gemini-2.5-flash"), - ("Gemini 3 Pro - Reasoning-first", "gemini-3-pro-preview"), + ("Gemini 2.5 Flash - Balanced, stable", "gemini-2.5-flash"), + ("Gemini 3.1 Flash Lite - Most cost-efficient", "gemini-3.1-flash-lite-preview"), ("Gemini 2.5 Flash Lite - Fast, low-cost", "gemini-2.5-flash-lite"), ], "xai": [ ("Grok 4.1 Fast (Non-Reasoning) - Speed optimized, 2M ctx", "grok-4-1-fast-non-reasoning"), ("Grok 4 Fast (Non-Reasoning) - Speed optimized", "grok-4-fast-non-reasoning"), ("Grok 4.1 Fast (Reasoning) - High-performance, 2M ctx", "grok-4-1-fast-reasoning"), - ("Grok 4 Fast (Reasoning) - High-performance", "grok-4-fast-reasoning"), ], "openrouter": [ ("NVIDIA Nemotron 3 Nano 30B (free)", "nvidia/nemotron-3-nano-30b-a3b:free"), @@ -227,22 +228,24 @@ def select_mid_thinking_agent(provider) -> str: return model.strip() options = ollama_models else: + # Ordering: medium → light → heavy (balanced for mid-tier tasks) + # Within same tier, newer models first MID_AGENT_OPTIONS = { "openai": [ - ("GPT-5.1 - Flexible reasoning", "gpt-5.1"), - ("GPT-5 - Advanced reasoning", "gpt-5"), - ("GPT-4.1 - Smartest non-reasoning, 1M context", "gpt-4.1"), - ("GPT-5 Mini - Cost-optimized reasoning", "gpt-5-mini"), + ("GPT-5.2 - Strong reasoning, cost-effective", "gpt-5.2"), + ("GPT-5 Mini - Balanced speed, cost, and capability", "gpt-5-mini"), + ("GPT-5.4 - Latest frontier, 1M context", "gpt-5.4"), + ("GPT-4.1 - Smartest non-reasoning model", "gpt-4.1"), ], "anthropic": [ - ("Claude Sonnet 4.5 - Best for agents/coding", "claude-sonnet-4-5"), - ("Claude Sonnet 4 - High-performance", "claude-sonnet-4-20250514"), - ("Claude Haiku 4.5 - Fast + extended thinking", "claude-haiku-4-5"), + ("Claude Sonnet 4.6 - Best speed and intelligence balance", "claude-sonnet-4-6"), + ("Claude Sonnet 4.5 - Agents and coding", "claude-sonnet-4-5"), + ("Claude Haiku 4.5 - Fast, near-instant responses", "claude-haiku-4-5"), ], "google": [ - ("Gemini 2.5 Flash - Balanced, recommended", "gemini-2.5-flash"), + ("Gemini 2.5 Flash - Balanced, stable", "gemini-2.5-flash"), ("Gemini 3 Flash - Next-gen fast", "gemini-3-flash-preview"), - ("Gemini 3 Pro - Reasoning-first", "gemini-3-pro-preview"), + ("Gemini 2.5 Pro - Stable pro model", "gemini-2.5-pro"), ], "xai": [ ("Grok 4.1 Fast (Reasoning) - High-performance, 2M ctx", "grok-4-1-fast-reasoning"), @@ -250,7 +253,6 @@ def select_mid_thinking_agent(provider) -> str: ("Grok 4.1 Fast (Non-Reasoning) - Speed optimized, 2M ctx", "grok-4-1-fast-non-reasoning"), ], "openrouter": [ - ("DeepSeek R1 - Strong open-source reasoning", "deepseek/deepseek-r1"), ("Z.AI GLM 4.5 Air (free)", "z-ai/glm-4.5-air:free"), ("NVIDIA Nemotron 3 Nano 30B (free)", "nvidia/nemotron-3-nano-30b-a3b:free"), ], @@ -296,36 +298,34 @@ def select_deep_thinking_agent(provider) -> str: return model.strip() options = ollama_models else: + # Ordering: heavy → medium → light (most capable first for deep tasks) + # Within same tier, newer models first DEEP_AGENT_OPTIONS = { "openai": [ - ("GPT-5.2 - Latest flagship", "gpt-5.2"), - ("GPT-5.1 - Flexible reasoning", "gpt-5.1"), - ("GPT-5 - Advanced reasoning", "gpt-5"), - ("GPT-4.1 - Smartest non-reasoning, 1M context", "gpt-4.1"), - ("GPT-5 Mini - Cost-optimized reasoning", "gpt-5-mini"), - ("GPT-5 Nano - Ultra-fast, high-throughput", "gpt-5-nano"), + ("GPT-5.4 - Latest frontier, 1M context", "gpt-5.4"), + ("GPT-5.2 - Strong reasoning, cost-effective", "gpt-5.2"), + ("GPT-5 Mini - Balanced speed, cost, and capability", "gpt-5-mini"), + ("GPT-5.4 Pro - Most capable, expensive ($30/$180 per 1M tokens)", "gpt-5.4-pro"), ], "anthropic": [ - ("Claude Sonnet 4.5 - Best for agents/coding", "claude-sonnet-4-5"), + ("Claude Opus 4.6 - Most intelligent, agents and coding", "claude-opus-4-6"), ("Claude Opus 4.5 - Premium, max intelligence", "claude-opus-4-5"), - ("Claude Opus 4.1 - Most capable model", "claude-opus-4-1-20250805"), - ("Claude Haiku 4.5 - Fast + extended thinking", "claude-haiku-4-5"), - ("Claude Sonnet 4 - High-performance", "claude-sonnet-4-20250514"), + ("Claude Sonnet 4.6 - Best speed and intelligence balance", "claude-sonnet-4-6"), + ("Claude Sonnet 4.5 - Agents and coding", "claude-sonnet-4-5"), ], "google": [ - ("Gemini 3 Pro - Reasoning-first", "gemini-3-pro-preview"), + ("Gemini 3.1 Pro - Reasoning-first, complex workflows", "gemini-3.1-pro-preview"), ("Gemini 3 Flash - Next-gen fast", "gemini-3-flash-preview"), - ("Gemini 2.5 Flash - Balanced, recommended", "gemini-2.5-flash"), + ("Gemini 2.5 Pro - Stable pro model", "gemini-2.5-pro"), + ("Gemini 2.5 Flash - Balanced, stable", "gemini-2.5-flash"), ], "xai": [ + ("Grok 4 - Flagship model", "grok-4-0709"), ("Grok 4.1 Fast (Reasoning) - High-performance, 2M ctx", "grok-4-1-fast-reasoning"), ("Grok 4 Fast (Reasoning) - High-performance", "grok-4-fast-reasoning"), - ("Grok 4 - Flagship model", "grok-4-0709"), ("Grok 4.1 Fast (Non-Reasoning) - Speed optimized, 2M ctx", "grok-4-1-fast-non-reasoning"), - ("Grok 4 Fast (Non-Reasoning) - Speed optimized", "grok-4-fast-non-reasoning"), ], "openrouter": [ - ("DeepSeek R1 - Strong open-source reasoning", "deepseek/deepseek-r1"), ("Z.AI GLM 4.5 Air (free)", "z-ai/glm-4.5-air:free"), ("NVIDIA Nemotron 3 Nano 30B (free)", "nvidia/nemotron-3-nano-30b-a3b:free"), ], diff --git a/pyproject.toml b/pyproject.toml index d230f21e..b32ac09c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,14 +4,13 @@ build-backend = "setuptools.build_meta" [project] name = "tradingagents" -version = "0.2.0" +version = "0.2.1" description = "TradingAgents: Multi-Agents LLM Financial Trading Framework" readme = "README.md" requires-python = ">=3.10" dependencies = [ "langchain-core>=0.3.81", "backtrader>=1.9.78.123", - "chainlit>=2.5.5", "langchain-anthropic>=0.3.15", "langchain-experimental>=0.3.4", "langchain-google-genai>=2.1.5", diff --git a/requirements.txt b/requirements.txt index 9e51ed98..184468b8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,6 @@ requests tqdm pytz redis -chainlit rich typer questionary diff --git a/tradingagents/__init__.py b/tradingagents/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tradingagents/agents/utils/technical_indicators_tools.py b/tradingagents/agents/utils/technical_indicators_tools.py index c6c08bca..77acf09c 100644 --- a/tradingagents/agents/utils/technical_indicators_tools.py +++ b/tradingagents/agents/utils/technical_indicators_tools.py @@ -10,14 +10,22 @@ def get_indicators( look_back_days: Annotated[int, "how many days to look back"] = 30, ) -> str: """ - Retrieve technical indicators for a given ticker symbol. + Retrieve a single technical indicator for a given ticker symbol. Uses the configured technical_indicators vendor. Args: symbol (str): Ticker symbol of the company, e.g. AAPL, TSM - indicator (str): Technical indicator to get the analysis and report of + indicator (str): A single technical indicator name, e.g. 'rsi', 'macd'. Call this tool once per indicator. curr_date (str): The current trading date you are trading on, YYYY-mm-dd look_back_days (int): How many days to look back, default is 30 Returns: str: A formatted dataframe containing the technical indicators for the specified ticker symbol and indicator. """ - return route_to_vendor("get_indicators", symbol, indicator, curr_date, look_back_days) \ No newline at end of file + # LLMs sometimes pass multiple indicators as a comma-separated string; + # split and process each individually. + indicators = [i.strip() for i in indicator.split(",") if i.strip()] + if len(indicators) > 1: + results = [] + for ind in indicators: + results.append(route_to_vendor("get_indicators", symbol, ind, curr_date, look_back_days)) + return "\n\n".join(results) + return route_to_vendor("get_indicators", symbol, indicator.strip(), curr_date, look_back_days) \ No newline at end of file diff --git a/tradingagents/dataflows/stockstats_utils.py b/tradingagents/dataflows/stockstats_utils.py index b31935b7..467156a2 100644 --- a/tradingagents/dataflows/stockstats_utils.py +++ b/tradingagents/dataflows/stockstats_utils.py @@ -6,6 +6,19 @@ import os from .config import get_config +def _clean_dataframe(data: pd.DataFrame) -> pd.DataFrame: + """Normalize a stock DataFrame for stockstats: parse dates, drop invalid rows, fill price gaps.""" + data["Date"] = pd.to_datetime(data["Date"], errors="coerce") + data = data.dropna(subset=["Date"]) + + price_cols = [c for c in ["Open", "High", "Low", "Close", "Volume"] if c in data.columns] + data[price_cols] = data[price_cols].apply(pd.to_numeric, errors="coerce") + data = data.dropna(subset=["Close"]) + data[price_cols] = data[price_cols].ffill().bfill() + + return data + + class StockstatsUtils: @staticmethod def get_stock_stats( @@ -36,8 +49,7 @@ class StockstatsUtils: ) if os.path.exists(data_file): - data = pd.read_csv(data_file) - data["Date"] = pd.to_datetime(data["Date"]) + data = pd.read_csv(data_file, on_bad_lines="skip") else: data = yf.download( symbol, @@ -50,6 +62,7 @@ class StockstatsUtils: data = data.reset_index() data.to_csv(data_file, index=False) + data = _clean_dataframe(data) df = wrap(data) df["Date"] = df["Date"].dt.strftime("%Y-%m-%d") curr_date_str = curr_date_dt.strftime("%Y-%m-%d") diff --git a/tradingagents/dataflows/y_finance.py b/tradingagents/dataflows/y_finance.py index bc78d8b3..b915490d 100644 --- a/tradingagents/dataflows/y_finance.py +++ b/tradingagents/dataflows/y_finance.py @@ -3,7 +3,7 @@ from datetime import datetime from dateutil.relativedelta import relativedelta import yfinance as yf import os -from .stockstats_utils import StockstatsUtils +from .stockstats_utils import StockstatsUtils, _clean_dataframe def get_YFin_data_online( symbol: Annotated[str, "ticker symbol of the company"], @@ -209,31 +209,30 @@ def _get_stock_stats_bulk( os.path.join( config.get("data_cache_dir", "data"), f"{symbol}-YFin-data-2015-01-01-2025-03-25.csv", - ) + ), + on_bad_lines="skip", ) - df = wrap(data) except FileNotFoundError: raise Exception("Stockstats fail: Yahoo Finance data not fetched yet!") else: # Online data fetching with caching today_date = pd.Timestamp.today() curr_date_dt = pd.to_datetime(curr_date) - + end_date = today_date start_date = today_date - pd.DateOffset(years=15) start_date_str = start_date.strftime("%Y-%m-%d") end_date_str = end_date.strftime("%Y-%m-%d") - + os.makedirs(config["data_cache_dir"], exist_ok=True) - + data_file = os.path.join( config["data_cache_dir"], f"{symbol}-YFin-data-{start_date_str}-{end_date_str}.csv", ) - + if os.path.exists(data_file): - data = pd.read_csv(data_file) - data["Date"] = pd.to_datetime(data["Date"]) + data = pd.read_csv(data_file, on_bad_lines="skip") else: data = yf.download( symbol, @@ -245,9 +244,10 @@ def _get_stock_stats_bulk( ) data = data.reset_index() data.to_csv(data_file, index=False) - - df = wrap(data) - df["Date"] = df["Date"].dt.strftime("%Y-%m-%d") + + data = _clean_dataframe(data) + df = wrap(data) + df["Date"] = df["Date"].dt.strftime("%Y-%m-%d") # Calculate the indicator for all rows at once df[indicator] # This triggers stockstats to calculate the indicator diff --git a/tradingagents/graph/propagation.py b/tradingagents/graph/propagation.py index 7aba5258..0fd10c0c 100644 --- a/tradingagents/graph/propagation.py +++ b/tradingagents/graph/propagation.py @@ -24,14 +24,26 @@ class Propagator: "company_of_interest": company_name, "trade_date": str(trade_date), "investment_debate_state": InvestDebateState( - {"history": "", "current_response": "", "count": 0} + { + "bull_history": "", + "bear_history": "", + "history": "", + "current_response": "", + "judge_decision": "", + "count": 0, + } ), "risk_debate_state": RiskDebateState( { + "aggressive_history": "", + "conservative_history": "", + "neutral_history": "", "history": "", + "latest_speaker": "", "current_aggressive_response": "", "current_conservative_response": "", "current_neutral_response": "", + "judge_decision": "", "count": 0, } ), diff --git a/tradingagents/graph/trading_graph.py b/tradingagents/graph/trading_graph.py index b1560f90..e4f09df4 100644 --- a/tradingagents/graph/trading_graph.py +++ b/tradingagents/graph/trading_graph.py @@ -326,6 +326,7 @@ class TradingAgentsGraph: with open( f"eval_results/{self.ticker}/TradingAgentsStrategy_logs/full_states_log_{trade_date}.json", "w", + encoding="utf-8", ) as f: json.dump(self.log_states_dict, f, indent=4) diff --git a/tradingagents/llm_clients/anthropic_client.py b/tradingagents/llm_clients/anthropic_client.py index e2f1abba..8539c752 100644 --- a/tradingagents/llm_clients/anthropic_client.py +++ b/tradingagents/llm_clients/anthropic_client.py @@ -16,7 +16,7 @@ class AnthropicClient(BaseLLMClient): """Return configured ChatAnthropic instance.""" llm_kwargs = {"model": self.model} - for key in ("timeout", "max_retries", "api_key", "max_tokens", "callbacks"): + for key in ("timeout", "max_retries", "api_key", "max_tokens", "callbacks", "http_client", "http_async_client"): if key in self.kwargs: llm_kwargs[key] = self.kwargs[key] diff --git a/tradingagents/llm_clients/factory.py b/tradingagents/llm_clients/factory.py index 028c88a2..93c2a7d3 100644 --- a/tradingagents/llm_clients/factory.py +++ b/tradingagents/llm_clients/factory.py @@ -19,6 +19,12 @@ def create_llm_client( model: Model name/identifier base_url: Optional base URL for API endpoint **kwargs: Additional provider-specific arguments + - http_client: Custom httpx.Client for SSL proxy or certificate customization + - http_async_client: Custom httpx.AsyncClient for async operations + - timeout: Request timeout in seconds + - max_retries: Maximum retry attempts + - api_key: API key for the provider + - callbacks: LangChain callbacks Returns: Configured BaseLLMClient instance diff --git a/tradingagents/llm_clients/google_client.py b/tradingagents/llm_clients/google_client.py index a1bd386b..3dd85e3f 100644 --- a/tradingagents/llm_clients/google_client.py +++ b/tradingagents/llm_clients/google_client.py @@ -38,7 +38,7 @@ class GoogleClient(BaseLLMClient): """Return configured ChatGoogleGenerativeAI instance.""" llm_kwargs = {"model": self.model} - for key in ("timeout", "max_retries", "google_api_key", "callbacks"): + for key in ("timeout", "max_retries", "google_api_key", "callbacks", "http_client", "http_async_client"): if key in self.kwargs: llm_kwargs[key] = self.kwargs[key] diff --git a/tradingagents/llm_clients/openai_client.py b/tradingagents/llm_clients/openai_client.py index 1076dacf..d231c8ab 100644 --- a/tradingagents/llm_clients/openai_client.py +++ b/tradingagents/llm_clients/openai_client.py @@ -8,25 +8,23 @@ from .validators import validate_model class UnifiedChatOpenAI(ChatOpenAI): - """ChatOpenAI subclass that strips incompatible params for certain models.""" + """ChatOpenAI subclass that strips temperature/top_p for GPT-5 family models. + + GPT-5 family models use reasoning natively. temperature/top_p are only + accepted when reasoning.effort is 'none'; with any other effort level + (or for older GPT-5/GPT-5-mini/GPT-5-nano which always reason) the API + rejects these params. Langchain defaults temperature=0.7, so we must + strip it to avoid errors. + + Non-GPT-5 models (GPT-4.1, xAI, Ollama, etc.) are unaffected. + """ def __init__(self, **kwargs): - model = kwargs.get("model", "") - if self._is_reasoning_model(model): + if "gpt-5" in kwargs.get("model", "").lower(): kwargs.pop("temperature", None) kwargs.pop("top_p", None) super().__init__(**kwargs) - @staticmethod - def _is_reasoning_model(model: str) -> bool: - """Check if model is a reasoning model that doesn't support temperature.""" - model_lower = model.lower() - return ( - model_lower.startswith("o1") - or model_lower.startswith("o3") - or "gpt-5" in model_lower - ) - class OpenAIClient(BaseLLMClient): """Client for OpenAI, Ollama, OpenRouter, and xAI providers.""" @@ -65,7 +63,7 @@ class OpenAIClient(BaseLLMClient): elif self.base_url: llm_kwargs["base_url"] = self.base_url - for key in ("timeout", "max_retries", "reasoning_effort", "api_key", "callbacks"): + for key in ("timeout", "max_retries", "reasoning_effort", "api_key", "callbacks", "http_client", "http_async_client"): if key in self.kwargs: llm_kwargs[key] = self.kwargs[key] diff --git a/tradingagents/llm_clients/validators.py b/tradingagents/llm_clients/validators.py index 3c0f2290..1e2388b3 100644 --- a/tradingagents/llm_clients/validators.py +++ b/tradingagents/llm_clients/validators.py @@ -6,59 +6,44 @@ Let LLM providers use their own defaults for unspecified params. VALID_MODELS = { "openai": [ - # GPT-5 series (2025) + # GPT-5 series + "gpt-5.4-pro", + "gpt-5.4", "gpt-5.2", "gpt-5.1", "gpt-5", "gpt-5-mini", "gpt-5-nano", - # GPT-4.1 series (2025) + # GPT-4.1 series "gpt-4.1", "gpt-4.1-mini", "gpt-4.1-nano", - # o-series reasoning models - "o4-mini", - "o3", - "o3-mini", - "o1", - "o1-preview", - # GPT-4o series (legacy but still supported) - "gpt-4o", - "gpt-4o-mini", ], "anthropic": [ - # Claude 4.5 series (2025) + # Claude 4.6 series (latest) + "claude-opus-4-6", + "claude-sonnet-4-6", + # Claude 4.5 series "claude-opus-4-5", "claude-sonnet-4-5", "claude-haiku-4-5", - # Claude 4.x series - "claude-opus-4-1-20250805", - "claude-sonnet-4-20250514", - # Claude 3.7 series - "claude-3-7-sonnet-20250219", - # Claude 3.5 series (legacy) - "claude-3-5-haiku-20241022", - "claude-3-5-sonnet-20241022", ], "google": [ + # Gemini 3.1 series (preview) + "gemini-3.1-pro-preview", + "gemini-3.1-flash-lite-preview", # Gemini 3 series (preview) - "gemini-3-pro-preview", "gemini-3-flash-preview", # Gemini 2.5 series "gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.5-flash-lite", - # Gemini 2.0 series - "gemini-2.0-flash", - "gemini-2.0-flash-lite", ], "xai": [ # Grok 4.1 series - "grok-4-1-fast", "grok-4-1-fast-reasoning", "grok-4-1-fast-non-reasoning", # Grok 4 series - "grok-4", "grok-4-0709", "grok-4-fast-reasoning", "grok-4-fast-non-reasoning",