713 lines
23 KiB
Python
Executable File
713 lines
23 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
GOAP Self-Integration Module - Allegro-Primus Child Autonomy System
|
|
Main integration point that connects GOAP with existing monitoring infrastructure.
|
|
Enables true autonomous behavior for the Child.
|
|
"""
|
|
|
|
import asyncio
|
|
import json
|
|
import time
|
|
import signal
|
|
import sys
|
|
from typing import Dict, List, Optional, Any
|
|
from dataclasses import dataclass, field
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
import subprocess
|
|
import sqlite3
|
|
|
|
# GOAP components
|
|
from goals import (
|
|
GoalManager, Goal, GoalCategory, GoalPriority,
|
|
goal_manager
|
|
)
|
|
from actions import (
|
|
ActionLibrary, Action, action_library
|
|
)
|
|
from planner import (
|
|
GOAPPlanner, Plan, PlanStatus, PlanLibrary,
|
|
planner
|
|
)
|
|
from executor import (
|
|
PlanExecutor, ExecutionContext, ExecutionScheduler,
|
|
ExecutionMonitor, executor
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class ChildState:
|
|
"""Complete state of the Child (Allegro-Primus)"""
|
|
timestamp: float = field(default_factory=time.time)
|
|
|
|
# System state
|
|
system: Dict[str, Any] = field(default_factory=dict)
|
|
|
|
# Knowledge state
|
|
knowledge: Dict[str, Any] = field(default_factory=dict)
|
|
|
|
# Skills state
|
|
skills: Dict[str, Any] = field(default_factory=dict)
|
|
|
|
# Social state
|
|
social: Dict[str, Any] = field(default_factory=dict)
|
|
|
|
# Performance state
|
|
performance: Dict[str, Any] = field(default_factory=dict)
|
|
|
|
# Security state
|
|
security: Dict[str, Any] = field(default_factory=dict)
|
|
|
|
# Growth state
|
|
growth: Dict[str, Any] = field(default_factory=dict)
|
|
|
|
# Data state
|
|
data: Dict[str, Any] = field(default_factory=dict)
|
|
|
|
# Adaptation state
|
|
adaptation: Dict[str, Any] = field(default_factory=dict)
|
|
|
|
def to_dict(self) -> Dict:
|
|
return {
|
|
'timestamp': self.timestamp,
|
|
'system': self.system,
|
|
'knowledge': self.knowledge,
|
|
'skills': self.skills,
|
|
'social': self.social,
|
|
'performance': self.performance,
|
|
'security': self.security,
|
|
'growth': self.growth,
|
|
'data': self.data,
|
|
'adaptation': self.adaptation
|
|
}
|
|
|
|
|
|
class WorldStateCollector:
|
|
"""Collects comprehensive world state for the Child"""
|
|
|
|
def __init__(self):
|
|
self.last_collection = 0
|
|
self.collection_interval = 60 # seconds
|
|
self.db_path = "/root/allegro/timmy_metrics.db"
|
|
|
|
def collect(self) -> ChildState:
|
|
"""Collect current world state"""
|
|
state = ChildState()
|
|
|
|
# Collect system state
|
|
state.system = self._collect_system_state()
|
|
|
|
# Collect knowledge state
|
|
state.knowledge = self._collect_knowledge_state()
|
|
|
|
# Collect skills state
|
|
state.skills = self._collect_skills_state()
|
|
|
|
# Collect social state
|
|
state.social = self._collect_social_state()
|
|
|
|
# Collect performance state
|
|
state.performance = self._collect_performance_state()
|
|
|
|
# Collect security state
|
|
state.security = self._collect_security_state()
|
|
|
|
# Collect growth state
|
|
state.growth = self._collect_growth_state()
|
|
|
|
# Collect data state
|
|
state.data = self._collect_data_state()
|
|
|
|
# Collect adaptation state
|
|
state.adaptation = self._collect_adaptation_state()
|
|
|
|
self.last_collection = time.time()
|
|
return state
|
|
|
|
def _collect_system_state(self) -> Dict:
|
|
"""Collect system metrics"""
|
|
try:
|
|
# CPU usage
|
|
cpu_result = subprocess.run(
|
|
['top', '-bn1'],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=5
|
|
)
|
|
cpu_line = [l for l in cpu_result.stdout.split('\n') if 'Cpu(s)' in l]
|
|
cpu_percent = 0
|
|
if cpu_line:
|
|
try:
|
|
cpu_str = cpu_line[0].split('%')[0].split()[-1]
|
|
cpu_percent = float(cpu_str)
|
|
except:
|
|
pass
|
|
|
|
# Memory usage
|
|
mem_result = subprocess.run(
|
|
['free', '-m'],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=5
|
|
)
|
|
mem_lines = mem_result.stdout.split('\n')
|
|
mem_percent = 0
|
|
if len(mem_lines) > 1:
|
|
try:
|
|
mem_info = mem_lines[1].split()
|
|
total = int(mem_info[1])
|
|
used = int(mem_info[2])
|
|
mem_percent = (used / total) * 100 if total > 0 else 0
|
|
except:
|
|
pass
|
|
|
|
# Disk usage
|
|
disk_result = subprocess.run(
|
|
['df', '-h', '/'],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=5
|
|
)
|
|
disk_lines = disk_result.stdout.split('\n')
|
|
disk_percent = 0
|
|
if len(disk_lines) > 1:
|
|
try:
|
|
disk_info = disk_lines[1].split()
|
|
disk_percent = int(disk_info[4].replace('%', ''))
|
|
except:
|
|
pass
|
|
|
|
# Uptime
|
|
uptime_result = subprocess.run(
|
|
['uptime', '-p'],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=5
|
|
)
|
|
uptime_str = uptime_result.stdout.strip()
|
|
uptime_hours = 0
|
|
if 'hour' in uptime_str:
|
|
try:
|
|
uptime_hours = int(uptime_str.split('hour')[0].split()[-1])
|
|
except:
|
|
pass
|
|
|
|
# Recent errors from logs
|
|
recent_errors = self._count_recent_errors()
|
|
|
|
return {
|
|
'cpu_percent': cpu_percent,
|
|
'memory_percent': mem_percent,
|
|
'disk_percent': disk_percent,
|
|
'uptime_hours': uptime_hours,
|
|
'recent_errors': recent_errors,
|
|
'timestamp': time.time()
|
|
}
|
|
except Exception as e:
|
|
return {'error': str(e), 'timestamp': time.time()}
|
|
|
|
def _collect_knowledge_state(self) -> Dict:
|
|
"""Collect knowledge metrics"""
|
|
try:
|
|
# Count knowledge files
|
|
knowledge_dir = Path("/root/allegro/research")
|
|
total_facts = 0
|
|
if knowledge_dir.exists():
|
|
total_facts = len(list(knowledge_dir.glob('**/*.md')))
|
|
|
|
# Get topics from heartbeat logs
|
|
topics = self._extract_topics_from_logs()
|
|
|
|
return {
|
|
'total_facts': total_facts,
|
|
'topics': topics,
|
|
'new_facts_per_hour': len(topics), # Simplified
|
|
'concept_connections': total_facts * 2, # Estimate
|
|
'timestamp': time.time()
|
|
}
|
|
except Exception as e:
|
|
return {'error': str(e), 'timestamp': time.time()}
|
|
|
|
def _collect_skills_state(self) -> Dict:
|
|
"""Collect skills metrics"""
|
|
try:
|
|
# Check installed skills
|
|
skills_dir = Path("/root/wizards/allegro/skills")
|
|
available = []
|
|
if skills_dir.exists():
|
|
available = [d.name for d in skills_dir.iterdir() if d.is_dir()]
|
|
|
|
# Get recently used skills from logs
|
|
recently_used = self._get_recently_used_skills()
|
|
|
|
return {
|
|
'available': available,
|
|
'proficiencies': {s: 0.7 for s in available}, # Default
|
|
'recently_used': recently_used,
|
|
'timestamp': time.time()
|
|
}
|
|
except Exception as e:
|
|
return {'error': str(e), 'timestamp': time.time()}
|
|
|
|
def _collect_social_state(self) -> Dict:
|
|
"""Collect social metrics"""
|
|
try:
|
|
# Query timmy_metrics.db for activity
|
|
active_users = 0
|
|
messages_sent = 0
|
|
|
|
if Path(self.db_path).exists():
|
|
try:
|
|
conn = sqlite3.connect(self.db_path)
|
|
cursor = conn.cursor()
|
|
|
|
# Count heartbeats in last 24h
|
|
cursor.execute('''
|
|
SELECT COUNT(DISTINCT timmy_pubkey) FROM heartbeats
|
|
WHERE timestamp > datetime('now', '-1 day')
|
|
''')
|
|
result = cursor.fetchone()
|
|
active_users = result[0] if result else 0
|
|
|
|
conn.close()
|
|
except:
|
|
pass
|
|
|
|
return {
|
|
'avg_response_time': 15,
|
|
'user_satisfaction': 0.85,
|
|
'active_users': active_users,
|
|
'total_users': max(1, active_users),
|
|
'messages_sent': messages_sent,
|
|
'timestamp': time.time()
|
|
}
|
|
except Exception as e:
|
|
return {'error': str(e), 'timestamp': time.time()}
|
|
|
|
def _collect_performance_state(self) -> Dict:
|
|
"""Collect performance metrics"""
|
|
try:
|
|
# Get execution stats from executor state
|
|
executor_state_path = Path("/root/allegro/goap/executor_state.json")
|
|
if executor_state_path.exists():
|
|
with open(executor_state_path, 'r') as f:
|
|
data = json.load(f)
|
|
success_rate = data.get('total_plans_succeeded', 0) / max(1, data.get('total_plans_executed', 1))
|
|
return {
|
|
'task_success_rate': success_rate,
|
|
'time_efficiency': 0.8,
|
|
'error_rate': 1 - success_rate,
|
|
'timestamp': time.time()
|
|
}
|
|
|
|
return {
|
|
'task_success_rate': 0.8,
|
|
'time_efficiency': 0.8,
|
|
'error_rate': 0.1,
|
|
'timestamp': time.time()
|
|
}
|
|
except Exception as e:
|
|
return {'error': str(e), 'timestamp': time.time()}
|
|
|
|
def _collect_security_state(self) -> Dict:
|
|
"""Collect security metrics"""
|
|
try:
|
|
# Check last backup
|
|
backup_dir = Path("/root/backups")
|
|
last_backup_hours = 999
|
|
if backup_dir.exists():
|
|
backups = sorted(backup_dir.glob('*.tar.gz'), key=lambda p: p.stat().st_mtime, reverse=True)
|
|
if backups:
|
|
last_backup_hours = (time.time() - backups[0].stat().st_mtime) / 3600
|
|
|
|
return {
|
|
'active_alerts': [],
|
|
'credentials_valid': True,
|
|
'last_backup_hours': last_backup_hours,
|
|
'access_anomalies': 0,
|
|
'timestamp': time.time()
|
|
}
|
|
except Exception as e:
|
|
return {'error': str(e), 'timestamp': time.time()}
|
|
|
|
def _collect_growth_state(self) -> Dict:
|
|
"""Collect growth metrics"""
|
|
try:
|
|
return {
|
|
'performance_trend': 0.05,
|
|
'new_capabilities': 0,
|
|
'optimizations_found': 0,
|
|
'new_areas_explored': 0,
|
|
'experiment_success_rate': 0.5,
|
|
'discoveries': 0,
|
|
'timestamp': time.time()
|
|
}
|
|
except Exception as e:
|
|
return {'error': str(e), 'timestamp': time.time()}
|
|
|
|
def _collect_data_state(self) -> Dict:
|
|
"""Collect data metrics"""
|
|
try:
|
|
return {
|
|
'integrity_check': True,
|
|
'pending_syncs': 0,
|
|
'corruption_detected': False,
|
|
'timestamp': time.time()
|
|
}
|
|
except Exception as e:
|
|
return {'error': str(e), 'timestamp': time.time()}
|
|
|
|
def _collect_adaptation_state(self) -> Dict:
|
|
"""Collect adaptation metrics"""
|
|
try:
|
|
return {
|
|
'pattern_recognition_rate': 0.7,
|
|
'change_response_time': 60,
|
|
'accuracy_drift': 0.0,
|
|
'timestamp': time.time()
|
|
}
|
|
except Exception as e:
|
|
return {'error': str(e), 'timestamp': time.time()}
|
|
|
|
def _count_recent_errors(self) -> int:
|
|
"""Count recent errors from logs"""
|
|
try:
|
|
log_dir = Path("/root/allegro/heartbeat_logs")
|
|
if not log_dir.exists():
|
|
return 0
|
|
|
|
error_count = 0
|
|
for log_file in log_dir.glob('*.log'):
|
|
try:
|
|
with open(log_file, 'r') as f:
|
|
content = f.read()
|
|
error_count += content.count('[ERROR]')
|
|
except:
|
|
pass
|
|
|
|
return error_count
|
|
except:
|
|
return 0
|
|
|
|
def _extract_topics_from_logs(self) -> List[str]:
|
|
"""Extract research topics from logs"""
|
|
return ['system_health', 'performance', 'security']
|
|
|
|
def _get_recently_used_skills(self) -> List[str]:
|
|
"""Get recently used skills"""
|
|
return ['monitoring', 'analysis']
|
|
|
|
|
|
class SelfGOAP:
|
|
"""
|
|
Main GOAP integration class for Allegro-Primus Child.
|
|
Provides autonomous goal-directed behavior.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
goal_manager: GoalManager = None,
|
|
action_library: ActionLibrary = None,
|
|
planner: GOAPPlanner = None,
|
|
executor: PlanExecutor = None
|
|
):
|
|
# Components
|
|
self.goal_manager = goal_manager or GoalManager()
|
|
self.action_library = action_library or ActionLibrary()
|
|
self.planner = planner or GOAPPlanner()
|
|
self.executor = executor or PlanExecutor()
|
|
|
|
# State collection
|
|
self.state_collector = WorldStateCollector()
|
|
self.current_state: Optional[ChildState] = None
|
|
|
|
# Scheduling
|
|
self.scheduler = ExecutionScheduler(self.executor)
|
|
self.monitor = ExecutionMonitor(self.executor)
|
|
|
|
# Configuration
|
|
self.autonomy_enabled = True
|
|
self.planning_interval = 300 # Plan every 5 minutes
|
|
self.last_planning = 0
|
|
|
|
# Logging
|
|
self.log_path = Path("/root/allegro/goap/autonomy.log")
|
|
self.log_path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
# State persistence
|
|
self.state_file = Path("/root/allegro/goap/self_state.json")
|
|
|
|
# Running flag
|
|
self.running = False
|
|
|
|
# Load persisted state
|
|
self._load_state()
|
|
|
|
def _load_state(self):
|
|
"""Load persisted state"""
|
|
if self.state_file.exists():
|
|
try:
|
|
with open(self.state_file, 'r') as f:
|
|
data = json.load(f)
|
|
self.autonomy_enabled = data.get('autonomy_enabled', True)
|
|
self.last_planning = data.get('last_planning', 0)
|
|
except Exception as e:
|
|
self._log(f"Failed to load state: {e}")
|
|
|
|
def _save_state(self):
|
|
"""Persist state"""
|
|
try:
|
|
with open(self.state_file, 'w') as f:
|
|
json.dump({
|
|
'autonomy_enabled': self.autonomy_enabled,
|
|
'last_planning': self.last_planning,
|
|
'last_saved': time.time()
|
|
}, f, indent=2)
|
|
except Exception as e:
|
|
self._log(f"Failed to save state: {e}")
|
|
|
|
def _log(self, message: str):
|
|
"""Log message to file and stdout"""
|
|
timestamp = datetime.now().isoformat()
|
|
log_entry = f"[{timestamp}] {message}"
|
|
print(log_entry)
|
|
|
|
try:
|
|
with open(self.log_path, 'a') as f:
|
|
f.write(log_entry + '\n')
|
|
except:
|
|
pass
|
|
|
|
async def initialize(self):
|
|
"""Initialize the GOAP system"""
|
|
self._log("=== GOAP Self-System Initializing ===")
|
|
|
|
# Collect initial state
|
|
self.current_state = self.state_collector.collect()
|
|
self._log(f"Initial state collected: {len(json.dumps(self.current_state.to_dict()))} bytes")
|
|
|
|
# Update goals with current state
|
|
world_state = self.current_state.to_dict()
|
|
self.goal_manager.update_all(world_state)
|
|
|
|
self._log(f"Goals initialized: {len(self.goal_manager.goals)} goals active")
|
|
self._log(f"Actions available: {len(self.action_library.get_all())} actions")
|
|
|
|
# Start scheduler
|
|
await self.scheduler.start()
|
|
|
|
self._log("=== GOAP Self-System Ready ===")
|
|
|
|
async def run_cycle(self):
|
|
"""Run one autonomy cycle"""
|
|
if not self.autonomy_enabled:
|
|
return
|
|
|
|
# Collect current state
|
|
self.current_state = self.state_collector.collect()
|
|
world_state = self.current_state.to_dict()
|
|
|
|
# Update goals
|
|
self.goal_manager.update_all(world_state)
|
|
|
|
# Check if we should plan
|
|
time_since_planning = time.time() - self.last_planning
|
|
if time_since_planning < self.planning_interval:
|
|
return
|
|
|
|
self.last_planning = time.time()
|
|
self._save_state()
|
|
|
|
# Get active goals
|
|
active_goals = self.goal_manager.get_active_goals(threshold=0.8)
|
|
|
|
if not active_goals:
|
|
self._log("All goals satisfied - no planning needed")
|
|
return
|
|
|
|
self._log(f"Active goals: {len(active_goals)}")
|
|
for goal in active_goals[:3]:
|
|
self._log(f" - {goal.name}: satisfaction={goal.state.satisfaction:.2f}, "
|
|
f"urgency={goal.state.urgency:.2f}")
|
|
|
|
# Plan for top goal
|
|
top_goal = active_goals[0]
|
|
self._log(f"Planning for top goal: {top_goal.name}")
|
|
|
|
plan = self.planner.plan(top_goal, world_state)
|
|
|
|
if not plan:
|
|
self._log(f"No plan found for goal: {top_goal.name}")
|
|
return
|
|
|
|
self._log(f"Plan created: {len(plan.actions)} actions, "
|
|
f"estimated cost: {plan.estimated_cost}")
|
|
|
|
# Create execution context
|
|
context = ExecutionContext(
|
|
world_state=world_state,
|
|
user_context={'source': 'autonomous_cycle'},
|
|
system_context={'goal': top_goal.name}
|
|
)
|
|
|
|
# Schedule plan execution
|
|
self.scheduler.schedule(plan, context, priority=top_goal.state.effective_priority)
|
|
|
|
async def run(self):
|
|
"""Main run loop"""
|
|
self.running = True
|
|
|
|
# Initialize
|
|
await self.initialize()
|
|
|
|
self._log("=== GOAP Autonomy Loop Starting ===")
|
|
|
|
try:
|
|
while self.running:
|
|
await self.run_cycle()
|
|
|
|
# Sleep for a bit
|
|
await asyncio.sleep(10)
|
|
|
|
except asyncio.CancelledError:
|
|
self._log("Autonomy loop cancelled")
|
|
except Exception as e:
|
|
self._log(f"Error in autonomy loop: {e}")
|
|
finally:
|
|
await self.shutdown()
|
|
|
|
async def shutdown(self):
|
|
"""Shutdown the GOAP system"""
|
|
self._log("=== GOAP Self-System Shutting Down ===")
|
|
|
|
self.running = False
|
|
|
|
# Stop scheduler
|
|
await self.scheduler.stop()
|
|
|
|
# Save state
|
|
self._save_state()
|
|
self.goal_manager.save_state()
|
|
|
|
self._log("=== GOAP Self-System Shutdown Complete ===")
|
|
|
|
def get_status(self) -> Dict:
|
|
"""Get current system status"""
|
|
return {
|
|
'running': self.running,
|
|
'autonomy_enabled': self.autonomy_enabled,
|
|
'current_state': self.current_state.to_dict() if self.current_state else None,
|
|
'goals': self.goal_manager.get_stats(),
|
|
'actions': self.action_library.get_stats(),
|
|
'planner': self.planner.get_stats(),
|
|
'executor': self.executor.get_stats(),
|
|
'scheduler': self.scheduler.get_queue_status()
|
|
}
|
|
|
|
def enable_autonomy(self):
|
|
"""Enable autonomous behavior"""
|
|
self.autonomy_enabled = True
|
|
self._log("Autonomy enabled")
|
|
self._save_state()
|
|
|
|
def disable_autonomy(self):
|
|
"""Disable autonomous behavior"""
|
|
self.autonomy_enabled = False
|
|
self._log("Autonomy disabled")
|
|
self._save_state()
|
|
|
|
def force_plan(self, goal_name: Optional[str] = None) -> Optional[Plan]:
|
|
"""Force immediate planning for a goal"""
|
|
self.current_state = self.state_collector.collect()
|
|
world_state = self.current_state.to_dict()
|
|
|
|
if goal_name and goal_name in self.goal_manager.goals:
|
|
goal = self.goal_manager.goals[goal_name]
|
|
else:
|
|
goal = self.goal_manager.get_top_goal()
|
|
|
|
if not goal:
|
|
self._log("No goal available for forced planning")
|
|
return None
|
|
|
|
plan = self.planner.plan(goal, world_state)
|
|
|
|
if plan:
|
|
self._log(f"Forced plan created: {len(plan.actions)} actions for {goal.name}")
|
|
|
|
return plan
|
|
|
|
|
|
# =============================================================================
|
|
# CLI INTERFACE
|
|
# =============================================================================
|
|
|
|
async def main():
|
|
"""Main entry point"""
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(description='Allegro-Primus GOAP Autonomy System')
|
|
parser.add_argument('--run', action='store_true', help='Run autonomy loop')
|
|
parser.add_argument('--status', action='store_true', help='Show status')
|
|
parser.add_argument('--enable', action='store_true', help='Enable autonomy')
|
|
parser.add_argument('--disable', action='store_true', help='Disable autonomy')
|
|
parser.add_argument('--plan', type=str, help='Force plan for goal')
|
|
parser.add_argument('--once', action='store_true', help='Run one cycle and exit')
|
|
|
|
args = parser.parse_args()
|
|
|
|
# Create GOAP system
|
|
goap = SelfGOAP()
|
|
|
|
if args.status:
|
|
status = goap.get_status()
|
|
print(json.dumps(status, indent=2, default=str))
|
|
|
|
elif args.enable:
|
|
goap.enable_autonomy()
|
|
print("Autonomy enabled")
|
|
|
|
elif args.disable:
|
|
goap.disable_autonomy()
|
|
print("Autonomy disabled")
|
|
|
|
elif args.plan:
|
|
plan = goap.force_plan(args.plan)
|
|
if plan:
|
|
print(f"Plan for {plan.goal.name}:")
|
|
for i, action in enumerate(plan.actions):
|
|
print(f" {i+1}. {action.name}")
|
|
else:
|
|
print("No plan found")
|
|
|
|
elif args.once:
|
|
await goap.initialize()
|
|
await goap.run_cycle()
|
|
await goap.shutdown()
|
|
|
|
elif args.run:
|
|
# Set up signal handlers
|
|
goap_system = goap
|
|
|
|
def signal_handler(sig, frame):
|
|
print("\nReceived shutdown signal")
|
|
asyncio.create_task(goap_system.shutdown())
|
|
sys.exit(0)
|
|
|
|
signal.signal(signal.SIGINT, signal_handler)
|
|
signal.signal(signal.SIGTERM, signal_handler)
|
|
|
|
# Run
|
|
await goap.run()
|
|
|
|
else:
|
|
parser.print_help()
|
|
|
|
|
|
# Singleton instance for import
|
|
self_goap = SelfGOAP()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main())
|