From 5bca7fb79df1a8b5e7ab41bcb901cb58ee8e6517 Mon Sep 17 00:00:00 2001 From: Apple Date: Wed, 18 Feb 2026 10:20:10 -0800 Subject: [PATCH] router: unify top-level DeepSeek-first + on-demand CrewAI policy --- services/router/crewai_client.py | 34 +++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/services/router/crewai_client.py b/services/router/crewai_client.py index 88ecc124..a6f0f50e 100644 --- a/services/router/crewai_client.py +++ b/services/router/crewai_client.py @@ -14,6 +14,10 @@ logger = logging.getLogger(__name__) CREWAI_URL = os.getenv("CREWAI_URL", "http://dagi-staging-crewai-service:9010") CREWAI_ENABLED = os.getenv("CREWAI_ENABLED", "true").lower() == "true" CREWAI_ORCHESTRATORS_ALWAYS = os.getenv("CREWAI_ORCHESTRATORS_ALWAYS", "true").lower() == "true" +TOP_LEVEL_CREWAI_ON_DEMAND = os.getenv("TOP_LEVEL_CREWAI_ON_DEMAND", "true").lower() == "true" +TOP_LEVEL_CREWAI_TEAM_LIMIT_FAST = int(os.getenv("TOP_LEVEL_CREWAI_TEAM_LIMIT_FAST", "3")) +TOP_LEVEL_CREWAI_PROFILE_FAST = os.getenv("TOP_LEVEL_CREWAI_PROFILE_FAST", "") +TOP_LEVEL_CREWAI_PROFILE_COMPLEX = os.getenv("TOP_LEVEL_CREWAI_PROFILE_COMPLEX", "") HELION_CREWAI_TEAM_LIMIT = int(os.getenv("HELION_CREWAI_TEAM_LIMIT", "3")) CLAN_CREWAI_PROFILE = os.getenv("CLAN_CREWAI_PROFILE", "zhos_mvp") CLAN_CREWAI_PROFILE_FAST = os.getenv("CLAN_CREWAI_PROFILE_FAST", "default") @@ -99,12 +103,17 @@ def should_use_crewai(agent_id, prompt, agent_config, metadata=None, force_crewa force_detailed = bool(metadata.get("force_detailed")) requires_complex = bool(metadata.get("requires_complex_reasoning")) - # Helion policy: DeepSeek direct path by default; CrewAI only on-demand. - # This keeps first-touch replies fast and concise. - if agent_id == "helion": + # Top-level policy: DeepSeek direct path by default; CrewAI only on-demand. + # This keeps first-touch replies fast and concise across all top-level agents. + if crewai_info.get("orchestrator", False) and TOP_LEVEL_CREWAI_ON_DEMAND: if force_detailed or requires_complex: - return True, "helion_complex_or_detailed" - return False, "helion_direct_deepseek_first" + return True, "orchestrator_complex_or_detailed" + if len(prompt) >= MIN_PROMPT_LENGTH_FOR_CREW: + prompt_lower = prompt.lower() + has_complexity = any(kw in prompt_lower for kw in COMPLEXITY_KEYWORDS) + if has_complexity: + return True, "orchestrator_complexity_keywords_detected" + return False, "orchestrator_direct_llm_first" # Architecture mode: top-level orchestrators go through CrewAI API by default. if CREWAI_ORCHESTRATORS_ALWAYS: @@ -132,9 +141,19 @@ async def call_crewai(agent_id, task, context=None, team=None, profile=None): metadata = (effective_context.get("metadata", {}) or {}) force_detailed = bool(metadata.get("force_detailed")) requires_complex = bool(metadata.get("requires_complex_reasoning")) + is_orchestrator = bool(get_agent_crewai_info(agent_id).get("orchestrator", False)) # Helion policy: limit CrewAI participants unless user requested detailed mode. if agent_id == "helion" and not force_detailed and HELION_CREWAI_TEAM_LIMIT > 0 and len(team) > HELION_CREWAI_TEAM_LIMIT: team = team[:HELION_CREWAI_TEAM_LIMIT] + if ( + is_orchestrator + and TOP_LEVEL_CREWAI_ON_DEMAND + and not force_detailed + and not requires_complex + and TOP_LEVEL_CREWAI_TEAM_LIMIT_FAST > 0 + and len(team) > TOP_LEVEL_CREWAI_TEAM_LIMIT_FAST + ): + team = team[:TOP_LEVEL_CREWAI_TEAM_LIMIT_FAST] async with httpx.AsyncClient(timeout=600.0) as client: effective_profile = profile or (effective_context.get("metadata", {}) or {}).get("crewai_profile") @@ -144,6 +163,11 @@ async def call_crewai(agent_id, task, context=None, team=None, profile=None): if (force_detailed or requires_complex) else CLAN_CREWAI_PROFILE_FAST ) + if not effective_profile and is_orchestrator and TOP_LEVEL_CREWAI_ON_DEMAND: + if force_detailed or requires_complex: + effective_profile = TOP_LEVEL_CREWAI_PROFILE_COMPLEX or None + else: + effective_profile = TOP_LEVEL_CREWAI_PROFILE_FAST or None if ( agent_id == "clan" and effective_profile == CLAN_CREWAI_PROFILE_FAST