From 6ed76943d33f7fc9d14ffd95e809775f87f117c2 Mon Sep 17 00:00:00 2001 From: hazeone <709547807@qq.com> Date: Mon, 24 Nov 2025 19:02:14 +0800 Subject: [PATCH] fix deepseek provider --- python/configs/agents/news_agent.yaml | 2 +- python/configs/providers/deepseek.yaml | 9 ++- python/configs/providers/openrouter.yaml | 6 +- python/valuecell/adapters/models/__init__.py | 6 ++ python/valuecell/adapters/models/factory.py | 34 ++++++++ python/valuecell/utils/model.py | 81 +++++++++++++++++++- 6 files changed, 131 insertions(+), 7 deletions(-) diff --git a/python/configs/agents/news_agent.yaml b/python/configs/agents/news_agent.yaml index b377064c6..c9c7d84b7 100644 --- a/python/configs/agents/news_agent.yaml +++ b/python/configs/agents/news_agent.yaml @@ -18,7 +18,7 @@ enabled: true models: # Primary model for news analysis, summarization, and query processing primary: - model_id: "google/gemini-2.5-pro" + model_id: "google/gemini-3-pro-preview" provider: "openrouter" # Must explicitly specify provider (not null) # Provider-specific model mappings for fallback diff --git a/python/configs/providers/deepseek.yaml b/python/configs/providers/deepseek.yaml index d5d17a241..9cfd652e5 100644 --- a/python/configs/providers/deepseek.yaml +++ b/python/configs/providers/deepseek.yaml @@ -17,8 +17,15 @@ default_model: "deepseek-chat" # Model Parameters Defaults defaults: temperature: 0.7 + max_tokens: 8096 # Available Models (commonly used) models: - id: "deepseek-chat" - name: "DeepSeek Chat" \ No newline at end of file + name: "DeepSeek Chat" + context_length: 128000 + description: "DeepSeek Chat model" + - id: "deepseek-reasoner" + name: "DeepSeek Reasoner" + context_length: 128000 + description: "DeepSeek Reasoner model with enhanced reasoning capabilities" diff --git a/python/configs/providers/openrouter.yaml b/python/configs/providers/openrouter.yaml index e058772e2..30a3188af 100644 --- a/python/configs/providers/openrouter.yaml +++ b/python/configs/providers/openrouter.yaml @@ -35,8 +35,10 @@ models: name: "GPT-5" - id: "google/gemini-2.5-flash" name: "Gemini 2.5 Flash" - - id: "google/gemini-2.5-pro" - name: "Gemini 2.5 Pro" + - id: "google/gemini-3-pro-preview" + name: "Gemini 3 Pro Preview" + - id: "x-ai/grok-4.1-fast" + name: "Grok 4.1 Fast(free)" # ============================================ # Embedding Models Configuration diff --git a/python/valuecell/adapters/models/__init__.py b/python/valuecell/adapters/models/__init__.py index ff53633b9..562247e80 100644 --- a/python/valuecell/adapters/models/__init__.py +++ b/python/valuecell/adapters/models/__init__.py @@ -21,9 +21,12 @@ from valuecell.adapters.models.factory import ( AzureProvider, + DeepSeekProvider, GoogleProvider, ModelFactory, ModelProvider, + OpenAICompatibleProvider, + OpenAIProvider, OpenRouterProvider, SiliconFlowProvider, create_model, @@ -37,10 +40,13 @@ "ModelProvider", "get_model_factory", # Provider implementations + "OpenAIProvider", + "OpenAICompatibleProvider", "OpenRouterProvider", "GoogleProvider", "AzureProvider", "SiliconFlowProvider", + "DeepSeekProvider", # Convenience functions "create_model", "create_model_for_agent", diff --git a/python/valuecell/adapters/models/factory.py b/python/valuecell/adapters/models/factory.py index 9a06dfd12..d0a30ac68 100644 --- a/python/valuecell/adapters/models/factory.py +++ b/python/valuecell/adapters/models/factory.py @@ -471,6 +471,39 @@ def is_available(self) -> bool: return bool(self.config.api_key and self.config.base_url) +class DeepSeekProvider(ModelProvider): + """DeepSeek model provider + + DeepSeek provides OpenAI-compatible API endpoints for their models. + This provider uses the OpenAI-compatible interface to interact with DeepSeek's API. + """ + + def create_model(self, model_id: Optional[str] = None, **kwargs): + """Create DeepSeek model via agno (OpenAI-compatible)""" + try: + from agno.models.openai import OpenAILike + except ImportError: + raise ImportError( + "agno package not installed. Install with: pip install agno" + ) + + model_id = model_id or self.config.default_model + params = {**self.config.parameters, **kwargs} + + logger.info(f"Creating DeepSeek model: {model_id}") + + return OpenAILike( + id=model_id, + api_key=self.config.api_key, + base_url=self.config.base_url, + temperature=params.get("temperature"), + max_tokens=params.get("max_tokens"), + top_p=params.get("top_p"), + frequency_penalty=params.get("frequency_penalty"), + presence_penalty=params.get("presence_penalty"), + ) + + class ModelFactory: """ Factory for creating model instances with provider abstraction @@ -490,6 +523,7 @@ class ModelFactory: "siliconflow": SiliconFlowProvider, "openai": OpenAIProvider, "openai-compatible": OpenAICompatibleProvider, + "deepseek": DeepSeekProvider, } def __init__(self, config_manager: Optional[ConfigManager] = None): diff --git a/python/valuecell/utils/model.py b/python/valuecell/utils/model.py index f37b5fbde..54105be36 100644 --- a/python/valuecell/utils/model.py +++ b/python/valuecell/utils/model.py @@ -16,6 +16,9 @@ from agno.models.base import Model as AgnoModel from agno.models.google import Gemini as AgnoGeminiModel from agno.models.openai import OpenAIChat as AgnoOpenAIChatModel +from agno.models.openai import OpenAILike as AgnoOpenAILikeModel +from agno.models.openrouter import OpenRouter as AgnoOpenRouterModel +from agno.models.siliconflow import Siliconflow as AgnoSiliconflowModel from valuecell.adapters.models.factory import ( create_embedder, @@ -28,19 +31,91 @@ def model_should_use_json_mode(model: AgnoModel) -> bool: + """ + Determine if a model should use JSON mode instead of structured outputs. + + JSON mode is required for: + 1. Models that support JSON mode but not OpenAI's structured outputs + 2. OpenAI-compatible APIs that don't support response_format with json_schema + + Returns True for: + - Google Gemini models + - OpenAI models (official) + - DeepSeek models (OpenAI-compatible but no structured outputs support) + - OpenRouter models (third-party proxy, safer to use JSON mode) + - SiliconFlow models (OpenAI-compatible but limited structured outputs support) + - Other OpenAI-compatible APIs (safer default) + """ try: provider = getattr(model, "provider", None) name = getattr(model, "name", None) + + # Google Gemini requires JSON mode if provider == AgnoGeminiModel.provider and name == AgnoGeminiModel.name: + logger.debug("Detected Gemini model - using JSON mode") return True + + # Official OpenAI models support both, but JSON mode is more reliable if ( provider == AgnoOpenAIChatModel.provider and name == AgnoOpenAIChatModel.name ): + logger.debug("Detected OpenAI model - using JSON mode") return True - except Exception: - # Any unexpected condition falls back to standard (non-JSON) mode - return False + + # OpenRouter models - third-party proxy supporting many models + # Use JSON mode for compatibility across different underlying models + if ( + AgnoOpenRouterModel + and provider == AgnoOpenRouterModel.provider + and name == AgnoOpenRouterModel.name + ): + logger.debug( + "Detected OpenRouter model - using JSON mode (third-party proxy)" + ) + return True + + # SiliconFlow models - OpenAI-compatible but limited structured outputs + if ( + AgnoSiliconflowModel + and provider == AgnoSiliconflowModel.provider + and name == AgnoSiliconflowModel.name + ): + logger.debug("Detected SiliconFlow model - using JSON mode") + return True + + # OpenAI-compatible models (OpenAILike) - check base_url + if ( + AgnoOpenAILikeModel + and provider == AgnoOpenAILikeModel.provider + and name == AgnoOpenAILikeModel.name + ): + base_url = getattr(model, "base_url", None) + if base_url: + base_url_str = str(base_url).lower() + + # DeepSeek doesn't support structured outputs, only JSON mode + if "deepseek.com" in base_url_str: + logger.debug( + "Detected DeepSeek API - forcing JSON mode " + "(structured outputs not supported)" + ) + return True + + # For other OpenAI-compatible APIs, use JSON mode as safer default + # Most OpenAI-compatible APIs support JSON mode but not structured outputs + logger.debug( + f"Detected OpenAI-compatible API ({base_url_str}) - using JSON mode" + ) + return True + + except Exception as e: + # Any unexpected condition falls back to JSON mode for safety + logger.debug( + f"Exception in model_should_use_json_mode: {e}, defaulting to JSON mode" + ) + return True + return False