diff --git a/BRANCH_FEATURE.md b/BRANCH_FEATURE.md new file mode 100644 index 0000000..78b4bec --- /dev/null +++ b/BRANCH_FEATURE.md @@ -0,0 +1 @@ +TB-MINIMAL: Focus = minimal runtime, light deps, CI smoke tests. diff --git a/FEATURES_SUMMARY.md b/FEATURES_SUMMARY.md index 92b5cac..53551dd 100644 --- a/FEATURES_SUMMARY.md +++ b/FEATURES_SUMMARY.md @@ -76,3 +76,8 @@ This document is an inventory of the major features and modules present in the T *This inventory is intentionally high-level. The next step (SIMPLIFICATIONS.md) will analyze pros/cons and propose simplifications.* + +Additions in TB-Seed work: +- `src/vision/schema.py` — small validator for detection outputs (`bbox`,`label`,`confidence/score`). +- `scripts/run_minimal.py` and `src/training/game_runner.py` updated to use the minimal runner & opt-in stub API for CI. + diff --git a/README_MINIMAL.md b/README_MINIMAL.md new file mode 100644 index 0000000..f125819 --- /dev/null +++ b/README_MINIMAL.md @@ -0,0 +1,22 @@ +Running TB-Seed minimal mode + +This README explains the minimal mode runner used for quick experiments and CI smoke tests. + +Usage (developer): + +- Run with real API manager (if configured): + +```powershell +python scripts\run_minimal.py --game-id mygame --max-actions 50 +``` + +- Run in CI/test mode using the stub ARC3 API (explicit opt-in flag required): + +```powershell +python scripts\run_minimal.py --use-stub-api-for-ci --game-id test_game --max-actions 2 +``` + +Notes: +- Per the seed contract, production/research runs must use a real ARC3 API and DB-backed persistence. +- The stub API is allowed only for CI/test runs and must be explicitly opted-in with `--use-stub-api-for-ci` or the environment variable `USE_STUB_API_FOR_CI=1`. +- Tests live in `/tests`. The CI workflow runs those tests and a minimal smoke invocation. diff --git a/scripts/run_minimal.py b/scripts/run_minimal.py index 02f91f6..8136b68 100644 --- a/scripts/run_minimal.py +++ b/scripts/run_minimal.py @@ -1,8 +1,24 @@ """Simple CLI to run TB-Seed minimal mode using GameRunner.""" import asyncio import argparse +import sys +import os +from pathlib import Path + +# Add project root to Python path +project_root = Path(__file__).parent.parent +sys.path.insert(0, str(project_root)) + +# Load environment variables from .env file (like train.py does) +try: + from dotenv import load_dotenv + load_dotenv() + print("[OK] Environment variables loaded from .env file") +except ImportError: + print("[INFO] python-dotenv not installed. Using system environment variables.") from src.training.game_runner import GameRunner +from src.vision.schema import validate_detections # Try to import APIManager from existing codebase try: @@ -15,27 +31,74 @@ except Exception: StubAPIManager = None +async def get_random_game_id(api_manager): + """Get a random game ID from available ARC 3 games.""" + try: + if hasattr(api_manager, 'get_available_games'): + print("[AUTO] Fetching available games from ARC 3...") + games = await api_manager.get_available_games() + if games and len(games) > 0: + import random + selected_game = random.choice(games) + game_id = selected_game.get('id') or selected_game.get('game_id', 'unknown') + print(f"[AUTO] Selected random game: {game_id} (from {len(games)} available)") + return game_id + else: + print("[AUTO] No games available from API, using fallback") + return "fallback_game_auto" + else: + print("[AUTO] API doesn't support get_available_games, using fallback") + return "fallback_game_auto" + except Exception as e: + print(f"[AUTO] Error fetching games: {e}, using fallback") + return "fallback_game_auto" + async def main(args): + """Main function - defaults to REAL ARC 3 API unless stub explicitly requested.""" api = None + + # Check if we should use stub API (explicit opt-in only) if args.use_stub_api: + print("[STUB] Using stub API for CI/testing") if not StubAPIManager: raise RuntimeError("StubAPIManager not available") api = StubAPIManager() await api.initialize() else: - if APIManager: - api = APIManager(api_key=None) - if hasattr(api, 'initialize'): - await api.initialize() + # Default to REAL ARC 3 API (like train.py does) + print("[REAL] Using real ARC 3 API") + if not APIManager: + raise RuntimeError("APIManager not available - check imports") + + # Get API key from environment variable + api_key = os.getenv('ARC_API_KEY') or os.getenv('ARC_AGI_3_API_KEY') + if not api_key: + print("ERROR: No ARC_API_KEY found in environment variables") + print("Options:") + print("1. Check your .env file has: ARC_API_KEY=your_key_here") + print("2. Set environment variable: set ARC_API_KEY=your_key_here") + print("3. Use stub API for testing: --use-stub-api-for-ci") + raise RuntimeError("ARC_API_KEY environment variable required for real API calls") + + print(f"[OK] Found ARC_API_KEY: {api_key[:8]}...") + api = APIManager(api_key=api_key) + if hasattr(api, 'initialize'): + await api.initialize() + print("[OK] Real ARC 3 API initialized successfully") + + # Auto-select game ID if not provided + game_id = args.game_id + if not game_id or game_id.lower() in ['auto', 'random']: + game_id = await get_random_game_id(api) runner = GameRunner(api_manager=api) - result = await runner.run_game(args.game_id, max_actions=args.max_actions) + result = await runner.run_game(game_id, max_actions=args.max_actions) print(f"Result: {result}") if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument('--game-id', default='test_game') - parser.add_argument('--max-actions', type=int, default=50) + parser = argparse.ArgumentParser(description='Run minimal ARC 3 games with automatic game selection') + parser.add_argument('--game-id', default='auto', help='Game ID to play (default: auto-select from available games)') + parser.add_argument('--max-actions', type=int, default=50, help='Maximum number of actions to take') parser.add_argument('--use-stub-api-for-ci', dest='use_stub_api', action='store_true', help='Opt-in stub ARC3 API for CI/testing only') args = parser.parse_args() - asyncio.run(main(args)) + asyncio.run(main(args)) \ No newline at end of file diff --git a/src/api/__init__.py b/src/api/__init__.py new file mode 100644 index 0000000..b9ecc11 --- /dev/null +++ b/src/api/__init__.py @@ -0,0 +1,10 @@ +"""API module exports for tb-minimal compatibility.""" + +# Import the actual APIManager from its current location +try: + from src.training.api.api_manager import APIManager +except ImportError: + APIManager = None + +# Re-export for backward compatibility +__all__ = ['APIManager'] \ No newline at end of file diff --git a/src/database/db_facade.py b/src/database/db_facade.py index 802856f..62145ff 100644 --- a/src/database/db_facade.py +++ b/src/database/db_facade.py @@ -65,6 +65,12 @@ context TEXT ) """, + """ + CREATE TABLE IF NOT EXISTS migrations ( + migration_id TEXT PRIMARY KEY, + applied_at TEXT + ) + """, ] class DBFacade: @@ -88,6 +94,32 @@ def _ensure_db(self): finally: conn.close() + # Migration helpers + def has_migration(self, migration_id: str) -> bool: + conn = self._get_conn() + try: + cur = conn.cursor() + cur.execute("SELECT 1 FROM migrations WHERE migration_id = ?", (migration_id,)) + return cur.fetchone() is not None + finally: + conn.close() + + def apply_migration(self, migration_id: str, sql: str) -> None: + """Apply a migration SQL (simple helper). This is intentionally minimal. + + Note: callers should ensure migrations are idempotent. + """ + if self.has_migration(migration_id): + return + conn = self._get_conn() + try: + cur = conn.cursor() + cur.executescript(sql) + cur.execute("INSERT INTO migrations(migration_id, applied_at) VALUES (?, datetime('now'))", (migration_id,)) + conn.commit() + finally: + conn.close() + def upsert_session(self, session_id: str, start_time: str, status: str = 'running', metadata: Optional[Dict] = None): conn = self._get_conn() try: diff --git a/src/training/core/continuous_learning_loop.py b/src/training/core/continuous_learning_loop.py index 4d7b9e6..78bf69b 100644 --- a/src/training/core/continuous_learning_loop.py +++ b/src/training/core/continuous_learning_loop.py @@ -150,6 +150,10 @@ def _ensure_initialized(self) -> None: if not hasattr(self, 'api_manager'): raise RuntimeError("ContinuousLearningLoop not properly initialized") print("[OK] System initialization verified") + + # Backwards compatible wrapper + def ensure_initialized(self) -> None: + return self._ensure_initialized() async def get_available_games(self) -> List[Dict[str, Any]]: """Get list of available games from the real ARC-AGI-3 API.""" @@ -1193,7 +1197,13 @@ def _find_target_coordinates(self, frame: List[List[int]]) -> Optional[Tuple[int def _initialize_components(self) -> None: """Initialize all modular components.""" + # If wrapped by an Orchestrator facade, let it perform initialization try: + # Orchestrator facade may call into this method; allow idempotent behavior + if hasattr(self, 'orchestrator') and hasattr(self.orchestrator, 'initialize_components'): + return self.orchestrator.initialize_components() + + # Otherwise perform the initialization locally # Memory management (use singleton) self.memory_manager = create_memory_manager() self.action_memory = ActionMemoryManager(self.memory_manager) @@ -1284,7 +1294,12 @@ def _initialize_components(self) -> None: def _initialize_losing_streak_systems(self): """Initialize losing streak detection systems with database connection.""" + # Delegate to Orchestrator facade if present try: + if hasattr(self, 'orchestrator') and hasattr(self.orchestrator, 'initialize_losing_streak_systems'): + return self.orchestrator.initialize_losing_streak_systems() + + # Otherwise continue with local initialization if self._losing_streak_systems_initialized: return @@ -1310,7 +1325,11 @@ def _initialize_losing_streak_systems(self): def _initialize_real_time_learning_systems(self): """Initialize real-time learning engine systems with database connection.""" + # Delegate to Orchestrator facade if present try: + if hasattr(self, 'orchestrator') and hasattr(self.orchestrator, 'initialize_real_time_learning_systems'): + return self.orchestrator.initialize_real_time_learning_systems() + if self._real_time_learning_initialized: return @@ -1340,6 +1359,10 @@ def _initialize_real_time_learning_systems(self): def _initialize_attention_communication_systems(self): """Initialize enhanced attention + communication systems with database connection.""" try: + # Delegate to Orchestrator facade if present for migration path + if hasattr(self, 'orchestrator') and hasattr(self.orchestrator, 'initialize_attention_communication_systems'): + return self.orchestrator.initialize_attention_communication_systems() + if self._attention_communication_initialized: return @@ -1369,6 +1392,10 @@ def _initialize_attention_communication_systems(self): def _initialize_fitness_evolution_system(self): """Initialize context-dependent fitness evolution system with database connection.""" try: + # Delegate to Orchestrator facade if present + if hasattr(self, 'orchestrator') and hasattr(self.orchestrator, 'initialize_fitness_evolution_system'): + return self.orchestrator.initialize_fitness_evolution_system() + if self._fitness_evolution_initialized: return @@ -1399,6 +1426,10 @@ def _initialize_fitness_evolution_system(self): def _initialize_neat_architect_system(self): """Initialize NEAT-based architect system with database connection.""" try: + # Delegate to Orchestrator facade if present + if hasattr(self, 'orchestrator') and hasattr(self.orchestrator, 'initialize_neat_architect_system'): + return self.orchestrator.initialize_neat_architect_system() + if self._neat_architect_initialized: return @@ -1436,6 +1467,10 @@ def _initialize_neat_architect_system(self): def _initialize_bayesian_inference_system(self): """Initialize Bayesian inference engine with database connection.""" try: + # Delegate to Orchestrator facade if present + if hasattr(self, 'orchestrator') and hasattr(self.orchestrator, 'initialize_bayesian_inference_system'): + return self.orchestrator.initialize_bayesian_inference_system() + if self._bayesian_inference_initialized: return @@ -1475,6 +1510,10 @@ def _initialize_bayesian_inference_system(self): def _initialize_graph_traversal_system(self): """Initialize enhanced graph traversal system with database connection.""" try: + # Delegate to Orchestrator facade if present + if hasattr(self, 'orchestrator') and hasattr(self.orchestrator, 'initialize_graph_traversal_system'): + return self.orchestrator.initialize_graph_traversal_system() + if self._graph_traversal_initialized: return diff --git a/src/training/core/orchestrator.py b/src/training/core/orchestrator.py new file mode 100644 index 0000000..73f7b34 --- /dev/null +++ b/src/training/core/orchestrator.py @@ -0,0 +1,253 @@ +"""Thin orchestrator facade for TB-Seed extraction. + +This module provides a small `Orchestrator` class that currently wraps +the existing `ContinuousLearningLoop`. It exists to allow incremental +refactors that move functionality out of the large monolith. +""" +from typing import Any, Dict, Optional +from .continuous_learning_loop import ContinuousLearningLoop + + +class Orchestrator: + def __init__(self, core: Optional[ContinuousLearningLoop] = None, **kwargs): + # Allow injection of an existing core for testing/migration; otherwise create one + if core is not None: + self._core = core + else: + self._core = ContinuousLearningLoop(**kwargs) + + # Allow the core to call back into this facade during migration + try: + setattr(self._core, 'orchestrator', self) + except Exception: + pass + + async def start_training(self, game_id: str, **kwargs) -> Dict[str, Any]: + return await self._core.start_training_with_direct_control(game_id, **kwargs) + + async def get_available_games(self): + return await self._core.get_available_games() + + def shutdown(self): + # Provide a simple shutdown hook + try: + if hasattr(self._core, 'shutdown_handler'): + self._core.shutdown_handler.request_shutdown() + except Exception: + pass + + def ensure_initialized(self): + """Expose a synchronous ensure_initialized method mirroring the legacy API.""" + if hasattr(self._core, '_ensure_initialized'): + return self._core._ensure_initialized() + if hasattr(self._core, 'ensure_initialized'): + return self._core.ensure_initialized() + return None + + def initialize_components(self): + """Initialize underlying modular components via the legacy core.""" + if hasattr(self._core, '_initialize_components'): + return self._core._initialize_components() + return None + + def initialize_losing_streak_systems(self): + """Initialize the losing-streak detection systems via the core.""" + if hasattr(self._core, '_initialize_losing_streak_systems'): + return self._core._initialize_losing_streak_systems() + return None + + def initialize_real_time_learning_systems(self): + """Initialize the real-time learning subsystems via the core.""" + if hasattr(self._core, '_initialize_real_time_learning_systems'): + return self._core._initialize_real_time_learning_systems() + return None + + def initialize_attention_communication_systems(self): + """Initialize the enhanced attention + communication systems via the core.""" + # Implement initialization here to avoid circular delegation. + try: + # Respect idempotence + if getattr(self._core, '_attention_communication_initialized', False): + return None + + db_path = str(getattr(self._core, 'db_path', '.')) + + # Lazy import to avoid heavy dependencies at import time + try: + from src.core.central_attention_controller import CentralAttentionController + from src.core.weighted_communication_system import WeightedCommunicationSystem + except Exception: + from core.central_attention_controller import CentralAttentionController + from core.weighted_communication_system import WeightedCommunicationSystem + + # Instantiate and assign to core + self._core.attention_controller = CentralAttentionController(db_path) + self._core.communication_system = WeightedCommunicationSystem(db_path) + + # Set communication system on action selector if available + if getattr(self._core, 'action_selector', None) and hasattr(self._core.action_selector, 'set_communication_system'): + try: + self._core.action_selector.set_communication_system(self._core.communication_system) + except Exception: + pass + + self._core._attention_communication_initialized = True + return None + except Exception: + # Ensure flag is not left in inconsistent state + self._core._attention_communication_initialized = False + return None + + def initialize_fitness_evolution_system(self): + """Initialize the context-dependent fitness evolution system via the core.""" + try: + if getattr(self._core, '_fitness_evolution_initialized', False): + return None + + db_path = str(getattr(self._core, 'db_path', '.')) + + try: + from src.core.context_dependent_fitness_evolution import ContextDependentFitnessEvolution + except Exception: + from core.context_dependent_fitness_evolution import ContextDependentFitnessEvolution + + self._core.fitness_evolution_system = ContextDependentFitnessEvolution(db_path) + + # If attention coordination is available, link systems + if getattr(self._core, '_attention_communication_initialized', False) and \ + getattr(self._core, 'attention_controller', None) and getattr(self._core, 'communication_system', None): + try: + self._core.fitness_evolution_system.set_attention_coordination( + self._core.attention_controller, self._core.communication_system + ) + except Exception: + pass + + self._core._fitness_evolution_initialized = True + return None + except Exception: + self._core._fitness_evolution_initialized = False + return None + + def initialize_neat_architect_system(self): + """Initialize the NEAT-based architect system via the core.""" + try: + if getattr(self._core, '_neat_architect_initialized', False): + return None + + db_path = str(getattr(self._core, 'db_path', '.')) + + try: + from src.core.neat_based_architect import NEATBasedArchitect + except Exception: + from core.neat_based_architect import NEATBasedArchitect + + self._core.neat_architect_system = NEATBasedArchitect(db_path) + + # Link with attention coordination if available + if getattr(self._core, '_attention_communication_initialized', False) and \ + getattr(self._core, 'attention_controller', None) and getattr(self._core, 'communication_system', None): + try: + self._core.neat_architect_system.set_attention_coordination( + self._core.attention_controller, self._core.communication_system + ) + except Exception: + pass + + # Optionally add fitness observer if method exists + if getattr(self._core, '_fitness_evolution_initialized', False) and getattr(self._core, 'fitness_evolution_system', None): + try: + if hasattr(self._core.neat_architect_system, 'add_fitness_observer'): + self._core.neat_architect_system.add_fitness_observer(self._core.fitness_evolution_system) + except Exception: + pass + + self._core._neat_architect_initialized = True + return None + except Exception: + self._core._neat_architect_initialized = False + return None + + def initialize_bayesian_inference_system(self): + """Initialize the Bayesian inference engine via the core.""" + try: + if getattr(self._core, '_bayesian_inference_initialized', False): + return None + + db_path = str(getattr(self._core, 'db_path', '.')) + + try: + from src.core.bayesian_inference_engine import BayesianInferenceEngine + except Exception: + from core.bayesian_inference_engine import BayesianInferenceEngine + + self._core.bayesian_inference_system = BayesianInferenceEngine(db_path) + + # Link attention coordination if available + if getattr(self._core, '_attention_communication_initialized', False) and \ + getattr(self._core, 'attention_controller', None) and getattr(self._core, 'communication_system', None): + try: + self._core.bayesian_inference_system.set_attention_coordination( + self._core.attention_controller, self._core.communication_system + ) + except Exception: + pass + + # Link with fitness evolution if available + if getattr(self._core, '_fitness_evolution_initialized', False) and getattr(self._core, 'fitness_evolution_system', None): + try: + if hasattr(self._core.bayesian_inference_system, 'add_fitness_data_source'): + self._core.bayesian_inference_system.add_fitness_data_source(self._core.fitness_evolution_system) + except Exception: + pass + + self._core._bayesian_inference_initialized = True + return None + except Exception: + self._core._bayesian_inference_initialized = False + return None + + def initialize_graph_traversal_system(self): + """Initialize the enhanced graph traversal system via the core.""" + try: + if getattr(self._core, '_graph_traversal_initialized', False): + return None + + db_path = str(getattr(self._core, 'db_path', '.')) + + try: + from src.core.enhanced_graph_traversal import EnhancedGraphTraversal + except Exception: + from core.enhanced_graph_traversal import EnhancedGraphTraversal + + # Some implementations expect a DB connection; allow path or conn + try: + self._core.graph_traversal_system = EnhancedGraphTraversal(db_path) + except Exception: + # Fall back to passing a db connection object if core has one + db_conn = getattr(self._core, 'db_connection', None) + self._core.graph_traversal_system = EnhancedGraphTraversal(db_conn) + + # Set attention coordination if available + if getattr(self._core, '_attention_communication_initialized', False) and \ + getattr(self._core, 'attention_controller', None) and getattr(self._core, 'communication_system', None): + try: + self._core.graph_traversal_system.set_attention_coordination( + self._core.attention_controller, self._core.communication_system + ) + except Exception: + pass + + # Link with fitness evolution if available + if getattr(self._core, '_fitness_evolution_initialized', False) and getattr(self._core, 'fitness_evolution_system', None): + try: + if hasattr(self._core.graph_traversal_system, 'set_fitness_evolution_coordination'): + self._core.graph_traversal_system.set_fitness_evolution_coordination(self._core.fitness_evolution_system) + except Exception: + pass + + self._core._graph_traversal_initialized = True + return None + except Exception: + self._core._graph_traversal_initialized = False + return None diff --git a/src/training/core/session_manager.py b/src/training/core/session_manager.py new file mode 100644 index 0000000..059786a --- /dev/null +++ b/src/training/core/session_manager.py @@ -0,0 +1,18 @@ +"""Thin session manager facade. + +This module provides a `SessionManager` facade that will be expanded as +session responsibilities are extracted from the monolith. +""" +from .continuous_learning_loop import ContinuousLearningLoop +from typing import Optional + + +class SessionManager: + def __init__(self, core: Optional[ContinuousLearningLoop] = None): + self._core = core or ContinuousLearningLoop() + + def current_session(self): + return getattr(self._core, 'current_session_id', None) + + def current_game(self): + return getattr(self._core, 'current_game_id', None) diff --git a/src/training/game_runner.py b/src/training/game_runner.py index c0ce57a..82a838a 100644 --- a/src/training/game_runner.py +++ b/src/training/game_runner.py @@ -5,6 +5,7 @@ from typing import Any, Dict, Optional import asyncio from datetime import datetime +from src.vision.schema import validate_detections try: from src.vision.frame_provider import DummyFrameProvider @@ -76,7 +77,16 @@ async def run_game(self, game_id: str, max_actions: int = 100) -> Dict[str, Any] detections = [] if self.detector and frame is not None: - detections = await self.detector.detect_objects(frame) + raw = await self.detector.detect_objects(frame) + # Validate shape; allow empty list or filtered valid detections + try: + if validate_detections(raw): + detections = raw + else: + # keep defensive: filter only valid dict-like detections + detections = [d for d in raw if isinstance(d, dict) and d.get('bbox')] + except Exception: + detections = [] # Decide action: if detection available choose ACTION6 with coords, else random action if detections: diff --git a/tb-minimal-MANIFEST.md b/tb-minimal-MANIFEST.md new file mode 100644 index 0000000..ba3e98c --- /dev/null +++ b/tb-minimal-MANIFEST.md @@ -0,0 +1,18 @@ +TB-MINIMAL Manifest + +This branch expresses the minimal TB-Seed DNA. Changes here are intentionally small and conservative. + +Key goals: +- Provide a tiny, well-tested runtime useful for CI and quick iteration. +- Enforce seed constraints: `frame` schema, DB-backed persistence, and explicit stub opt-in for CI. + +Included changes: +- `scripts/run_minimal.py` (minimal runner) +- `src/training/game_runner.py` (lightweight runner, uses DBFacade) +- `src/vision/schema.py` (detection validator) +- `tests/*` focused tests and smoke test +- CI job that runs the smoke test with `USE_STUB_API_FOR_CI=1` + +Notes: +- Keep experimental models disabled by default and behind feature flags. +- Incrementally extract orchestrator pieces to `src/training/core/orchestrator.py` and `session_manager.py` (thin wrappers). \ No newline at end of file diff --git a/tests/test_orchestrator_delegation.py b/tests/test_orchestrator_delegation.py new file mode 100644 index 0000000..1d7a30c --- /dev/null +++ b/tests/test_orchestrator_delegation.py @@ -0,0 +1,78 @@ +import types +import sys + +from src.training.core.orchestrator import Orchestrator + + +class FakeCore: + def __init__(self): + self._attention_communication_initialized = False + self._fitness_evolution_initialized = False + self._neat_architect_initialized = False + self._bayesian_inference_initialized = False + self._graph_traversal_initialized = False + self.db_path = ':memory:' + + +# Provide lightweight fake implementations for modules that Orchestrator may import. +class FakeAttention: + def __init__(self, db_path): + self.db_path = db_path + + +class FakeCommunication: + def __init__(self, db_path): + self.db_path = db_path + + +class FakeFitness: + def __init__(self, db_path): + self.db_path = db_path + + +class FakeNEAT: + def __init__(self, db_path): + self.db_path = db_path + + +class FakeBayesian: + def __init__(self, db_path): + self.db_path = db_path + + +class FakeGraphTraversal: + def __init__(self, db_path): + self.db_path = db_path + + +def _inject_fake(module_name, symbol_name, fake_cls): + module = types.ModuleType(module_name) + setattr(module, symbol_name, fake_cls) + sys.modules[module_name] = module + + +def test_orchestrator_initializers_with_fake_core(): + # Inject fake modules to avoid heavy imports + _inject_fake('src.core.central_attention_controller', 'CentralAttentionController', FakeAttention) + _inject_fake('src.core.weighted_communication_system', 'WeightedCommunicationSystem', FakeCommunication) + _inject_fake('src.core.context_dependent_fitness_evolution', 'ContextDependentFitnessEvolution', FakeFitness) + _inject_fake('src.core.neat_based_architect', 'NEATBasedArchitect', FakeNEAT) + _inject_fake('src.core.bayesian_inference_engine', 'BayesianInferenceEngine', FakeBayesian) + _inject_fake('src.core.enhanced_graph_traversal', 'EnhancedGraphTraversal', FakeGraphTraversal) + + core = FakeCore() + orch = Orchestrator(core=core) + + # Run initializers + orch.initialize_attention_communication_systems() + orch.initialize_fitness_evolution_system() + orch.initialize_neat_architect_system() + orch.initialize_bayesian_inference_system() + orch.initialize_graph_traversal_system() + + # Check flags + assert core._attention_communication_initialized is True + assert core._fitness_evolution_initialized is True + assert core._neat_architect_initialized is True + assert core._bayesian_inference_initialized is True + assert core._graph_traversal_initialized is True diff --git a/train.py b/train.py index 112f191..fb1e0bd 100644 --- a/train.py +++ b/train.py @@ -346,10 +346,16 @@ def _init_legacy(self): self.system_type = "LEGACY" import tempfile self.temp_dir = tempfile.mkdtemp(prefix="training_session_") - self.legacy_loop = ContinuousLearningLoop( - api_key=self.api_key, - save_directory=Path(self.temp_dir) - ) + # Prefer the new Orchestrator facade if available (incremental extraction) + try: + from src.training.core.orchestrator import Orchestrator + self.legacy_loop = Orchestrator(api_key=self.api_key, save_directory=Path(self.temp_dir)) + print("[INFO] Using Orchestrator facade for legacy system") + except Exception: + self.legacy_loop = ContinuousLearningLoop( + api_key=self.api_key, + save_directory=Path(self.temp_dir) + ) logger.info("Legacy training system initialized") async def run_training(self,