diff --git a/tradingagents/dataflows/interface.py b/tradingagents/dataflows/interface.py index 0caf4b68..117ec9b9 100644 --- a/tradingagents/dataflows/interface.py +++ b/tradingagents/dataflows/interface.py @@ -132,10 +132,17 @@ def get_vendor(category: str, method: str = None) -> str: return config.get("data_vendors", {}).get(category, "default") def route_to_vendor(method: str, *args, **kwargs): - """Route method calls to appropriate vendor implementation with fallback support.""" + """Route method calls to appropriate vendor implementation with fallback support. + + Fallback policy: + - Try configured vendor order first, then other available vendors. + - On any vendor error, continue trying next vendor. + - Return first successful result. + - If all vendors fail, raise a summarized runtime error. + """ category = get_category_for_method(method) vendor_config = get_vendor(category, method) - primary_vendors = [v.strip() for v in vendor_config.split(',')] + primary_vendors = [v.strip() for v in vendor_config.split(',') if v.strip()] if method not in VENDOR_METHODS: raise ValueError(f"Method '{method}' not supported") @@ -147,6 +154,7 @@ def route_to_vendor(method: str, *args, **kwargs): if vendor not in fallback_vendors: fallback_vendors.append(vendor) + errors = [] for vendor in fallback_vendors: if vendor not in VENDOR_METHODS[method]: continue @@ -156,7 +164,9 @@ def route_to_vendor(method: str, *args, **kwargs): try: return impl_func(*args, **kwargs) - except AlphaVantageRateLimitError: - continue # Only rate limits trigger fallback + except Exception as e: + errors.append(f"{vendor}: {type(e).__name__}: {e}") + continue - raise RuntimeError(f"No available vendor for '{method}'") \ No newline at end of file + details = " | ".join(errors) if errors else "no vendor candidates" + raise RuntimeError(f"No available vendor for '{method}'. Tried: {details}") \ No newline at end of file diff --git a/tradingagents/default_config.py b/tradingagents/default_config.py index 45cec1ed..091f1bc0 100644 --- a/tradingagents/default_config.py +++ b/tradingagents/default_config.py @@ -12,6 +12,7 @@ DEFAULT_CONFIG = { "deep_think_llm": "gpt-5.2", "quick_think_llm": "gpt-5-mini", "backend_url": "https://api.openai.com/v1", + "default_headers": {"User-Agent": "curl/8.0"}, "factor_rules_path": os.getenv("TRADINGAGENTS_FACTOR_RULES_PATH", ""), # Provider-specific thinking configuration "google_thinking_level": None, # "high", "minimal", etc. diff --git a/tradingagents/graph/trading_graph.py b/tradingagents/graph/trading_graph.py index a72e2f6e..e6dcd19d 100644 --- a/tradingagents/graph/trading_graph.py +++ b/tradingagents/graph/trading_graph.py @@ -145,6 +145,10 @@ class TradingAgentsGraph: if reasoning_effort: kwargs["reasoning_effort"] = reasoning_effort + default_headers = self.config.get("default_headers") + if default_headers: + kwargs["default_headers"] = default_headers + return kwargs def _create_tool_nodes(self) -> Dict[str, ToolNode]: diff --git a/tradingagents/llm_clients/openai_client.py b/tradingagents/llm_clients/openai_client.py index 7011895f..c365daae 100644 --- a/tradingagents/llm_clients/openai_client.py +++ b/tradingagents/llm_clients/openai_client.py @@ -61,7 +61,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", "default_headers"): if key in self.kwargs: llm_kwargs[key] = self.kwargs[key]