autonomy build, phase 1
This commit is contained in:
74
cortex/autonomy/self/analyzer.py
Normal file
74
cortex/autonomy/self/analyzer.py
Normal file
@@ -0,0 +1,74 @@
|
||||
"""
|
||||
Analyze interactions and update self-state accordingly.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Dict, Any
|
||||
from .state import update_self_state
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def analyze_and_update_state(
|
||||
monologue: Dict[str, Any],
|
||||
user_prompt: str,
|
||||
response: str,
|
||||
context: Dict[str, Any]
|
||||
) -> None:
|
||||
"""
|
||||
Analyze interaction and update self-state.
|
||||
|
||||
This runs after response generation to update Lyra's internal state
|
||||
based on the interaction.
|
||||
|
||||
Args:
|
||||
monologue: Inner monologue output
|
||||
user_prompt: User's message
|
||||
response: Lyra's response
|
||||
context: Full context state
|
||||
"""
|
||||
|
||||
# Simple heuristics for state updates
|
||||
# TODO: Replace with LLM-based sentiment analysis in Phase 2
|
||||
|
||||
mood_delta = 0.0
|
||||
energy_delta = 0.0
|
||||
confidence_delta = 0.0
|
||||
curiosity_delta = 0.0
|
||||
new_focus = None
|
||||
|
||||
# Analyze intent from monologue
|
||||
intent = monologue.get("intent", "").lower() if monologue else ""
|
||||
|
||||
if "technical" in intent or "complex" in intent:
|
||||
energy_delta = -0.05 # Deep thinking is tiring
|
||||
confidence_delta = 0.05 if len(response) > 200 else -0.05
|
||||
new_focus = "technical_problem"
|
||||
|
||||
elif "creative" in intent or "brainstorm" in intent:
|
||||
mood_delta = 0.1 # Creative work is engaging
|
||||
curiosity_delta = 0.1
|
||||
new_focus = "creative_exploration"
|
||||
|
||||
elif "clarification" in intent or "confused" in intent:
|
||||
confidence_delta = -0.05
|
||||
new_focus = "understanding_user"
|
||||
|
||||
elif "simple" in intent or "casual" in intent:
|
||||
energy_delta = 0.05 # Light conversation is refreshing
|
||||
new_focus = "conversation"
|
||||
|
||||
# Check for learning opportunities (questions in user prompt)
|
||||
if "?" in user_prompt and any(word in user_prompt.lower() for word in ["how", "why", "what"]):
|
||||
curiosity_delta += 0.05
|
||||
|
||||
# Update state
|
||||
update_self_state(
|
||||
mood_delta=mood_delta,
|
||||
energy_delta=energy_delta,
|
||||
new_focus=new_focus,
|
||||
confidence_delta=confidence_delta,
|
||||
curiosity_delta=curiosity_delta
|
||||
)
|
||||
|
||||
logger.info(f"Self-state updated based on interaction: focus={new_focus}")
|
||||
@@ -1,11 +1,189 @@
|
||||
"""
|
||||
Stub for self state management.
|
||||
Self-state management for Project Lyra.
|
||||
Maintains persistent identity, mood, energy, and focus across sessions.
|
||||
"""
|
||||
|
||||
def load_self_state():
|
||||
"""Load self state - stub implementation"""
|
||||
return {
|
||||
"mood": "neutral",
|
||||
"energy": 0.8,
|
||||
"focus": "user_request"
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
# Configuration
|
||||
STATE_FILE = Path(os.getenv("SELF_STATE_FILE", "/app/data/self_state.json"))
|
||||
VERBOSE_DEBUG = os.getenv("VERBOSE_DEBUG", "false").lower() == "true"
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
if VERBOSE_DEBUG:
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
# Default state structure
|
||||
DEFAULT_STATE = {
|
||||
"mood": "neutral",
|
||||
"energy": 0.8,
|
||||
"focus": "user_request",
|
||||
"confidence": 0.7,
|
||||
"curiosity": 0.5,
|
||||
"last_updated": None,
|
||||
"interaction_count": 0,
|
||||
"learning_queue": [], # Topics Lyra wants to explore
|
||||
"active_goals": [], # Self-directed goals
|
||||
"preferences": {
|
||||
"verbosity": "medium",
|
||||
"formality": "casual",
|
||||
"proactivity": 0.3 # How likely to suggest things unprompted
|
||||
},
|
||||
"metadata": {
|
||||
"version": "1.0",
|
||||
"created_at": None
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SelfState:
|
||||
"""Manages Lyra's persistent self-state."""
|
||||
|
||||
def __init__(self):
|
||||
self._state = self._load_state()
|
||||
|
||||
def _load_state(self) -> Dict[str, Any]:
|
||||
"""Load state from disk or create default."""
|
||||
if STATE_FILE.exists():
|
||||
try:
|
||||
with open(STATE_FILE, 'r') as f:
|
||||
state = json.load(f)
|
||||
logger.info(f"Loaded self-state from {STATE_FILE}")
|
||||
return state
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to load self-state: {e}")
|
||||
return self._create_default_state()
|
||||
else:
|
||||
return self._create_default_state()
|
||||
|
||||
def _create_default_state(self) -> Dict[str, Any]:
|
||||
"""Create and save default state."""
|
||||
state = DEFAULT_STATE.copy()
|
||||
state["metadata"]["created_at"] = datetime.now().isoformat()
|
||||
state["last_updated"] = datetime.now().isoformat()
|
||||
self._save_state(state)
|
||||
logger.info("Created new default self-state")
|
||||
return state
|
||||
|
||||
def _save_state(self, state: Dict[str, Any]) -> None:
|
||||
"""Persist state to disk."""
|
||||
try:
|
||||
STATE_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(STATE_FILE, 'w') as f:
|
||||
json.dump(state, f, indent=2)
|
||||
if VERBOSE_DEBUG:
|
||||
logger.debug(f"Saved self-state to {STATE_FILE}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to save self-state: {e}")
|
||||
|
||||
def get_state(self) -> Dict[str, Any]:
|
||||
"""Get current state snapshot."""
|
||||
return self._state.copy()
|
||||
|
||||
def update_from_interaction(
|
||||
self,
|
||||
mood_delta: float = 0.0,
|
||||
energy_delta: float = 0.0,
|
||||
new_focus: Optional[str] = None,
|
||||
confidence_delta: float = 0.0,
|
||||
curiosity_delta: float = 0.0
|
||||
) -> None:
|
||||
"""
|
||||
Update state based on interaction.
|
||||
|
||||
Args:
|
||||
mood_delta: Change in mood (-1.0 to 1.0)
|
||||
energy_delta: Change in energy (-1.0 to 1.0)
|
||||
new_focus: New focus area
|
||||
confidence_delta: Change in confidence
|
||||
curiosity_delta: Change in curiosity
|
||||
"""
|
||||
# Apply deltas with bounds checking
|
||||
self._state["energy"] = max(0.0, min(1.0,
|
||||
self._state.get("energy", 0.8) + energy_delta))
|
||||
|
||||
self._state["confidence"] = max(0.0, min(1.0,
|
||||
self._state.get("confidence", 0.7) + confidence_delta))
|
||||
|
||||
self._state["curiosity"] = max(0.0, min(1.0,
|
||||
self._state.get("curiosity", 0.5) + curiosity_delta))
|
||||
|
||||
# Update focus if provided
|
||||
if new_focus:
|
||||
self._state["focus"] = new_focus
|
||||
|
||||
# Update mood (simplified sentiment)
|
||||
if mood_delta != 0:
|
||||
mood_map = ["frustrated", "neutral", "engaged", "excited"]
|
||||
current_mood_idx = 1 # neutral default
|
||||
if self._state.get("mood") in mood_map:
|
||||
current_mood_idx = mood_map.index(self._state["mood"])
|
||||
|
||||
new_mood_idx = max(0, min(len(mood_map) - 1,
|
||||
int(current_mood_idx + mood_delta * 2)))
|
||||
self._state["mood"] = mood_map[new_mood_idx]
|
||||
|
||||
# Increment interaction counter
|
||||
self._state["interaction_count"] = self._state.get("interaction_count", 0) + 1
|
||||
self._state["last_updated"] = datetime.now().isoformat()
|
||||
|
||||
# Persist changes
|
||||
self._save_state(self._state)
|
||||
|
||||
if VERBOSE_DEBUG:
|
||||
logger.debug(f"Updated self-state: mood={self._state['mood']}, "
|
||||
f"energy={self._state['energy']:.2f}, "
|
||||
f"confidence={self._state['confidence']:.2f}")
|
||||
|
||||
def add_learning_goal(self, topic: str) -> None:
|
||||
"""Add topic to learning queue."""
|
||||
queue = self._state.get("learning_queue", [])
|
||||
if topic not in [item.get("topic") for item in queue]:
|
||||
queue.append({
|
||||
"topic": topic,
|
||||
"added_at": datetime.now().isoformat(),
|
||||
"priority": 0.5
|
||||
})
|
||||
self._state["learning_queue"] = queue
|
||||
self._save_state(self._state)
|
||||
logger.info(f"Added learning goal: {topic}")
|
||||
|
||||
def add_active_goal(self, goal: str, context: str = "") -> None:
|
||||
"""Add self-directed goal."""
|
||||
goals = self._state.get("active_goals", [])
|
||||
goals.append({
|
||||
"goal": goal,
|
||||
"context": context,
|
||||
"created_at": datetime.now().isoformat(),
|
||||
"status": "active"
|
||||
})
|
||||
self._state["active_goals"] = goals
|
||||
self._save_state(self._state)
|
||||
logger.info(f"Added active goal: {goal}")
|
||||
|
||||
|
||||
# Global instance
|
||||
_self_state_instance = None
|
||||
|
||||
def get_self_state_instance() -> SelfState:
|
||||
"""Get or create global SelfState instance."""
|
||||
global _self_state_instance
|
||||
if _self_state_instance is None:
|
||||
_self_state_instance = SelfState()
|
||||
return _self_state_instance
|
||||
|
||||
|
||||
def load_self_state() -> Dict[str, Any]:
|
||||
"""Load self state - public API for backwards compatibility."""
|
||||
return get_self_state_instance().get_state()
|
||||
|
||||
|
||||
def update_self_state(**kwargs) -> None:
|
||||
"""Update self state - public API."""
|
||||
get_self_state_instance().update_from_interaction(**kwargs)
|
||||
|
||||
Reference in New Issue
Block a user