Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion python/configs/agents/news_agent.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 8 additions & 1 deletion python/configs/providers/deepseek.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
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"
6 changes: 4 additions & 2 deletions python/configs/providers/openrouter.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions python/valuecell/adapters/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@

from valuecell.adapters.models.factory import (
AzureProvider,
DeepSeekProvider,
GoogleProvider,
ModelFactory,
ModelProvider,
OpenAICompatibleProvider,
OpenAIProvider,
OpenRouterProvider,
SiliconFlowProvider,
create_model,
Expand All @@ -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",
Expand Down
34 changes: 34 additions & 0 deletions python/valuecell/adapters/models/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -490,6 +523,7 @@ class ModelFactory:
"siliconflow": SiliconFlowProvider,
"openai": OpenAIProvider,
"openai-compatible": OpenAICompatibleProvider,
"deepseek": DeepSeekProvider,
}

def __init__(self, config_manager: Optional[ConfigManager] = None):
Expand Down
81 changes: 78 additions & 3 deletions python/valuecell/utils/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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


Expand Down