"""LLM and search provider definitions for the Setup Wizard.""" from __future__ import annotations from dataclasses import dataclass, field @dataclass class LLMProvider: name: str display_name: str description: str use: str models: list[str] default_model: str env_var: str | None package: str | None # Optional: some providers use a different field name for the API key in YAML api_key_field: str = "api_key" # Extra config fields beyond the common ones (merged into YAML) extra_config: dict = field(default_factory=dict) # Per-model supports_vision overrides for providers whose models differ in # capability (e.g. MiniMax M3 supports vision but M2.7 is text-only). The # provider-level extra_config holds the default (default_model) capability. model_vision_overrides: dict[str, bool] = field(default_factory=dict) auth_hint: str | None = None base_url_prompt: str | None = None model_prompt: str | None = None def extra_config_for(self, model_name: str) -> dict: """Return extra_config for a selected model, applying per-model overrides. Does not mutate the shared provider-level ``extra_config``. """ config = dict(self.extra_config) if model_name in self.model_vision_overrides: config["supports_vision"] = self.model_vision_overrides[model_name] return config @dataclass class WebProvider: name: str display_name: str description: str use: str env_var: str | None # None = no API key required tool_name: str extra_config: dict = field(default_factory=dict) @dataclass class SearchProvider: name: str display_name: str description: str use: str env_var: str | None # None = no API key required tool_name: str = "web_search" extra_config: dict = field(default_factory=dict) OPENAI_COMPAT_THINKING_CONFIG = { "supports_thinking": True, "when_thinking_enabled": { "extra_body": { "thinking": { "type": "enabled", } } }, "when_thinking_disabled": { "extra_body": { "thinking": { "type": "disabled", } } }, } ANTHROPIC_THINKING_CONFIG = { "supports_thinking": True, "when_thinking_enabled": { "thinking": { "type": "enabled", "budget_tokens": 4096, } }, "when_thinking_disabled": { "thinking": { "type": "disabled", } }, } LLM_PROVIDERS: list[LLMProvider] = [ LLMProvider( name="volcengine", display_name="Volcengine Doubao", description="Doubao Seed with thinking support", use="deerflow.models.patched_deepseek:PatchedChatDeepSeek", models=["doubao-seed-1-8-251228"], default_model="doubao-seed-1-8-251228", env_var="VOLCENGINE_API_KEY", package="langchain-deepseek", extra_config={ "api_base": "https://ark.cn-beijing.volces.com/api/v3", "timeout": 600.0, "max_retries": 2, "supports_vision": True, "supports_reasoning_effort": True, **OPENAI_COMPAT_THINKING_CONFIG, }, ), LLMProvider( name="openai", display_name="OpenAI", description="GPT-5, GPT-4.1, GPT-4o", use="langchain_openai:ChatOpenAI", models=["gpt-5", "gpt-5-mini", "gpt-4.1", "gpt-4o"], default_model="gpt-5", env_var="OPENAI_API_KEY", package="langchain-openai", extra_config={ "request_timeout": 600.0, "max_retries": 2, "max_tokens": 4096, "temperature": 0.7, "supports_vision": True, }, ), LLMProvider( name="openai_responses", display_name="OpenAI Responses API", description="GPT-5 via /v1/responses", use="langchain_openai:ChatOpenAI", models=["gpt-5", "gpt-5-mini"], default_model="gpt-5", env_var="OPENAI_API_KEY", package="langchain-openai", extra_config={ "request_timeout": 600.0, "max_retries": 2, "use_responses_api": True, "output_version": "responses/v1", "supports_vision": True, }, ), LLMProvider( name="anthropic", display_name="Anthropic", description="Claude Sonnet 4 with extended thinking", use="langchain_anthropic:ChatAnthropic", models=["claude-sonnet-4-20250514", "claude-opus-4-5", "claude-sonnet-4-5"], default_model="claude-sonnet-4-20250514", env_var="ANTHROPIC_API_KEY", package="langchain-anthropic", extra_config={ "default_request_timeout": 600.0, "max_retries": 2, "max_tokens": 16000, "supports_vision": True, **ANTHROPIC_THINKING_CONFIG, }, ), LLMProvider( name="deepseek", display_name="DeepSeek", description="DeepSeek Reasoner with thinking support", use="deerflow.models.patched_deepseek:PatchedChatDeepSeek", models=["deepseek-reasoner", "deepseek-chat"], default_model="deepseek-reasoner", env_var="DEEPSEEK_API_KEY", package="langchain-deepseek", extra_config={ "timeout": 600.0, "max_retries": 2, "max_tokens": 8192, "supports_vision": False, **OPENAI_COMPAT_THINKING_CONFIG, }, ), LLMProvider( name="google", display_name="Google Gemini", description="Native Gemini SDK, no thinking support", use="langchain_google_genai:ChatGoogleGenerativeAI", models=["gemini-2.5-pro", "gemini-2.0-flash"], default_model="gemini-2.5-pro", env_var="GEMINI_API_KEY", package="langchain-google-genai", api_key_field="gemini_api_key", extra_config={ "timeout": 600.0, "max_retries": 2, "max_tokens": 8192, "supports_vision": True, }, ), LLMProvider( name="gemini_openai_gateway", display_name="Gemini OpenAI-compatible", description="Gemini thinking via an OpenAI-compatible gateway", use="deerflow.models.patched_openai:PatchedChatOpenAI", models=["google/gemini-2.5-pro-preview"], default_model="google/gemini-2.5-pro-preview", env_var="GEMINI_API_KEY", package="langchain-openai", extra_config={ "request_timeout": 600.0, "max_retries": 2, "max_tokens": 16384, "supports_vision": True, **OPENAI_COMPAT_THINKING_CONFIG, }, base_url_prompt="Gateway base URL (e.g. https://your-gateway.example/v1)", ), LLMProvider( name="ollama_qwen", display_name="Ollama Qwen3", description="Native local Ollama provider with thinking support", use="langchain_ollama:ChatOllama", models=["qwen3:32b"], default_model="qwen3:32b", env_var=None, package="langchain-ollama", extra_config={ "base_url": "http://localhost:11434", "num_predict": 8192, "temperature": 0.7, "reasoning": True, "supports_thinking": True, "supports_vision": False, }, auth_hint="No API key is required. Ensure Ollama is running and the model is pulled.", ), LLMProvider( name="ollama_gemma", display_name="Ollama Gemma", description="Native local Ollama provider with vision support", use="langchain_ollama:ChatOllama", models=["gemma4:27b"], default_model="gemma4:27b", env_var=None, package="langchain-ollama", extra_config={ "base_url": "http://localhost:11434", "num_predict": 8192, "temperature": 0.7, "reasoning": True, "supports_thinking": True, "supports_vision": True, }, auth_hint="No API key is required. Ensure Ollama is running and the model is pulled.", ), LLMProvider( name="mimo", display_name="Xiaomi MiMo", description="MiMo thinking models with reasoning replay", use="deerflow.models.patched_mimo:PatchedChatMiMo", models=["mimo-v2.5-pro", "mimo-v2.5", "mimo-v2-pro", "mimo-v2-omni", "mimo-v2-flash"], default_model="mimo-v2.5-pro", env_var="MIMO_API_KEY", package="langchain-openai", extra_config={ "base_url": "https://api.xiaomimimo.com/v1", "request_timeout": 600.0, "max_retries": 2, "max_tokens": 8192, "supports_vision": False, **OPENAI_COMPAT_THINKING_CONFIG, }, ), LLMProvider( name="kimi", display_name="Moonshot Kimi", description="Kimi K2.5 with thinking support", use="deerflow.models.patched_deepseek:PatchedChatDeepSeek", models=["kimi-k2.5"], default_model="kimi-k2.5", env_var="MOONSHOT_API_KEY", package="langchain-deepseek", extra_config={ "api_base": "https://api.moonshot.cn/v1", "timeout": 600.0, "max_retries": 2, "max_tokens": 32768, "supports_vision": True, **OPENAI_COMPAT_THINKING_CONFIG, }, ), LLMProvider( name="novita", display_name="Novita AI", description="DeepSeek V3.2 via OpenAI-compatible API", use="langchain_openai:ChatOpenAI", models=["deepseek/deepseek-v3.2"], default_model="deepseek/deepseek-v3.2", env_var="NOVITA_API_KEY", package="langchain-openai", extra_config={ "base_url": "https://api.novita.ai/openai", "request_timeout": 600.0, "max_retries": 2, "max_tokens": 4096, "temperature": 0.7, "supports_vision": True, **OPENAI_COMPAT_THINKING_CONFIG, }, ), LLMProvider( name="minimax", display_name="MiniMax", description="International OpenAI-compatible endpoint", use="langchain_openai:ChatOpenAI", models=["MiniMax-M3", "MiniMax-M2.7", "MiniMax-M2.7-highspeed"], default_model="MiniMax-M3", env_var="MINIMAX_API_KEY", package="langchain-openai", extra_config={ "base_url": "https://api.minimax.io/v1", "request_timeout": 600.0, "max_retries": 2, "max_tokens": 4096, "temperature": 1.0, "supports_vision": True, "supports_thinking": True, }, model_vision_overrides={ "MiniMax-M2.7": False, "MiniMax-M2.7-highspeed": False, }, ), LLMProvider( name="minimax_cn", display_name="MiniMax CN", description="China OpenAI-compatible endpoint", use="langchain_openai:ChatOpenAI", models=["MiniMax-M3", "MiniMax-M2.7", "MiniMax-M2.7-highspeed"], default_model="MiniMax-M3", env_var="MINIMAX_API_KEY", package="langchain-openai", extra_config={ "base_url": "https://api.minimaxi.com/v1", "request_timeout": 600.0, "max_retries": 2, "max_tokens": 4096, "temperature": 1.0, "supports_vision": True, "supports_thinking": True, }, model_vision_overrides={ "MiniMax-M2.7": False, "MiniMax-M2.7-highspeed": False, }, ), LLMProvider( name="openrouter", display_name="OpenRouter", description="OpenAI-compatible gateway with broad model catalog", use="langchain_openai:ChatOpenAI", models=["google/gemini-2.5-flash-preview", "openai/gpt-5-mini", "anthropic/claude-sonnet-4"], default_model="google/gemini-2.5-flash-preview", env_var="OPENROUTER_API_KEY", package="langchain-openai", extra_config={ "base_url": "https://openrouter.ai/api/v1", "request_timeout": 600.0, "max_retries": 2, "max_tokens": 8192, "temperature": 0.7, }, ), LLMProvider( name="vllm", display_name="vLLM", description="Self-hosted OpenAI-compatible serving", use="deerflow.models.vllm_provider:VllmChatModel", models=["Qwen/Qwen3-32B", "Qwen/Qwen2.5-Coder-32B-Instruct"], default_model="Qwen/Qwen3-32B", env_var="VLLM_API_KEY", package=None, extra_config={ "base_url": "http://localhost:8000/v1", "request_timeout": 600.0, "max_retries": 2, "max_tokens": 8192, "supports_thinking": True, "supports_vision": False, "when_thinking_enabled": { "extra_body": { "chat_template_kwargs": { "enable_thinking": True, } } }, "when_thinking_disabled": { "extra_body": { "chat_template_kwargs": { "enable_thinking": False, } } }, }, ), LLMProvider( name="mindie", display_name="MindIE", description="Qwen3-Coder on MindIE Engine", use="deerflow.models.mindie_provider:MindIEChatModel", models=["Qwen3-Coder-480B-A35B-Instruct-Client"], default_model="Qwen3-Coder-480B-A35B-Instruct-Client", env_var="OPENAI_API_KEY", package=None, extra_config={ "base_url": "http://localhost:8989/v1", "temperature": 0, "max_retries": 1, "supports_thinking": False, "supports_vision": False, "supports_reasoning_effort": False, "read_timeout": 900.0, "connect_timeout": 30.0, "write_timeout": 60.0, "pool_timeout": 30.0, }, ), LLMProvider( name="codex", display_name="Codex CLI", description="Uses Codex CLI local auth (~/.codex/auth.json)", use="deerflow.models.openai_codex_provider:CodexChatModel", models=["gpt-5.4", "gpt-5-mini"], default_model="gpt-5.4", env_var=None, package=None, api_key_field="api_key", extra_config={"supports_thinking": True, "supports_reasoning_effort": True}, auth_hint="Uses existing Codex CLI auth from ~/.codex/auth.json", ), LLMProvider( name="claude_code", display_name="Claude Code OAuth", description="Uses Claude Code local OAuth credentials", use="deerflow.models.claude_provider:ClaudeChatModel", models=["claude-sonnet-4-6", "claude-opus-4-1"], default_model="claude-sonnet-4-6", env_var=None, package=None, extra_config={"max_tokens": 4096, "supports_thinking": True}, auth_hint="Uses Claude Code OAuth credentials from your local machine", ), LLMProvider( name="other", display_name="Other OpenAI-compatible", description="Custom gateway with base_url and model name", use="langchain_openai:ChatOpenAI", models=["gpt-4o"], default_model="gpt-4o", env_var="OPENAI_API_KEY", package="langchain-openai", base_url_prompt="Base URL (e.g. https://api.openai.com/v1)", model_prompt="Model name", ), ] SEARCH_PROVIDERS: list[SearchProvider] = [ SearchProvider( name="ddg", display_name="DuckDuckGo (free, no key needed)", description="No API key required", use="deerflow.community.ddg_search.tools:web_search_tool", env_var=None, extra_config={"max_results": 5}, ), SearchProvider( name="tavily", display_name="Tavily", description="Recommended, free tier available", use="deerflow.community.tavily.tools:web_search_tool", env_var="TAVILY_API_KEY", extra_config={"max_results": 5}, ), SearchProvider( name="infoquest", display_name="InfoQuest", description="Higher quality vertical search, API key required", use="deerflow.community.infoquest.tools:web_search_tool", env_var="INFOQUEST_API_KEY", extra_config={"search_time_range": 10}, ), SearchProvider( name="exa", display_name="Exa", description="Neural + keyword web search, API key required", use="deerflow.community.exa.tools:web_search_tool", env_var="EXA_API_KEY", extra_config={ "max_results": 5, "search_type": "auto", "contents_max_characters": 1000, }, ), SearchProvider( name="firecrawl", display_name="Firecrawl", description="Search + crawl via Firecrawl API", use="deerflow.community.firecrawl.tools:web_search_tool", env_var="FIRECRAWL_API_KEY", extra_config={"max_results": 5}, ), ] WEB_FETCH_PROVIDERS: list[WebProvider] = [ WebProvider( name="jina_ai", display_name="Jina AI Reader", description="Good default reader, no API key required", use="deerflow.community.jina_ai.tools:web_fetch_tool", env_var=None, tool_name="web_fetch", extra_config={"timeout": 10}, ), WebProvider( name="exa", display_name="Exa", description="API key required", use="deerflow.community.exa.tools:web_fetch_tool", env_var="EXA_API_KEY", tool_name="web_fetch", ), WebProvider( name="infoquest", display_name="InfoQuest", description="API key required", use="deerflow.community.infoquest.tools:web_fetch_tool", env_var="INFOQUEST_API_KEY", tool_name="web_fetch", extra_config={"timeout": 10, "fetch_time": 10, "navigation_timeout": 30}, ), WebProvider( name="firecrawl", display_name="Firecrawl", description="Search-grade crawl with markdown output, API key required", use="deerflow.community.firecrawl.tools:web_fetch_tool", env_var="FIRECRAWL_API_KEY", tool_name="web_fetch", ), ]