|  |  | 
					
						
						|  |  | 
					
						
						|  | import os | 
					
						
						|  | from typing import Dict, Any, Optional | 
					
						
						|  | from dataclasses import dataclass | 
					
						
						|  | from enum import Enum | 
					
						
						|  |  | 
					
						
						|  | from ankigen_core.logging import logger | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | class AgentMode(Enum): | 
					
						
						|  | """Agent system operation modes""" | 
					
						
						|  | LEGACY = "legacy" | 
					
						
						|  | AGENT_ONLY = "agent_only" | 
					
						
						|  | HYBRID = "hybrid" | 
					
						
						|  | A_B_TEST = "a_b_test" | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | @dataclass | 
					
						
						|  | class AgentFeatureFlags: | 
					
						
						|  | """Feature flags for controlling agent system rollout""" | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | mode: AgentMode = AgentMode.LEGACY | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | enable_subject_expert_agent: bool = False | 
					
						
						|  | enable_pedagogical_agent: bool = False | 
					
						
						|  | enable_content_structuring_agent: bool = False | 
					
						
						|  | enable_generation_coordinator: bool = False | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | enable_content_accuracy_judge: bool = False | 
					
						
						|  | enable_pedagogical_judge: bool = False | 
					
						
						|  | enable_clarity_judge: bool = False | 
					
						
						|  | enable_technical_judge: bool = False | 
					
						
						|  | enable_completeness_judge: bool = False | 
					
						
						|  | enable_judge_coordinator: bool = False | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | enable_revision_agent: bool = False | 
					
						
						|  | enable_enhancement_agent: bool = False | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | enable_multi_agent_generation: bool = False | 
					
						
						|  | enable_parallel_judging: bool = False | 
					
						
						|  | enable_agent_handoffs: bool = False | 
					
						
						|  | enable_agent_tracing: bool = True | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | ab_test_ratio: float = 0.5 | 
					
						
						|  | ab_test_user_hash: Optional[str] = None | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | agent_timeout: float = 30.0 | 
					
						
						|  | max_agent_retries: int = 3 | 
					
						
						|  | enable_agent_caching: bool = True | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | min_judge_consensus: float = 0.6 | 
					
						
						|  | max_revision_iterations: int = 3 | 
					
						
						|  |  | 
					
						
						|  | @classmethod | 
					
						
						|  | def from_env(cls) -> "AgentFeatureFlags": | 
					
						
						|  | """Load feature flags from environment variables""" | 
					
						
						|  | return cls( | 
					
						
						|  | mode=AgentMode(os.getenv("ANKIGEN_AGENT_MODE", "legacy")), | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | enable_subject_expert_agent=_env_bool("ANKIGEN_ENABLE_SUBJECT_EXPERT"), | 
					
						
						|  | enable_pedagogical_agent=_env_bool("ANKIGEN_ENABLE_PEDAGOGICAL_AGENT"), | 
					
						
						|  | enable_content_structuring_agent=_env_bool("ANKIGEN_ENABLE_CONTENT_STRUCTURING"), | 
					
						
						|  | enable_generation_coordinator=_env_bool("ANKIGEN_ENABLE_GENERATION_COORDINATOR"), | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | enable_content_accuracy_judge=_env_bool("ANKIGEN_ENABLE_CONTENT_JUDGE"), | 
					
						
						|  | enable_pedagogical_judge=_env_bool("ANKIGEN_ENABLE_PEDAGOGICAL_JUDGE"), | 
					
						
						|  | enable_clarity_judge=_env_bool("ANKIGEN_ENABLE_CLARITY_JUDGE"), | 
					
						
						|  | enable_technical_judge=_env_bool("ANKIGEN_ENABLE_TECHNICAL_JUDGE"), | 
					
						
						|  | enable_completeness_judge=_env_bool("ANKIGEN_ENABLE_COMPLETENESS_JUDGE"), | 
					
						
						|  | enable_judge_coordinator=_env_bool("ANKIGEN_ENABLE_JUDGE_COORDINATOR"), | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | enable_revision_agent=_env_bool("ANKIGEN_ENABLE_REVISION_AGENT"), | 
					
						
						|  | enable_enhancement_agent=_env_bool("ANKIGEN_ENABLE_ENHANCEMENT_AGENT"), | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | enable_multi_agent_generation=_env_bool("ANKIGEN_ENABLE_MULTI_AGENT_GEN"), | 
					
						
						|  | enable_parallel_judging=_env_bool("ANKIGEN_ENABLE_PARALLEL_JUDGING"), | 
					
						
						|  | enable_agent_handoffs=_env_bool("ANKIGEN_ENABLE_AGENT_HANDOFFS"), | 
					
						
						|  | enable_agent_tracing=_env_bool("ANKIGEN_ENABLE_AGENT_TRACING", default=True), | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | ab_test_ratio=float(os.getenv("ANKIGEN_AB_TEST_RATIO", "0.5")), | 
					
						
						|  | ab_test_user_hash=os.getenv("ANKIGEN_AB_TEST_USER_HASH"), | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | agent_timeout=float(os.getenv("ANKIGEN_AGENT_TIMEOUT", "30.0")), | 
					
						
						|  | max_agent_retries=int(os.getenv("ANKIGEN_MAX_AGENT_RETRIES", "3")), | 
					
						
						|  | enable_agent_caching=_env_bool("ANKIGEN_ENABLE_AGENT_CACHING", default=True), | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | min_judge_consensus=float(os.getenv("ANKIGEN_MIN_JUDGE_CONSENSUS", "0.6")), | 
					
						
						|  | max_revision_iterations=int(os.getenv("ANKIGEN_MAX_REVISION_ITERATIONS", "3")), | 
					
						
						|  | ) | 
					
						
						|  |  | 
					
						
						|  | def should_use_agents(self) -> bool: | 
					
						
						|  | """Determine if agents should be used based on current mode""" | 
					
						
						|  | if self.mode == AgentMode.LEGACY: | 
					
						
						|  | return False | 
					
						
						|  | elif self.mode == AgentMode.AGENT_ONLY: | 
					
						
						|  | return True | 
					
						
						|  | elif self.mode == AgentMode.HYBRID: | 
					
						
						|  |  | 
					
						
						|  | return ( | 
					
						
						|  | self.enable_subject_expert_agent or | 
					
						
						|  | self.enable_pedagogical_agent or | 
					
						
						|  | self.enable_content_structuring_agent or | 
					
						
						|  | any([ | 
					
						
						|  | self.enable_content_accuracy_judge, | 
					
						
						|  | self.enable_pedagogical_judge, | 
					
						
						|  | self.enable_clarity_judge, | 
					
						
						|  | self.enable_technical_judge, | 
					
						
						|  | self.enable_completeness_judge, | 
					
						
						|  | ]) | 
					
						
						|  | ) | 
					
						
						|  | elif self.mode == AgentMode.A_B_TEST: | 
					
						
						|  |  | 
					
						
						|  | if self.ab_test_user_hash: | 
					
						
						|  |  | 
					
						
						|  | import hashlib | 
					
						
						|  | hash_value = int(hashlib.md5(self.ab_test_user_hash.encode()).hexdigest(), 16) | 
					
						
						|  | return (hash_value % 100) < (self.ab_test_ratio * 100) | 
					
						
						|  | else: | 
					
						
						|  |  | 
					
						
						|  | import random | 
					
						
						|  | return random.random() < self.ab_test_ratio | 
					
						
						|  |  | 
					
						
						|  | return False | 
					
						
						|  |  | 
					
						
						|  | def get_enabled_agents(self) -> Dict[str, bool]: | 
					
						
						|  | """Get a dictionary of all enabled agents""" | 
					
						
						|  | return { | 
					
						
						|  | "subject_expert": self.enable_subject_expert_agent, | 
					
						
						|  | "pedagogical": self.enable_pedagogical_agent, | 
					
						
						|  | "content_structuring": self.enable_content_structuring_agent, | 
					
						
						|  | "generation_coordinator": self.enable_generation_coordinator, | 
					
						
						|  | "content_accuracy_judge": self.enable_content_accuracy_judge, | 
					
						
						|  | "pedagogical_judge": self.enable_pedagogical_judge, | 
					
						
						|  | "clarity_judge": self.enable_clarity_judge, | 
					
						
						|  | "technical_judge": self.enable_technical_judge, | 
					
						
						|  | "completeness_judge": self.enable_completeness_judge, | 
					
						
						|  | "judge_coordinator": self.enable_judge_coordinator, | 
					
						
						|  | "revision_agent": self.enable_revision_agent, | 
					
						
						|  | "enhancement_agent": self.enable_enhancement_agent, | 
					
						
						|  | } | 
					
						
						|  |  | 
					
						
						|  | def to_dict(self) -> Dict[str, Any]: | 
					
						
						|  | """Convert to dictionary for logging/debugging""" | 
					
						
						|  | return { | 
					
						
						|  | "mode": self.mode.value, | 
					
						
						|  | "enabled_agents": self.get_enabled_agents(), | 
					
						
						|  | "workflow_features": { | 
					
						
						|  | "multi_agent_generation": self.enable_multi_agent_generation, | 
					
						
						|  | "parallel_judging": self.enable_parallel_judging, | 
					
						
						|  | "agent_handoffs": self.enable_agent_handoffs, | 
					
						
						|  | "agent_tracing": self.enable_agent_tracing, | 
					
						
						|  | }, | 
					
						
						|  | "ab_test_ratio": self.ab_test_ratio, | 
					
						
						|  | "performance_config": { | 
					
						
						|  | "timeout": self.agent_timeout, | 
					
						
						|  | "max_retries": self.max_agent_retries, | 
					
						
						|  | "caching": self.enable_agent_caching, | 
					
						
						|  | }, | 
					
						
						|  | "quality_thresholds": { | 
					
						
						|  | "min_judge_consensus": self.min_judge_consensus, | 
					
						
						|  | "max_revision_iterations": self.max_revision_iterations, | 
					
						
						|  | } | 
					
						
						|  | } | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | def _env_bool(env_var: str, default: bool = False) -> bool: | 
					
						
						|  | """Helper to parse boolean environment variables""" | 
					
						
						|  | value = os.getenv(env_var, str(default)).lower() | 
					
						
						|  | return value in ("true", "1", "yes", "on", "enabled") | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | _global_flags: Optional[AgentFeatureFlags] = None | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | def get_feature_flags() -> AgentFeatureFlags: | 
					
						
						|  | """Get the global feature flags instance""" | 
					
						
						|  | global _global_flags | 
					
						
						|  | if _global_flags is None: | 
					
						
						|  | _global_flags = AgentFeatureFlags.from_env() | 
					
						
						|  | logger.info(f"Loaded agent feature flags: {_global_flags.mode.value}") | 
					
						
						|  | logger.debug(f"Feature flags config: {_global_flags.to_dict()}") | 
					
						
						|  | return _global_flags | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | def set_feature_flags(flags: AgentFeatureFlags): | 
					
						
						|  | """Set global feature flags (for testing or runtime reconfiguration)""" | 
					
						
						|  | global _global_flags | 
					
						
						|  | _global_flags = flags | 
					
						
						|  | logger.info(f"Updated agent feature flags: {flags.mode.value}") | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | def reset_feature_flags(): | 
					
						
						|  | """Reset feature flags (reload from environment)""" | 
					
						
						|  | global _global_flags | 
					
						
						|  | _global_flags = None |