mirror of
https://github.com/SuperClaude-Org/SuperClaude_Framework.git
synced 2025-12-29 16:16:08 +00:00
Revolutionary transformation from hardcoded Python intelligence to hot-reloadable YAML patterns, enabling dynamic configuration without code changes. ## Phase 1: Foundation Intelligence Complete ### YAML Intelligence Patterns (6 files) - intelligence_patterns.yaml: Multi-dimensional pattern recognition with adaptive learning - mcp_orchestration.yaml: Server selection decision trees with load balancing - hook_coordination.yaml: Parallel execution patterns with dependency resolution - performance_intelligence.yaml: Resource zones and auto-optimization triggers - validation_intelligence.yaml: Health scoring and proactive diagnostic patterns - user_experience.yaml: Project detection and smart UX adaptations ### Python Infrastructure Enhanced (4 components) - intelligence_engine.py: Generic YAML pattern interpreter with hot-reload - learning_engine.py: Enhanced with YAML intelligence integration - yaml_loader.py: Added intelligence configuration helper methods - validate_system.py: New YAML-driven validation with health scoring ### Key Features Implemented - Hot-reload intelligence: Update patterns without code changes or restarts - Declarative configuration: All intelligence logic expressed in YAML - Graceful fallbacks: System works correctly even with missing YAML files - Multi-pattern coordination: Intelligent recommendations from multiple sources - Health scoring: Component-weighted validation with predictive diagnostics - Generic architecture: Single engine consumes all intelligence pattern types ### Testing Results ✅ All components integrate correctly ✅ Hot-reload mechanism functional ✅ Graceful error handling verified ✅ YAML-driven validation operational ✅ Health scoring system working (detected real system issues) This enables users to modify intelligence behavior by editing YAML files, add new pattern types without coding, and hot-reload improvements in real-time. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
891 lines
39 KiB
Python
891 lines
39 KiB
Python
"""
|
|
Learning Engine for SuperClaude-Lite
|
|
|
|
Cross-hook adaptation system that learns from user patterns, operation effectiveness,
|
|
and system performance to continuously improve SuperClaude intelligence.
|
|
"""
|
|
|
|
import json
|
|
import time
|
|
import statistics
|
|
from typing import Dict, Any, List, Optional, Tuple, Set
|
|
from dataclasses import dataclass, asdict
|
|
from enum import Enum
|
|
from pathlib import Path
|
|
|
|
from yaml_loader import config_loader
|
|
from intelligence_engine import IntelligenceEngine
|
|
|
|
|
|
class LearningType(Enum):
|
|
"""Types of learning patterns."""
|
|
USER_PREFERENCE = "user_preference"
|
|
OPERATION_PATTERN = "operation_pattern"
|
|
PERFORMANCE_OPTIMIZATION = "performance_optimization"
|
|
ERROR_RECOVERY = "error_recovery"
|
|
EFFECTIVENESS_FEEDBACK = "effectiveness_feedback"
|
|
|
|
|
|
class AdaptationScope(Enum):
|
|
"""Scope of learning adaptations."""
|
|
SESSION = "session" # Apply only to current session
|
|
PROJECT = "project" # Apply to current project
|
|
USER = "user" # Apply across all user sessions
|
|
GLOBAL = "global" # Apply to all users (anonymized)
|
|
|
|
|
|
@dataclass
|
|
class LearningRecord:
|
|
"""Record of a learning event."""
|
|
timestamp: float
|
|
learning_type: LearningType
|
|
scope: AdaptationScope
|
|
context: Dict[str, Any]
|
|
pattern: Dict[str, Any]
|
|
effectiveness_score: float # 0.0 to 1.0
|
|
confidence: float # 0.0 to 1.0
|
|
metadata: Dict[str, Any]
|
|
|
|
|
|
@dataclass
|
|
class Adaptation:
|
|
"""An adaptation learned from patterns."""
|
|
adaptation_id: str
|
|
pattern_signature: str
|
|
trigger_conditions: Dict[str, Any]
|
|
modifications: Dict[str, Any]
|
|
effectiveness_history: List[float]
|
|
usage_count: int
|
|
last_used: float
|
|
confidence_score: float
|
|
|
|
|
|
@dataclass
|
|
class LearningInsight:
|
|
"""Insight derived from learning patterns."""
|
|
insight_type: str
|
|
description: str
|
|
evidence: List[str]
|
|
recommendations: List[str]
|
|
confidence: float
|
|
impact_score: float
|
|
|
|
|
|
class LearningEngine:
|
|
"""
|
|
Cross-hook adaptation system for continuous improvement.
|
|
|
|
Features:
|
|
- User preference learning and adaptation
|
|
- Operation pattern recognition and optimization
|
|
- Performance feedback integration
|
|
- Cross-hook coordination and knowledge sharing
|
|
- Effectiveness measurement and validation
|
|
- Personalization and project-specific adaptations
|
|
"""
|
|
|
|
def __init__(self, cache_dir: Path):
|
|
self.cache_dir = Path(cache_dir)
|
|
self.cache_dir.mkdir(exist_ok=True)
|
|
|
|
self.learning_records: List[LearningRecord] = []
|
|
self.adaptations: Dict[str, Adaptation] = {}
|
|
self.user_preferences: Dict[str, Any] = {}
|
|
self.project_patterns: Dict[str, Dict[str, Any]] = {}
|
|
|
|
# Initialize intelligence engine for YAML pattern integration
|
|
self.intelligence_engine = IntelligenceEngine()
|
|
|
|
self._load_learning_data()
|
|
|
|
def _load_learning_data(self):
|
|
"""Load existing learning data from cache with robust error handling."""
|
|
# Initialize empty data structures first
|
|
self.learning_records = []
|
|
self.adaptations = {}
|
|
self.user_preferences = {}
|
|
self.project_patterns = {}
|
|
|
|
try:
|
|
# Load learning records with corruption detection
|
|
records_file = self.cache_dir / "learning_records.json"
|
|
if records_file.exists():
|
|
try:
|
|
with open(records_file, 'r') as f:
|
|
content = f.read().strip()
|
|
if not content:
|
|
# Empty file, initialize with empty array
|
|
self._initialize_empty_records_file(records_file)
|
|
elif content == '[]':
|
|
# Valid empty array
|
|
self.learning_records = []
|
|
else:
|
|
# Try to parse JSON
|
|
data = json.loads(content)
|
|
if isinstance(data, list):
|
|
self.learning_records = [
|
|
LearningRecord(**record) for record in data
|
|
if self._validate_learning_record(record)
|
|
]
|
|
else:
|
|
# Invalid format, reinitialize
|
|
self._initialize_empty_records_file(records_file)
|
|
except (json.JSONDecodeError, TypeError, ValueError) as e:
|
|
# JSON corruption detected, reinitialize
|
|
print(f"Learning records corrupted, reinitializing: {e}")
|
|
self._initialize_empty_records_file(records_file)
|
|
else:
|
|
# File doesn't exist, create it
|
|
self._initialize_empty_records_file(records_file)
|
|
|
|
# Load adaptations with error handling
|
|
adaptations_file = self.cache_dir / "adaptations.json"
|
|
if adaptations_file.exists():
|
|
try:
|
|
with open(adaptations_file, 'r') as f:
|
|
data = json.load(f)
|
|
if isinstance(data, dict):
|
|
self.adaptations = {
|
|
k: Adaptation(**v) for k, v in data.items()
|
|
if self._validate_adaptation_data(v)
|
|
}
|
|
except (json.JSONDecodeError, TypeError, ValueError):
|
|
# Corrupted adaptations file, start fresh
|
|
self.adaptations = {}
|
|
|
|
# Load user preferences with error handling
|
|
preferences_file = self.cache_dir / "user_preferences.json"
|
|
if preferences_file.exists():
|
|
try:
|
|
with open(preferences_file, 'r') as f:
|
|
data = json.load(f)
|
|
if isinstance(data, dict):
|
|
self.user_preferences = data
|
|
except (json.JSONDecodeError, TypeError, ValueError):
|
|
self.user_preferences = {}
|
|
|
|
# Load project patterns with error handling
|
|
patterns_file = self.cache_dir / "project_patterns.json"
|
|
if patterns_file.exists():
|
|
try:
|
|
with open(patterns_file, 'r') as f:
|
|
data = json.load(f)
|
|
if isinstance(data, dict):
|
|
self.project_patterns = data
|
|
except (json.JSONDecodeError, TypeError, ValueError):
|
|
self.project_patterns = {}
|
|
|
|
except Exception as e:
|
|
# Final fallback - ensure all data structures are initialized
|
|
print(f"Error loading learning data, using defaults: {e}")
|
|
self.learning_records = []
|
|
self.adaptations = {}
|
|
self.user_preferences = {}
|
|
self.project_patterns = {}
|
|
|
|
def record_learning_event(self,
|
|
learning_type: LearningType,
|
|
scope: AdaptationScope,
|
|
context: Dict[str, Any],
|
|
pattern: Dict[str, Any],
|
|
effectiveness_score: float,
|
|
confidence: float = 1.0,
|
|
metadata: Dict[str, Any] = None) -> str:
|
|
"""
|
|
Record a learning event for future adaptation.
|
|
|
|
Args:
|
|
learning_type: Type of learning event
|
|
scope: Scope of the learning (session, project, user, global)
|
|
context: Context in which the learning occurred
|
|
pattern: Pattern or behavior that was observed
|
|
effectiveness_score: How effective the pattern was (0.0 to 1.0)
|
|
confidence: Confidence in the learning (0.0 to 1.0)
|
|
metadata: Additional metadata about the learning event
|
|
|
|
Returns:
|
|
Learning record ID
|
|
"""
|
|
if metadata is None:
|
|
metadata = {}
|
|
|
|
# Validate effectiveness score bounds
|
|
if not (0.0 <= effectiveness_score <= 1.0):
|
|
raise ValueError(f"Effectiveness score must be between 0.0 and 1.0, got: {effectiveness_score}")
|
|
|
|
# Validate confidence bounds
|
|
if not (0.0 <= confidence <= 1.0):
|
|
raise ValueError(f"Confidence must be between 0.0 and 1.0, got: {confidence}")
|
|
|
|
# Flag suspicious perfect score sequences (potential overfitting)
|
|
if effectiveness_score == 1.0:
|
|
metadata['perfect_score_flag'] = True
|
|
|
|
record = LearningRecord(
|
|
timestamp=time.time(),
|
|
learning_type=learning_type,
|
|
scope=scope,
|
|
context=context,
|
|
pattern=pattern,
|
|
effectiveness_score=effectiveness_score,
|
|
confidence=confidence,
|
|
metadata=metadata
|
|
)
|
|
|
|
self.learning_records.append(record)
|
|
|
|
# Trigger adaptation creation if pattern is significant
|
|
if effectiveness_score > 0.7 and confidence > 0.6:
|
|
self._create_adaptation_from_record(record)
|
|
|
|
# Save to cache
|
|
self._save_learning_data()
|
|
|
|
return f"learning_{int(record.timestamp)}"
|
|
|
|
def _create_adaptation_from_record(self, record: LearningRecord):
|
|
"""Create an adaptation from a significant learning record."""
|
|
pattern_signature = self._generate_pattern_signature(record.pattern, record.context)
|
|
|
|
# Check if adaptation already exists
|
|
if pattern_signature in self.adaptations:
|
|
adaptation = self.adaptations[pattern_signature]
|
|
adaptation.effectiveness_history.append(record.effectiveness_score)
|
|
adaptation.usage_count += 1
|
|
adaptation.last_used = record.timestamp
|
|
|
|
# Update confidence based on consistency
|
|
if len(adaptation.effectiveness_history) > 1:
|
|
consistency = 1.0 - statistics.stdev(adaptation.effectiveness_history[-5:]) / max(statistics.mean(adaptation.effectiveness_history[-5:]), 0.1)
|
|
adaptation.confidence_score = min(consistency * record.confidence, 1.0)
|
|
else:
|
|
# Create new adaptation
|
|
adaptation_id = f"adapt_{int(record.timestamp)}_{len(self.adaptations)}"
|
|
|
|
adaptation = Adaptation(
|
|
adaptation_id=adaptation_id,
|
|
pattern_signature=pattern_signature,
|
|
trigger_conditions=self._extract_trigger_conditions(record.context),
|
|
modifications=self._extract_modifications(record.pattern),
|
|
effectiveness_history=[record.effectiveness_score],
|
|
usage_count=1,
|
|
last_used=record.timestamp,
|
|
confidence_score=record.confidence
|
|
)
|
|
|
|
self.adaptations[pattern_signature] = adaptation
|
|
|
|
def _generate_pattern_signature(self, pattern: Dict[str, Any], context: Dict[str, Any]) -> str:
|
|
"""Generate a unique signature for a pattern using YAML intelligence patterns."""
|
|
# Get pattern dimensions from YAML intelligence patterns
|
|
intelligence_patterns = self.intelligence_engine.evaluate_context(context, 'intelligence_patterns')
|
|
pattern_dimensions = intelligence_patterns.get('recommendations', {}).get('pattern_dimensions', [])
|
|
|
|
# If no YAML dimensions available, use fallback dimensions
|
|
if not pattern_dimensions:
|
|
pattern_dimensions = ['context_type', 'complexity_score', 'operation_type', 'performance_score']
|
|
|
|
key_elements = []
|
|
|
|
# Use YAML-defined dimensions for signature generation
|
|
for dimension in pattern_dimensions:
|
|
if dimension in context:
|
|
value = context[dimension]
|
|
# Bucket numeric values for better grouping
|
|
if isinstance(value, (int, float)) and dimension in ['complexity_score', 'performance_score']:
|
|
bucketed_value = int(value * 10) / 10 # Round to 0.1
|
|
key_elements.append(f"{dimension}:{bucketed_value}")
|
|
elif isinstance(value, (int, float)) and dimension in ['file_count', 'directory_count']:
|
|
bucketed_value = min(int(value), 10) # Cap at 10 for grouping
|
|
key_elements.append(f"{dimension}:{bucketed_value}")
|
|
else:
|
|
key_elements.append(f"{dimension}:{value}")
|
|
elif dimension in pattern:
|
|
key_elements.append(f"{dimension}:{pattern[dimension]}")
|
|
|
|
# Add pattern-specific elements
|
|
for key in ['mcp_server', 'mode', 'compression_level', 'delegation_strategy']:
|
|
if key in pattern and key not in [d.split(':')[0] for d in key_elements]:
|
|
key_elements.append(f"{key}:{pattern[key]}")
|
|
|
|
signature = "_".join(sorted(key_elements))
|
|
return signature if signature else "unknown_pattern"
|
|
|
|
def _extract_trigger_conditions(self, context: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""Extract trigger conditions from context."""
|
|
conditions = {}
|
|
|
|
# Operational conditions
|
|
for key in ['operation_type', 'complexity_score', 'file_count', 'directory_count']:
|
|
if key in context:
|
|
conditions[key] = context[key]
|
|
|
|
# Environmental conditions
|
|
for key in ['resource_usage_percent', 'conversation_length', 'user_expertise']:
|
|
if key in context:
|
|
conditions[key] = context[key]
|
|
|
|
# Project conditions
|
|
for key in ['project_type', 'has_tests', 'is_production']:
|
|
if key in context:
|
|
conditions[key] = context[key]
|
|
|
|
return conditions
|
|
|
|
def _extract_modifications(self, pattern: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""Extract modifications to apply from pattern."""
|
|
modifications = {}
|
|
|
|
# MCP server preferences
|
|
if 'mcp_server' in pattern:
|
|
modifications['preferred_mcp_server'] = pattern['mcp_server']
|
|
|
|
# Mode preferences
|
|
if 'mode' in pattern:
|
|
modifications['preferred_mode'] = pattern['mode']
|
|
|
|
# Flag preferences
|
|
if 'flags' in pattern:
|
|
modifications['suggested_flags'] = pattern['flags']
|
|
|
|
# Performance optimizations
|
|
if 'optimization' in pattern:
|
|
modifications['optimization'] = pattern['optimization']
|
|
|
|
return modifications
|
|
|
|
def get_adaptations_for_context(self, context: Dict[str, Any]) -> List[Adaptation]:
|
|
"""Get relevant adaptations for the current context."""
|
|
relevant_adaptations = []
|
|
|
|
for adaptation in self.adaptations.values():
|
|
if self._matches_trigger_conditions(adaptation.trigger_conditions, context):
|
|
# Check effectiveness threshold
|
|
if adaptation.confidence_score > 0.5 and len(adaptation.effectiveness_history) > 0:
|
|
avg_effectiveness = statistics.mean(adaptation.effectiveness_history)
|
|
if avg_effectiveness > 0.6:
|
|
relevant_adaptations.append(adaptation)
|
|
|
|
# Sort by effectiveness and confidence
|
|
relevant_adaptations.sort(
|
|
key=lambda a: statistics.mean(a.effectiveness_history) * a.confidence_score,
|
|
reverse=True
|
|
)
|
|
|
|
return relevant_adaptations
|
|
|
|
def _matches_trigger_conditions(self, conditions: Dict[str, Any], context: Dict[str, Any]) -> bool:
|
|
"""Check if context matches adaptation trigger conditions."""
|
|
for key, expected_value in conditions.items():
|
|
if key not in context:
|
|
continue
|
|
|
|
context_value = context[key]
|
|
|
|
# Exact match for strings and booleans
|
|
if isinstance(expected_value, (str, bool)):
|
|
if context_value != expected_value:
|
|
return False
|
|
|
|
# Range match for numbers
|
|
elif isinstance(expected_value, (int, float)):
|
|
tolerance = 0.1 if isinstance(expected_value, float) else 1
|
|
if abs(context_value - expected_value) > tolerance:
|
|
return False
|
|
|
|
return True
|
|
|
|
def apply_adaptations(self,
|
|
context: Dict[str, Any],
|
|
base_recommendations: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""
|
|
Apply learned adaptations enhanced with YAML intelligence patterns.
|
|
|
|
Args:
|
|
context: Current operation context
|
|
base_recommendations: Base recommendations before adaptation
|
|
|
|
Returns:
|
|
Enhanced recommendations with learned adaptations and YAML intelligence applied
|
|
"""
|
|
# Get YAML intelligence recommendations first
|
|
mcp_intelligence = self.intelligence_engine.evaluate_context(context, 'mcp_orchestration')
|
|
ux_intelligence = self.intelligence_engine.evaluate_context(context, 'user_experience')
|
|
performance_intelligence = self.intelligence_engine.evaluate_context(context, 'performance_intelligence')
|
|
|
|
# Start with base recommendations and add YAML intelligence
|
|
enhanced_recommendations = base_recommendations.copy()
|
|
|
|
# Integrate YAML-based MCP recommendations
|
|
mcp_recs = mcp_intelligence.get('recommendations', {})
|
|
if mcp_recs.get('primary_server'):
|
|
if 'recommended_mcp_servers' not in enhanced_recommendations:
|
|
enhanced_recommendations['recommended_mcp_servers'] = []
|
|
servers = enhanced_recommendations['recommended_mcp_servers']
|
|
if mcp_recs['primary_server'] not in servers:
|
|
servers.insert(0, mcp_recs['primary_server'])
|
|
|
|
# Add support servers
|
|
for support_server in mcp_recs.get('support_servers', []):
|
|
if support_server not in servers:
|
|
servers.append(support_server)
|
|
|
|
# Integrate UX intelligence (project detection, smart defaults)
|
|
ux_recs = ux_intelligence.get('recommendations', {})
|
|
if ux_recs.get('suggested_servers'):
|
|
if 'recommended_mcp_servers' not in enhanced_recommendations:
|
|
enhanced_recommendations['recommended_mcp_servers'] = []
|
|
for server in ux_recs['suggested_servers']:
|
|
if server not in enhanced_recommendations['recommended_mcp_servers']:
|
|
enhanced_recommendations['recommended_mcp_servers'].append(server)
|
|
|
|
# Integrate performance optimizations
|
|
perf_recs = performance_intelligence.get('recommendations', {})
|
|
if perf_recs.get('optimizations'):
|
|
enhanced_recommendations['performance_optimizations'] = perf_recs['optimizations']
|
|
enhanced_recommendations['resource_zone'] = perf_recs.get('resource_zone', 'green')
|
|
|
|
# Apply learned adaptations on top of YAML intelligence
|
|
relevant_adaptations = self.get_adaptations_for_context(context)
|
|
|
|
for adaptation in relevant_adaptations:
|
|
# Apply modifications from adaptation
|
|
for modification_type, modification_value in adaptation.modifications.items():
|
|
if modification_type == 'preferred_mcp_server':
|
|
# Enhance MCP server selection
|
|
if 'recommended_mcp_servers' not in enhanced_recommendations:
|
|
enhanced_recommendations['recommended_mcp_servers'] = []
|
|
|
|
servers = enhanced_recommendations['recommended_mcp_servers']
|
|
if modification_value not in servers:
|
|
servers.insert(0, modification_value) # Prioritize learned preference
|
|
|
|
elif modification_type == 'preferred_mode':
|
|
# Enhance mode selection
|
|
if 'recommended_modes' not in enhanced_recommendations:
|
|
enhanced_recommendations['recommended_modes'] = []
|
|
|
|
modes = enhanced_recommendations['recommended_modes']
|
|
if modification_value not in modes:
|
|
modes.insert(0, modification_value)
|
|
|
|
elif modification_type == 'suggested_flags':
|
|
# Enhance flag suggestions
|
|
if 'suggested_flags' not in enhanced_recommendations:
|
|
enhanced_recommendations['suggested_flags'] = []
|
|
|
|
for flag in modification_value:
|
|
if flag not in enhanced_recommendations['suggested_flags']:
|
|
enhanced_recommendations['suggested_flags'].append(flag)
|
|
|
|
elif modification_type == 'optimization':
|
|
# Apply performance optimizations
|
|
if 'optimizations' not in enhanced_recommendations:
|
|
enhanced_recommendations['optimizations'] = []
|
|
enhanced_recommendations['optimizations'].append(modification_value)
|
|
|
|
# Update usage tracking
|
|
adaptation.usage_count += 1
|
|
adaptation.last_used = time.time()
|
|
|
|
# Add learning metadata
|
|
enhanced_recommendations['applied_adaptations'] = [
|
|
{
|
|
'id': adaptation.adaptation_id,
|
|
'confidence': adaptation.confidence_score,
|
|
'effectiveness': statistics.mean(adaptation.effectiveness_history)
|
|
}
|
|
for adaptation in relevant_adaptations
|
|
]
|
|
|
|
return enhanced_recommendations
|
|
|
|
def record_effectiveness_feedback(self,
|
|
adaptation_ids: List[str],
|
|
effectiveness_score: float,
|
|
context: Dict[str, Any]):
|
|
"""Record feedback on adaptation effectiveness."""
|
|
for adaptation_id in adaptation_ids:
|
|
# Find adaptation by ID
|
|
adaptation = None
|
|
for adapt in self.adaptations.values():
|
|
if adapt.adaptation_id == adaptation_id:
|
|
adaptation = adapt
|
|
break
|
|
|
|
if adaptation:
|
|
adaptation.effectiveness_history.append(effectiveness_score)
|
|
|
|
# Update confidence based on consistency
|
|
if len(adaptation.effectiveness_history) > 2:
|
|
recent_scores = adaptation.effectiveness_history[-5:]
|
|
consistency = 1.0 - statistics.stdev(recent_scores) / max(statistics.mean(recent_scores), 0.1)
|
|
adaptation.confidence_score = min(consistency, 1.0)
|
|
|
|
# Record learning event
|
|
self.record_learning_event(
|
|
LearningType.EFFECTIVENESS_FEEDBACK,
|
|
AdaptationScope.USER,
|
|
context,
|
|
{'adaptation_id': adaptation_id},
|
|
effectiveness_score,
|
|
adaptation.confidence_score
|
|
)
|
|
|
|
def generate_learning_insights(self) -> List[LearningInsight]:
|
|
"""Generate insights from learning patterns."""
|
|
insights = []
|
|
|
|
# User preference insights
|
|
insights.extend(self._analyze_user_preferences())
|
|
|
|
# Performance pattern insights
|
|
insights.extend(self._analyze_performance_patterns())
|
|
|
|
# Error pattern insights
|
|
insights.extend(self._analyze_error_patterns())
|
|
|
|
# Effectiveness insights
|
|
insights.extend(self._analyze_effectiveness_patterns())
|
|
|
|
return insights
|
|
|
|
def _analyze_user_preferences(self) -> List[LearningInsight]:
|
|
"""Analyze user preference patterns."""
|
|
insights = []
|
|
|
|
# Analyze MCP server preferences
|
|
mcp_usage = {}
|
|
for record in self.learning_records:
|
|
if record.learning_type == LearningType.USER_PREFERENCE:
|
|
server = record.pattern.get('mcp_server')
|
|
if server:
|
|
if server not in mcp_usage:
|
|
mcp_usage[server] = []
|
|
mcp_usage[server].append(record.effectiveness_score)
|
|
|
|
if mcp_usage:
|
|
# Find most effective server
|
|
server_effectiveness = {
|
|
server: statistics.mean(scores)
|
|
for server, scores in mcp_usage.items()
|
|
if len(scores) >= 3
|
|
}
|
|
|
|
if server_effectiveness:
|
|
best_server = max(server_effectiveness, key=server_effectiveness.get)
|
|
best_score = server_effectiveness[best_server]
|
|
|
|
if best_score > 0.8:
|
|
insights.append(LearningInsight(
|
|
insight_type="user_preference",
|
|
description=f"User consistently prefers {best_server} MCP server",
|
|
evidence=[f"Effectiveness score: {best_score:.2f}", f"Usage count: {len(mcp_usage[best_server])}"],
|
|
recommendations=[f"Auto-suggest {best_server} for similar operations"],
|
|
confidence=min(best_score, 1.0),
|
|
impact_score=0.7
|
|
))
|
|
|
|
return insights
|
|
|
|
def _analyze_performance_patterns(self) -> List[LearningInsight]:
|
|
"""Analyze performance optimization patterns."""
|
|
insights = []
|
|
|
|
# Analyze delegation effectiveness
|
|
delegation_records = [
|
|
r for r in self.learning_records
|
|
if r.learning_type == LearningType.PERFORMANCE_OPTIMIZATION
|
|
and 'delegation' in r.pattern
|
|
]
|
|
|
|
if len(delegation_records) >= 5:
|
|
avg_effectiveness = statistics.mean([r.effectiveness_score for r in delegation_records])
|
|
|
|
if avg_effectiveness > 0.75:
|
|
insights.append(LearningInsight(
|
|
insight_type="performance_optimization",
|
|
description="Delegation consistently improves performance",
|
|
evidence=[f"Average effectiveness: {avg_effectiveness:.2f}", f"Sample size: {len(delegation_records)}"],
|
|
recommendations=["Enable delegation for multi-file operations", "Lower delegation threshold"],
|
|
confidence=avg_effectiveness,
|
|
impact_score=0.8
|
|
))
|
|
|
|
return insights
|
|
|
|
def _analyze_error_patterns(self) -> List[LearningInsight]:
|
|
"""Analyze error recovery patterns."""
|
|
insights = []
|
|
|
|
error_records = [
|
|
r for r in self.learning_records
|
|
if r.learning_type == LearningType.ERROR_RECOVERY
|
|
]
|
|
|
|
if len(error_records) >= 3:
|
|
# Analyze common error contexts
|
|
error_contexts = {}
|
|
for record in error_records:
|
|
context_key = record.context.get('operation_type', 'unknown')
|
|
if context_key not in error_contexts:
|
|
error_contexts[context_key] = []
|
|
error_contexts[context_key].append(record)
|
|
|
|
for context, records in error_contexts.items():
|
|
if len(records) >= 2:
|
|
avg_recovery_effectiveness = statistics.mean([r.effectiveness_score for r in records])
|
|
|
|
insights.append(LearningInsight(
|
|
insight_type="error_recovery",
|
|
description=f"Error patterns identified for {context} operations",
|
|
evidence=[f"Occurrence count: {len(records)}", f"Recovery effectiveness: {avg_recovery_effectiveness:.2f}"],
|
|
recommendations=[f"Add proactive validation for {context} operations"],
|
|
confidence=min(len(records) / 5, 1.0),
|
|
impact_score=0.6
|
|
))
|
|
|
|
return insights
|
|
|
|
def _analyze_effectiveness_patterns(self) -> List[LearningInsight]:
|
|
"""Analyze overall effectiveness patterns."""
|
|
insights = []
|
|
|
|
if len(self.learning_records) >= 10:
|
|
recent_records = sorted(self.learning_records, key=lambda r: r.timestamp)[-10:]
|
|
avg_effectiveness = statistics.mean([r.effectiveness_score for r in recent_records])
|
|
|
|
if avg_effectiveness > 0.8:
|
|
insights.append(LearningInsight(
|
|
insight_type="effectiveness_trend",
|
|
description="SuperClaude effectiveness is high and improving",
|
|
evidence=[f"Recent average effectiveness: {avg_effectiveness:.2f}"],
|
|
recommendations=["Continue current learning patterns", "Consider expanding adaptation scope"],
|
|
confidence=avg_effectiveness,
|
|
impact_score=0.9
|
|
))
|
|
elif avg_effectiveness < 0.6:
|
|
insights.append(LearningInsight(
|
|
insight_type="effectiveness_concern",
|
|
description="SuperClaude effectiveness below optimal",
|
|
evidence=[f"Recent average effectiveness: {avg_effectiveness:.2f}"],
|
|
recommendations=["Review recent adaptations", "Gather more user feedback", "Adjust learning thresholds"],
|
|
confidence=1.0 - avg_effectiveness,
|
|
impact_score=0.8
|
|
))
|
|
|
|
return insights
|
|
|
|
def _save_learning_data(self):
|
|
"""Save learning data to cache files with validation and atomic writes."""
|
|
try:
|
|
# Save learning records with validation
|
|
records_file = self.cache_dir / "learning_records.json"
|
|
records_data = []
|
|
for record in self.learning_records:
|
|
try:
|
|
# Convert record to dict and handle enums
|
|
record_dict = asdict(record)
|
|
|
|
# Convert enum values to strings for JSON serialization
|
|
if isinstance(record_dict.get('learning_type'), LearningType):
|
|
record_dict['learning_type'] = record_dict['learning_type'].value
|
|
if isinstance(record_dict.get('scope'), AdaptationScope):
|
|
record_dict['scope'] = record_dict['scope'].value
|
|
|
|
# Validate the record
|
|
if self._validate_learning_record_dict(record_dict):
|
|
records_data.append(record_dict)
|
|
else:
|
|
print(f"Warning: Invalid record skipped: {record_dict}")
|
|
except Exception as e:
|
|
print(f"Warning: Error processing record: {e}")
|
|
continue # Skip invalid records
|
|
|
|
# Atomic write to prevent corruption during write
|
|
temp_file = records_file.with_suffix('.tmp')
|
|
with open(temp_file, 'w') as f:
|
|
json.dump(records_data, f, indent=2)
|
|
temp_file.replace(records_file)
|
|
|
|
# Save adaptations with validation
|
|
adaptations_file = self.cache_dir / "adaptations.json"
|
|
adaptations_data = {}
|
|
for k, v in self.adaptations.items():
|
|
try:
|
|
adapt_dict = asdict(v)
|
|
if self._validate_adaptation_data(adapt_dict):
|
|
adaptations_data[k] = adapt_dict
|
|
except Exception:
|
|
continue
|
|
|
|
temp_file = adaptations_file.with_suffix('.tmp')
|
|
with open(temp_file, 'w') as f:
|
|
json.dump(adaptations_data, f, indent=2)
|
|
temp_file.replace(adaptations_file)
|
|
|
|
# Save user preferences
|
|
preferences_file = self.cache_dir / "user_preferences.json"
|
|
if isinstance(self.user_preferences, dict):
|
|
temp_file = preferences_file.with_suffix('.tmp')
|
|
with open(temp_file, 'w') as f:
|
|
json.dump(self.user_preferences, f, indent=2)
|
|
temp_file.replace(preferences_file)
|
|
|
|
# Save project patterns
|
|
patterns_file = self.cache_dir / "project_patterns.json"
|
|
if isinstance(self.project_patterns, dict):
|
|
temp_file = patterns_file.with_suffix('.tmp')
|
|
with open(temp_file, 'w') as f:
|
|
json.dump(self.project_patterns, f, indent=2)
|
|
temp_file.replace(patterns_file)
|
|
|
|
except Exception as e:
|
|
print(f"Error saving learning data: {e}")
|
|
|
|
def _initialize_empty_records_file(self, records_file: Path):
|
|
"""Initialize learning records file with empty array."""
|
|
try:
|
|
with open(records_file, 'w') as f:
|
|
json.dump([], f)
|
|
except Exception as e:
|
|
print(f"Error initializing records file: {e}")
|
|
|
|
def _validate_learning_record(self, record_data: dict) -> bool:
|
|
"""Validate learning record data structure."""
|
|
required_fields = ['timestamp', 'learning_type', 'scope', 'context', 'pattern', 'effectiveness_score', 'confidence', 'metadata']
|
|
try:
|
|
return all(field in record_data for field in required_fields)
|
|
except (TypeError, AttributeError):
|
|
return False
|
|
|
|
def _validate_learning_record_dict(self, record_dict: dict) -> bool:
|
|
"""Validate learning record dictionary before saving."""
|
|
try:
|
|
# Check required fields exist and have valid types
|
|
if not isinstance(record_dict.get('timestamp'), (int, float)):
|
|
return False
|
|
|
|
# Handle both enum objects and string values for learning_type
|
|
learning_type = record_dict.get('learning_type')
|
|
if not (isinstance(learning_type, str) or isinstance(learning_type, LearningType)):
|
|
return False
|
|
|
|
# Handle both enum objects and string values for scope
|
|
scope = record_dict.get('scope')
|
|
if not (isinstance(scope, str) or isinstance(scope, AdaptationScope)):
|
|
return False
|
|
|
|
if not isinstance(record_dict.get('context'), dict):
|
|
return False
|
|
if not isinstance(record_dict.get('pattern'), dict):
|
|
return False
|
|
if not isinstance(record_dict.get('effectiveness_score'), (int, float)):
|
|
return False
|
|
if not isinstance(record_dict.get('confidence'), (int, float)):
|
|
return False
|
|
if not isinstance(record_dict.get('metadata'), dict):
|
|
return False
|
|
return True
|
|
except (TypeError, AttributeError):
|
|
return False
|
|
|
|
def _validate_adaptation_data(self, adapt_data: dict) -> bool:
|
|
"""Validate adaptation data structure."""
|
|
required_fields = ['adaptation_id', 'pattern_signature', 'trigger_conditions', 'modifications', 'effectiveness_history', 'usage_count', 'last_used', 'confidence_score']
|
|
try:
|
|
return all(field in adapt_data for field in required_fields)
|
|
except (TypeError, AttributeError):
|
|
return False
|
|
|
|
def get_intelligent_recommendations(self, context: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""
|
|
Get comprehensive intelligent recommendations combining YAML patterns and learned adaptations.
|
|
|
|
Args:
|
|
context: Current operation context
|
|
|
|
Returns:
|
|
Comprehensive recommendations with intelligence from multiple sources
|
|
"""
|
|
# Get base recommendations from all YAML intelligence patterns
|
|
base_recommendations = {}
|
|
|
|
# Collect recommendations from all intelligence pattern types
|
|
pattern_types = ['mcp_orchestration', 'hook_coordination', 'performance_intelligence',
|
|
'validation_intelligence', 'user_experience', 'intelligence_patterns']
|
|
|
|
intelligence_results = {}
|
|
for pattern_type in pattern_types:
|
|
try:
|
|
result = self.intelligence_engine.evaluate_context(context, pattern_type)
|
|
intelligence_results[pattern_type] = result
|
|
|
|
# Merge recommendations
|
|
recommendations = result.get('recommendations', {})
|
|
for key, value in recommendations.items():
|
|
if key not in base_recommendations:
|
|
base_recommendations[key] = value
|
|
elif isinstance(base_recommendations[key], list) and isinstance(value, list):
|
|
# Merge lists without duplicates
|
|
base_recommendations[key] = list(set(base_recommendations[key] + value))
|
|
except Exception as e:
|
|
print(f"Warning: Could not evaluate {pattern_type} patterns: {e}")
|
|
|
|
# Apply learned adaptations on top of YAML intelligence
|
|
enhanced_recommendations = self.apply_adaptations(context, base_recommendations)
|
|
|
|
# Add intelligence metadata
|
|
enhanced_recommendations['intelligence_metadata'] = {
|
|
'yaml_patterns_used': list(intelligence_results.keys()),
|
|
'adaptations_applied': len(self.get_adaptations_for_context(context)),
|
|
'confidence_scores': {k: v.get('confidence', 0.0) for k, v in intelligence_results.items()},
|
|
'recommendations_source': 'yaml_intelligence_plus_learned_adaptations'
|
|
}
|
|
|
|
return enhanced_recommendations
|
|
|
|
def cleanup_old_data(self, days_to_keep: int = 30):
|
|
"""Clean up old learning data to prevent cache bloat."""
|
|
cutoff_time = time.time() - (days_to_keep * 24 * 60 * 60)
|
|
|
|
# Remove old learning records
|
|
self.learning_records = [
|
|
record for record in self.learning_records
|
|
if record.timestamp > cutoff_time
|
|
]
|
|
|
|
# Remove unused adaptations
|
|
self.adaptations = {
|
|
k: v for k, v in self.adaptations.items()
|
|
if v.last_used > cutoff_time or v.usage_count > 5
|
|
}
|
|
|
|
self._save_learning_data()
|
|
|
|
def update_last_preference(self, preference_key: str, value: Any):
|
|
"""Simply store the last successful choice - no complex learning."""
|
|
if not self.user_preferences:
|
|
self.user_preferences = {}
|
|
self.user_preferences[preference_key] = {
|
|
"value": value,
|
|
"timestamp": time.time()
|
|
}
|
|
self._save_learning_data()
|
|
|
|
def get_last_preference(self, preference_key: str, default=None):
|
|
"""Get the last successful choice if available."""
|
|
if not self.user_preferences:
|
|
return default
|
|
pref = self.user_preferences.get(preference_key, {})
|
|
return pref.get("value", default)
|
|
|
|
def update_project_info(self, project_path: str, info_type: str, value: Any):
|
|
"""Store basic project information."""
|
|
if not self.project_patterns:
|
|
self.project_patterns = {}
|
|
if project_path not in self.project_patterns:
|
|
self.project_patterns[project_path] = {}
|
|
self.project_patterns[project_path][info_type] = value
|
|
self.project_patterns[project_path]["last_updated"] = time.time()
|
|
self._save_learning_data() |