diff --git a/.env.example b/.env.example index af92fcf93..458d74956 100644 --- a/.env.example +++ b/.env.example @@ -5,7 +5,9 @@ ANTHROPIC_API_KEY= XAI_API_KEY= DEEPSEEK_API_KEY= DASHSCOPE_API_KEY= +DASHSCOPE_CN_API_KEY= ZHIPU_API_KEY= +ZHIPU_CN_API_KEY= MINIMAX_API_KEY= MINIMAX_CN_API_KEY= OPENROUTER_API_KEY= diff --git a/README.md b/README.md index a897263f2..b4578a8c9 100644 --- a/README.md +++ b/README.md @@ -142,8 +142,10 @@ export GOOGLE_API_KEY=... # Google (Gemini) export ANTHROPIC_API_KEY=... # Anthropic (Claude) export XAI_API_KEY=... # xAI (Grok) export DEEPSEEK_API_KEY=... # DeepSeek -export DASHSCOPE_API_KEY=... # Qwen (Alibaba DashScope) -export ZHIPU_API_KEY=... # GLM (Zhipu) +export DASHSCOPE_API_KEY=... # Qwen — International (dashscope-intl.aliyuncs.com) +export DASHSCOPE_CN_API_KEY=... # Qwen — China (dashscope.aliyuncs.com) +export ZHIPU_API_KEY=... # GLM via Z.AI (international) +export ZHIPU_CN_API_KEY=... # GLM via BigModel (China, open.bigmodel.cn) export MINIMAX_API_KEY=... # MiniMax — Global (api.minimax.io, M2.x, 204K ctx) export MINIMAX_CN_API_KEY=... # MiniMax — China (api.minimaxi.com, M2.x, 204K ctx) export OPENROUTER_API_KEY=... # OpenRouter @@ -186,7 +188,7 @@ An interface will appear showing results as they load, letting you track the age ### Implementation Details -We built TradingAgents with LangGraph to ensure flexibility and modularity. The framework supports multiple LLM providers: OpenAI, Google, Anthropic, xAI, DeepSeek, Qwen (Alibaba DashScope), GLM (Zhipu), MiniMax (global + China), OpenRouter, Ollama for local models, and Azure OpenAI for enterprise. +We built TradingAgents with LangGraph to ensure flexibility and modularity. The framework supports multiple LLM providers: OpenAI, Google, Anthropic, xAI, DeepSeek, Qwen (Alibaba DashScope, international and China endpoints), GLM (Zhipu), MiniMax (global + China), OpenRouter, Ollama for local models, and Azure OpenAI for enterprise. ### Python Usage @@ -210,7 +212,7 @@ from tradingagents.graph.trading_graph import TradingAgentsGraph from tradingagents.default_config import DEFAULT_CONFIG config = DEFAULT_CONFIG.copy() -config["llm_provider"] = "openai" # openai, google, anthropic, xai, deepseek, qwen, glm, minimax, minimax-cn, openrouter, ollama, azure +config["llm_provider"] = "openai" # openai, google, anthropic, xai, deepseek, qwen, qwen-cn, glm, glm-cn, minimax, minimax-cn, openrouter, ollama, azure config["deep_think_llm"] = "gpt-5.4" # Model for complex reasoning config["quick_think_llm"] = "gpt-5.4-mini" # Model for quick tasks config["max_debate_rounds"] = 2 diff --git a/cli/main.py b/cli/main.py index 478b82fde..c466cb219 100644 --- a/cli/main.py +++ b/cli/main.py @@ -566,6 +566,8 @@ def get_user_selections(): selected_llm_provider, backend_url = ask_qwen_region() elif selected_llm_provider == "minimax": selected_llm_provider, backend_url = ask_minimax_region() + elif selected_llm_provider == "glm": + selected_llm_provider, backend_url = ask_glm_region() # Step 7: Thinking agents console.print( diff --git a/cli/utils.py b/cli/utils.py index 1245eea78..1ccc12302 100644 --- a/cli/utils.py +++ b/cli/utils.py @@ -329,6 +329,32 @@ def ask_gemini_thinking_config() -> str | None: ).ask() +def ask_glm_region() -> tuple[str, str]: + """Ask which GLM platform (Z.AI international vs BigModel China) to use. + + Zhipu serves the same GLM models under two brands with separate + accounts; keys aren't interchangeable. Returns (provider_key, backend_url). + """ + return questionary.select( + "Select GLM platform:", + choices=[ + questionary.Choice( + "Z.AI — api.z.ai (international, uses ZHIPU_API_KEY)", + value=("glm", "https://api.z.ai/api/paas/v4/"), + ), + questionary.Choice( + "BigModel — open.bigmodel.cn (China, uses ZHIPU_CN_API_KEY)", + value=("glm-cn", "https://open.bigmodel.cn/api/paas/v4/"), + ), + ], + style=questionary.Style([ + ("selected", "fg:cyan noinherit"), + ("highlighted", "fg:cyan noinherit"), + ("pointer", "fg:cyan noinherit"), + ]), + ).ask() + + def ask_qwen_region() -> tuple[str, str]: """Ask which Qwen region (international vs China) to use. diff --git a/tests/conftest.py b/tests/conftest.py index 5983446f5..506510cea 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -18,7 +18,9 @@ _API_KEY_ENV_VARS = ( "XAI_API_KEY", "DEEPSEEK_API_KEY", "DASHSCOPE_API_KEY", + "DASHSCOPE_CN_API_KEY", "ZHIPU_API_KEY", + "ZHIPU_CN_API_KEY", "MINIMAX_API_KEY", "MINIMAX_CN_API_KEY", "OPENROUTER_API_KEY", diff --git a/tradingagents/llm_clients/factory.py b/tradingagents/llm_clients/factory.py index 32c3bed31..9bf1d9fba 100644 --- a/tradingagents/llm_clients/factory.py +++ b/tradingagents/llm_clients/factory.py @@ -4,7 +4,9 @@ from .base_client import BaseLLMClient # Providers that use the OpenAI-compatible chat completions API _OPENAI_COMPATIBLE = ( - "openai", "xai", "deepseek", "qwen", "glm", + "openai", "xai", "deepseek", + "qwen", "qwen-cn", + "glm", "glm-cn", "minimax", "minimax-cn", "ollama", "openrouter", ) diff --git a/tradingagents/llm_clients/model_catalog.py b/tradingagents/llm_clients/model_catalog.py index 6a9ca999e..fac741bcf 100644 --- a/tradingagents/llm_clients/model_catalog.py +++ b/tradingagents/llm_clients/model_catalog.py @@ -8,6 +8,25 @@ ModelOption = Tuple[str, str] ProviderModeOptions = Dict[str, Dict[str, List[ModelOption]]] +# Shared model list for GLM via Z.AI (international) and BigModel (China). +# Source: docs.z.ai (GLM Coding Plan supported models + LLM guides). +# All GLM 4.7+ entries support thinking mode via thinking={"type":"enabled"}. +_GLM_MODELS: Dict[str, List[ModelOption]] = { + "quick": [ + ("GLM-5-Turbo - Fast, switchable thinking modes", "glm-5-turbo"), + ("GLM-4.7 - Previous-gen flagship", "glm-4.7"), + ("GLM-4.5-Air - Lightweight, cost-efficient", "glm-4.5-air"), + ("Custom model ID", "custom"), + ], + "deep": [ + ("GLM-5.1 - Latest flagship, 204K ctx", "glm-5.1"), + ("GLM-5 - Flagship, 204K ctx", "glm-5"), + ("GLM-4.7 - Previous-gen flagship", "glm-4.7"), + ("Custom model ID", "custom"), + ], +} + + # Shared model list for Qwen's global (dashscope-intl) and CN (dashscope) endpoints. # Source: modelstudio.console.alibabacloud.com (Featured Models — Flagship + Cost-optimized). # @@ -126,18 +145,10 @@ MODEL_OPTIONS: ProviderModeOptions = { # (dashscope) endpoints, so the two provider keys share one model list. "qwen": _QWEN_MODELS, "qwen-cn": _QWEN_MODELS, - "glm": { - "quick": [ - ("GLM-4.7", "glm-4.7"), - ("GLM-5", "glm-5"), - ("Custom model ID", "custom"), - ], - "deep": [ - ("GLM-5.1", "glm-5.1"), - ("GLM-5", "glm-5"), - ("Custom model ID", "custom"), - ], - }, + # GLM: Z.AI (international) and BigModel (China) host the same model + # IDs; the two provider keys share one model list. + "glm": _GLM_MODELS, + "glm-cn": _GLM_MODELS, # MiniMax: same model IDs across global (.io) and China (.com) regions, # so the two provider keys share one model list. "minimax": _MINIMAX_MODELS, diff --git a/tradingagents/llm_clients/openai_client.py b/tradingagents/llm_clients/openai_client.py index 5a159a12d..89c67e31d 100644 --- a/tradingagents/llm_clients/openai_client.py +++ b/tradingagents/llm_clients/openai_client.py @@ -139,8 +139,16 @@ _PASSTHROUGH_KWARGS = ( _PROVIDER_CONFIG = { "xai": ("https://api.x.ai/v1", "XAI_API_KEY"), "deepseek": ("https://api.deepseek.com", "DEEPSEEK_API_KEY"), + # DashScope exposes two regional endpoints with separate accounts; an + # international key won't authenticate against the China endpoint and + # vice versa (fixes issue #758). "qwen": ("https://dashscope-intl.aliyuncs.com/compatible-mode/v1", "DASHSCOPE_API_KEY"), + "qwen-cn": ("https://dashscope.aliyuncs.com/compatible-mode/v1", "DASHSCOPE_CN_API_KEY"), + # Zhipu exposes the same GLM models under two brands with separate + # accounts: Z.AI (international, api.z.ai) and BigModel + # (open.bigmodel.cn, China). Keys aren't interchangeable across them. "glm": ("https://api.z.ai/api/paas/v4/", "ZHIPU_API_KEY"), + "glm-cn": ("https://open.bigmodel.cn/api/paas/v4/", "ZHIPU_CN_API_KEY"), # MiniMax exposes two regional endpoints with separate keys; mainland # Chinese users hit .com while global users hit .io. "minimax": ("https://api.minimax.io/v1", "MINIMAX_API_KEY"),