Skip to content
Open
1 change: 1 addition & 0 deletions BRANCH_FEATURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
TB-MINIMAL: Focus = minimal runtime, light deps, CI smoke tests.
5 changes: 5 additions & 0 deletions FEATURES_SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

22 changes: 22 additions & 0 deletions README_MINIMAL.md
Original file line number Diff line number Diff line change
@@ -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.
81 changes: 72 additions & 9 deletions scripts/run_minimal.py
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -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))
10 changes: 10 additions & 0 deletions src/api/__init__.py
Original file line number Diff line number Diff line change
@@ -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']
32 changes: 32 additions & 0 deletions src/database/db_facade.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@
context TEXT
)
""",
"""
CREATE TABLE IF NOT EXISTS migrations (
migration_id TEXT PRIMARY KEY,
applied_at TEXT
)
""",
]

class DBFacade:
Expand All @@ -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:
Expand Down
39 changes: 39 additions & 0 deletions src/training/core/continuous_learning_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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

Expand All @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
Loading