From e7954e2e4bc7891aef4fd1e978e49782c3a85502 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Wed, 29 Oct 2025 11:41:58 +0800 Subject: [PATCH 1/9] fix: replace logging with loguru for improved logging functionality in SuperAgent --- python/valuecell/core/super_agent/core.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/python/valuecell/core/super_agent/core.py b/python/valuecell/core/super_agent/core.py index f5ef5aab0..8b87772a5 100644 --- a/python/valuecell/core/super_agent/core.py +++ b/python/valuecell/core/super_agent/core.py @@ -1,10 +1,10 @@ import asyncio -import logging from enum import Enum from typing import Optional from agno.agent import Agent from agno.db.in_memory import InMemoryDb +from loguru import logger from pydantic import BaseModel, Field from valuecell.core.super_agent.prompts import ( @@ -15,8 +15,6 @@ from valuecell.utils.env import agent_debug_mode_enabled from valuecell.utils.model import get_model, get_model_for_agent -logger = logging.getLogger(__name__) - class SuperAgentDecision(str, Enum): ANSWER = "answer" @@ -52,7 +50,7 @@ def __init__(self) -> None: model = get_model_for_agent("super_agent") except Exception: # Fallback to old behavior for backward compatibility - logger.warning( + logger.exception( "Failed to create model for super_agent, falling back to PLANNER_MODEL_ID" ) model = get_model("PLANNER_MODEL_ID") From 299e0fc4b33e550e6d956ed45356f0bf877ba5a0 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Wed, 29 Oct 2025 11:42:05 +0800 Subject: [PATCH 2/9] fix: remove unnecessary whitespace in super_agent.yaml --- python/configs/agents/super_agent.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/python/configs/agents/super_agent.yaml b/python/configs/agents/super_agent.yaml index 240bbe759..c151d7970 100644 --- a/python/configs/agents/super_agent.yaml +++ b/python/configs/agents/super_agent.yaml @@ -23,10 +23,6 @@ models: model_id: "anthropic/claude-haiku-4.5" provider: "openrouter" # Can be: openrouter, siliconflow, or null (uses system primary_provider) - - - - # Environment Variable Overrides # Format: ENV_VAR_NAME -> config.path # These allow runtime configuration through environment variables From ff13c383a70c449b570b1a2b53364fb1046c1ae6 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Wed, 29 Oct 2025 11:42:16 +0800 Subject: [PATCH 3/9] fix: handle missing models configuration in ConfigManager --- python/valuecell/config/manager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/valuecell/config/manager.py b/python/valuecell/config/manager.py index d659a4576..6173fabc9 100644 --- a/python/valuecell/config/manager.py +++ b/python/valuecell/config/manager.py @@ -274,7 +274,8 @@ def get_agent_config(self, agent_name: str) -> Optional[AgentConfig]: parameters = primary.get("parameters", {}) # Merge with global defaults - global_defaults = self._config.get("models", {}).get("defaults", {}) + models = self._config.get("models") or {} + global_defaults = models.get("defaults") or {} merged_params = {**global_defaults, **parameters} primary_model = AgentModelConfig( From 9510977ddec16cdb662180deb6d94170dec5d341 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Wed, 29 Oct 2025 11:59:57 +0800 Subject: [PATCH 4/9] fix: remove unused use_json_mode configuration from super_agent.yaml --- python/configs/agents/super_agent.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/python/configs/agents/super_agent.yaml b/python/configs/agents/super_agent.yaml index c151d7970..67fe76944 100644 --- a/python/configs/agents/super_agent.yaml +++ b/python/configs/agents/super_agent.yaml @@ -52,9 +52,8 @@ capabilities: advanced: # Debug mode (can be overridden by AGENT_DEBUG_MODE env var) debug_mode: false - + # Output format - use_json_mode: true markdown: false # Context settings From 6e6a2b3c5f2770ddf7cc44e3e7cb3cec5c73b7dd Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Wed, 29 Oct 2025 12:00:28 +0800 Subject: [PATCH 5/9] fix: refactor model handling in ExecutionPlanner and SuperAgent to use model_should_use_json_mode --- python/valuecell/core/plan/planner.py | 6 ++++-- python/valuecell/core/super_agent/core.py | 14 +++----------- python/valuecell/utils/model.py | 12 ++++++++++++ 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/python/valuecell/core/plan/planner.py b/python/valuecell/core/plan/planner.py index 64d65e00d..ad1803450 100644 --- a/python/valuecell/core/plan/planner.py +++ b/python/valuecell/core/plan/planner.py @@ -24,7 +24,7 @@ from valuecell.core.types import UserInput from valuecell.utils import generate_uuid from valuecell.utils.env import agent_debug_mode_enabled -from valuecell.utils.model import get_model +from valuecell.utils.model import get_model_for_agent, model_should_use_json_mode from valuecell.utils.uuid import generate_conversation_id from .models import ExecutionPlan, PlannerInput, PlannerResponse @@ -88,8 +88,9 @@ def __init__( agent_connections: RemoteConnections, ): self.agent_connections = agent_connections + model = get_model_for_agent("super_agent") self.planner_agent = Agent( - model=get_model("PLANNER_MODEL_ID"), + model=model, tools=[ # TODO: enable UserControlFlowTools when stable # UserControlFlowTools(), @@ -101,6 +102,7 @@ def __init__( markdown=False, output_schema=PlannerResponse, expected_output=PLANNER_EXPECTED_OUTPUT, + use_json_mode=model_should_use_json_mode(model), # context db=InMemoryDb(), add_datetime_to_context=True, diff --git a/python/valuecell/core/super_agent/core.py b/python/valuecell/core/super_agent/core.py index 8b87772a5..ba9550f2c 100644 --- a/python/valuecell/core/super_agent/core.py +++ b/python/valuecell/core/super_agent/core.py @@ -4,7 +4,6 @@ from agno.agent import Agent from agno.db.in_memory import InMemoryDb -from loguru import logger from pydantic import BaseModel, Field from valuecell.core.super_agent.prompts import ( @@ -13,7 +12,7 @@ ) from valuecell.core.types import UserInput from valuecell.utils.env import agent_debug_mode_enabled -from valuecell.utils.model import get_model, get_model_for_agent +from valuecell.utils.model import get_model_for_agent, model_should_use_json_mode class SuperAgentDecision(str, Enum): @@ -46,15 +45,7 @@ class SuperAgent: def __init__(self) -> None: # Try to use super_agent specific configuration first, # fallback to PLANNER_MODEL_ID for backward compatibility - try: - model = get_model_for_agent("super_agent") - except Exception: - # Fallback to old behavior for backward compatibility - logger.exception( - "Failed to create model for super_agent, falling back to PLANNER_MODEL_ID" - ) - model = get_model("PLANNER_MODEL_ID") - + model = get_model_for_agent("super_agent") self.agent = Agent( model=model, # TODO: enable tools when needed @@ -65,6 +56,7 @@ def __init__(self) -> None: # output format expected_output=SUPER_AGENT_EXPECTED_OUTPUT, output_schema=SuperAgentOutcome, + use_json_mode=model_should_use_json_mode(model), # context db=InMemoryDb(), add_datetime_to_context=True, diff --git a/python/valuecell/utils/model.py b/python/valuecell/utils/model.py index 5eb8f45f3..496340d0e 100644 --- a/python/valuecell/utils/model.py +++ b/python/valuecell/utils/model.py @@ -13,6 +13,9 @@ import os from typing import Optional +from agno.models.base import Model as AgnoModel +from agno.models.google import Gemini as AgnoGeminiModel + from valuecell.adapters.models.factory import ( create_embedder, create_model, @@ -22,6 +25,15 @@ logger = logging.getLogger(__name__) +def model_should_use_json_mode(model: AgnoModel) -> bool: + if ( + model.provider == AgnoGeminiModel.provider + and model.name == AgnoGeminiModel.name + ): + return True + return False + + def get_model(env_key: str, **kwargs): """ Get model instance using configuration system with environment variable override. From 3d60b476687f154760394734e149094c5905767e Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Wed, 29 Oct 2025 12:05:10 +0800 Subject: [PATCH 6/9] fix tests --- .../valuecell/core/coordinate/tests/test_e2e_persistence.py | 2 +- python/valuecell/core/plan/tests/test_planner.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/python/valuecell/core/coordinate/tests/test_e2e_persistence.py b/python/valuecell/core/coordinate/tests/test_e2e_persistence.py index 5866b7e39..231a93056 100644 --- a/python/valuecell/core/coordinate/tests/test_e2e_persistence.py +++ b/python/valuecell/core/coordinate/tests/test_e2e_persistence.py @@ -20,7 +20,7 @@ async def test_orchestrator_buffer_store_e2e(tmp_path, monkeypatch): factory_mod, "create_embedder", lambda *args, **kwargs: "stub-embedder" ) monkeypatch.setattr( - model_utils_mod, "create_model", lambda *args, **kwargs: "stub-model" + model_utils_mod, "get_model_for_agent", lambda *args, **kwargs: "stub-model" ) monkeypatch.setattr( model_utils_mod, "create_embedder", lambda *args, **kwargs: "stub-embedder" diff --git a/python/valuecell/core/plan/tests/test_planner.py b/python/valuecell/core/plan/tests/test_planner.py index 98f53d7cb..d65c9131d 100644 --- a/python/valuecell/core/plan/tests/test_planner.py +++ b/python/valuecell/core/plan/tests/test_planner.py @@ -69,7 +69,7 @@ def continue_run(self, *args, **kwargs): monkeypatch.setattr(planner_mod, "Agent", FakeAgent) monkeypatch.setattr( - model_utils_mod, "create_model", lambda *args, **kwargs: "stub-model" + model_utils_mod, "get_model_for_agent", lambda *args, **kwargs: "stub-model" ) monkeypatch.setattr(planner_mod, "agent_debug_mode_enabled", lambda: False) @@ -120,7 +120,7 @@ def run(self, *args, **kwargs): monkeypatch.setattr(planner_mod, "Agent", FakeAgent) monkeypatch.setattr( - model_utils_mod, "create_model", lambda *args, **kwargs: "stub-model" + model_utils_mod, "get_model_for_agent", lambda *args, **kwargs: "stub-model" ) monkeypatch.setattr(planner_mod, "agent_debug_mode_enabled", lambda: False) @@ -142,7 +142,7 @@ async def callback(request): def test_tool_get_enabled_agents_formats_cards(monkeypatch: pytest.MonkeyPatch): # Mock create_model to avoid API key validation in CI monkeypatch.setattr( - model_utils_mod, "create_model", lambda *args, **kwargs: "stub-model" + model_utils_mod, "get_model_for_agent", lambda *args, **kwargs: "stub-model" ) monkeypatch.setattr(planner_mod, "agent_debug_mode_enabled", lambda: False) From 5c01c7cdc507828e7c096bf82273997fc938aa0c Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Wed, 29 Oct 2025 12:09:42 +0800 Subject: [PATCH 7/9] fix: refactor model retrieval in ExecutionPlanner and SuperAgent for test compatibility --- python/valuecell/core/plan/planner.py | 7 ++++--- python/valuecell/core/super_agent/core.py | 17 ++++++++++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/python/valuecell/core/plan/planner.py b/python/valuecell/core/plan/planner.py index ad1803450..26fa26b63 100644 --- a/python/valuecell/core/plan/planner.py +++ b/python/valuecell/core/plan/planner.py @@ -19,12 +19,12 @@ from agno.agent import Agent from agno.db.in_memory import InMemoryDb +import valuecell.utils.model as model_utils_mod from valuecell.core.agent.connect import RemoteConnections from valuecell.core.task.models import Task, TaskStatus from valuecell.core.types import UserInput from valuecell.utils import generate_uuid from valuecell.utils.env import agent_debug_mode_enabled -from valuecell.utils.model import get_model_for_agent, model_should_use_json_mode from valuecell.utils.uuid import generate_conversation_id from .models import ExecutionPlan, PlannerInput, PlannerResponse @@ -88,7 +88,8 @@ def __init__( agent_connections: RemoteConnections, ): self.agent_connections = agent_connections - model = get_model_for_agent("super_agent") + # Fetch model via utils module reference so tests can monkeypatch it reliably + model = model_utils_mod.get_model_for_agent("super_agent") self.planner_agent = Agent( model=model, tools=[ @@ -102,7 +103,7 @@ def __init__( markdown=False, output_schema=PlannerResponse, expected_output=PLANNER_EXPECTED_OUTPUT, - use_json_mode=model_should_use_json_mode(model), + use_json_mode=model_utils_mod.model_should_use_json_mode(model), # context db=InMemoryDb(), add_datetime_to_context=True, diff --git a/python/valuecell/core/super_agent/core.py b/python/valuecell/core/super_agent/core.py index ba9550f2c..09a34d23e 100644 --- a/python/valuecell/core/super_agent/core.py +++ b/python/valuecell/core/super_agent/core.py @@ -6,13 +6,23 @@ from agno.db.in_memory import InMemoryDb from pydantic import BaseModel, Field +import valuecell.utils.model as model_utils_mod from valuecell.core.super_agent.prompts import ( SUPER_AGENT_EXPECTED_OUTPUT, SUPER_AGENT_INSTRUCTION, ) from valuecell.core.types import UserInput from valuecell.utils.env import agent_debug_mode_enabled -from valuecell.utils.model import get_model_for_agent, model_should_use_json_mode + + +# Backward-compatible helper so tests can monkeypatch `get_model` on this module +def get_model(agent_name: str): + """Return a model for the given agent via the centralized utils module. + + Exposed at module-level to support test monkeypatching without touching + global provider configuration. + """ + return model_utils_mod.get_model_for_agent(agent_name) class SuperAgentDecision(str, Enum): @@ -45,7 +55,8 @@ class SuperAgent: def __init__(self) -> None: # Try to use super_agent specific configuration first, # fallback to PLANNER_MODEL_ID for backward compatibility - model = get_model_for_agent("super_agent") + # Use module-level get_model indirection so tests can stub it easily + model = get_model("super_agent") self.agent = Agent( model=model, # TODO: enable tools when needed @@ -56,7 +67,7 @@ def __init__(self) -> None: # output format expected_output=SUPER_AGENT_EXPECTED_OUTPUT, output_schema=SuperAgentOutcome, - use_json_mode=model_should_use_json_mode(model), + use_json_mode=model_utils_mod.model_should_use_json_mode(model), # context db=InMemoryDb(), add_datetime_to_context=True, From 9f7c4065bc2364c8f3b87cf79841294357118b8b Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Wed, 29 Oct 2025 12:50:19 +0800 Subject: [PATCH 8/9] fix: enhance model_should_use_json_mode to handle attribute access safely --- python/valuecell/utils/model.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/python/valuecell/utils/model.py b/python/valuecell/utils/model.py index 496340d0e..7e17867e1 100644 --- a/python/valuecell/utils/model.py +++ b/python/valuecell/utils/model.py @@ -26,11 +26,14 @@ def model_should_use_json_mode(model: AgnoModel) -> bool: - if ( - model.provider == AgnoGeminiModel.provider - and model.name == AgnoGeminiModel.name - ): - return True + try: + provider = getattr(model, "provider", None) + name = getattr(model, "name", None) + if provider == AgnoGeminiModel.provider and name == AgnoGeminiModel.name: + return True + except Exception: + # Any unexpected condition falls back to standard (non-JSON) mode + return False return False From 109e3a5850c450d213d82e7caee99b0dd7ada9c2 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Wed, 29 Oct 2025 14:08:18 +0800 Subject: [PATCH 9/9] feat: add agent-specific embedder creation and update embedding model configuration --- python/configs/agents/research_agent.yaml | 7 ++- python/valuecell/adapters/models/factory.py | 62 +++++++++++++++++++ python/valuecell/agents/research_agent/vdb.py | 4 +- python/valuecell/config/manager.py | 6 +- python/valuecell/utils/model.py | 27 ++++++++ 5 files changed, 98 insertions(+), 8 deletions(-) diff --git a/python/configs/agents/research_agent.yaml b/python/configs/agents/research_agent.yaml index a22a298a5..a4f69de80 100644 --- a/python/configs/agents/research_agent.yaml +++ b/python/configs/agents/research_agent.yaml @@ -18,10 +18,10 @@ models: # Embedding model for knowledge base # Note: If not specified, will auto-select from available providers embedding: - # model_id: "openai/text-embedding-3-small" # Optional, uses provider default - # provider: "openrouter" # Optional, auto-detects if not specified + model_id: "Qwen/Qwen3-Embedding-4B" # Optional, uses provider default + provider: "siliconflow" # Optional, auto-detects if not specified parameters: - dimensions: ${EMBEDDER_DIMENSION:1536} + dimensions: ${EMBEDDER_DIMENSION:2560} # encoding_format: "float" # Environment Variable Overrides @@ -35,5 +35,6 @@ env_overrides: # Embedding model overrides (backward compatible with existing env vars) EMBEDDER_MODEL_ID: "models.embedding.model_id" + EMBEDDER_MODEL_PROVIDER: "models.embedding.provider" EMBEDDER_DIMENSION: "models.embedding.parameters.dimensions" diff --git a/python/valuecell/adapters/models/factory.py b/python/valuecell/adapters/models/factory.py index f4d9aca41..edfb7441f 100644 --- a/python/valuecell/adapters/models/factory.py +++ b/python/valuecell/adapters/models/factory.py @@ -443,6 +443,53 @@ def create_model_for_agent( **merged_params, ) + def create_embedder_for_agent( + self, agent_name: str, use_fallback: bool = True, **kwargs + ): + """ + Create an embedder for a specific agent using its configuration. + + This method will use the agent's `embedding_model` configuration when + present. If no embedding config is provided for the agent it will + attempt to use the agent's primary model provider and select the + provider's default embedding model. + """ + # Get agent configuration + agent_config = self.config_manager.get_agent_config(agent_name) + + if not agent_config: + raise ValueError(f"Agent configuration not found: {agent_name}") + + if not agent_config.enabled: + raise ValueError(f"Agent is disabled: {agent_name}") + + # If agent specifies an embedding_model, use it + if agent_config.embedding_model: + emb = agent_config.embedding_model + merged_params = {**emb.parameters, **kwargs} + logger.info( + f"Creating embedder for agent '{agent_name}': model_id={emb.model_id}, provider={emb.provider}, params={merged_params}" + ) + return self.create_embedder( + model_id=emb.model_id or None, + provider=emb.provider, + use_fallback=use_fallback, + **merged_params, + ) + + # Fallback: use primary model's provider and let factory pick provider default + primary = agent_config.primary_model + merged_params = {**primary.parameters, **kwargs} + logger.info( + f"Creating embedder for agent '{agent_name}' using primary provider: {primary.provider}" + ) + return self.create_embedder( + model_id=None, + provider=primary.provider, + use_fallback=use_fallback, + **merged_params, + ) + def get_available_providers(self) -> list[str]: """ Get list of available providers (with valid credentials) @@ -752,3 +799,18 @@ def create_embedder( """ factory = get_model_factory() return factory.create_embedder(model_id, provider, **kwargs) + + +def create_embedder_for_agent(agent_name: str, **kwargs): + """ + Convenience function to create an embedder configured for a specific agent. + + Args: + agent_name: Agent name + **kwargs: Override parameters + + Returns: + Embedder instance + """ + factory = get_model_factory() + return factory.create_embedder_for_agent(agent_name, **kwargs) diff --git a/python/valuecell/agents/research_agent/vdb.py b/python/valuecell/agents/research_agent/vdb.py index d65e3b631..4aa04f4ac 100644 --- a/python/valuecell/agents/research_agent/vdb.py +++ b/python/valuecell/agents/research_agent/vdb.py @@ -17,8 +17,8 @@ from agno.vectordb.lancedb import LanceDb from agno.vectordb.search import SearchType +import valuecell.utils.model as model_utils_mod from valuecell.utils.db import resolve_lancedb_uri -from valuecell.utils.model import get_embedder # Create embedder using the configuration system # This will: @@ -26,7 +26,7 @@ # - Auto-select provider with embedding support (e.g., SiliconFlow if SILICONFLOW_API_KEY is set) # - Use provider's default embedding model if not specified # - Fall back to other providers if primary fails -embedder = get_embedder("EMBEDDER_MODEL_ID") +embedder = model_utils_mod.get_embedder_for_agent("research_agent") # Alternative usage examples: # embedder = get_embedder() # Use default env key diff --git a/python/valuecell/config/manager.py b/python/valuecell/config/manager.py index 6173fabc9..d365f2328 100644 --- a/python/valuecell/config/manager.py +++ b/python/valuecell/config/manager.py @@ -271,11 +271,11 @@ def get_agent_config(self, agent_name: str) -> Optional[AgentConfig]: model_id = provider_config.default_model # Get parameters - parameters = primary.get("parameters", {}) + parameters = primary.get("parameters") or {} # Merge with global defaults - models = self._config.get("models") or {} - global_defaults = models.get("defaults") or {} + global_models = self._config.get("models") or {} + global_defaults = global_models.get("defaults") or {} merged_params = {**global_defaults, **parameters} primary_model = AgentModelConfig( diff --git a/python/valuecell/utils/model.py b/python/valuecell/utils/model.py index 7e17867e1..3bd4fa7ad 100644 --- a/python/valuecell/utils/model.py +++ b/python/valuecell/utils/model.py @@ -18,6 +18,7 @@ from valuecell.adapters.models.factory import ( create_embedder, + create_embedder_for_agent, create_model, create_model_for_agent, ) @@ -245,6 +246,32 @@ def get_embedder(env_key: str = "EMBEDDER_MODEL_ID", **kwargs): raise +def get_embedder_for_agent(agent_name: str, **kwargs): + """ + Get an embedder instance configured specifically for an agent. + + This mirrors `get_model_for_agent` but for embedders. It delegates to + the adapters/models factory which will resolve the agent's embedding + configuration and provider using the three-tier configuration system. + + Args: + agent_name: Agent name matching the config file + **kwargs: Override parameters for this specific call + + Returns: + Embedder instance configured for the agent + + Raises: + ValueError: If agent configuration not found or embedder creation fails + """ + + try: + return create_embedder_for_agent(agent_name, **kwargs) + except Exception as e: + logger.error(f"Failed to create embedder for agent '{agent_name}': {e}") + raise + + def create_embedder_with_provider( provider: str, model_id: Optional[str] = None, **kwargs ):