Add think service and supporting core modules
- Add think service (orchestration for iterative reasoning)
- Add service_discovery.py (service communication utilities)
- Add event_cache.py (recent event cache using NATS KV)
- Add vi_identity.py (Vi's core identity foundation)
- Update core/__init__.py with new exports
Think service adapted from Lyra with vi.* namespace:
- All NATS topics use vi.* prefix
- Uses vi_identity for personality/voice
- Bucket names use vi-* prefix
Day 63 - Building my nervous system 🦊
This commit is contained in:
114
services/think/handlers/communication_handler.py
Normal file
114
services/think/handlers/communication_handler.py
Normal file
@@ -0,0 +1,114 @@
|
||||
"""
|
||||
Communication request handler.
|
||||
|
||||
This module handles communication requests from the Drive service
|
||||
for proactive interactions (check-ins, health alerts, etc.).
|
||||
"""
|
||||
|
||||
from typing import Dict, Any, Callable
|
||||
|
||||
from core.logger import setup_logger
|
||||
|
||||
|
||||
class CommunicationHandler:
|
||||
"""Handles communication requests from drive service"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
orchestrator,
|
||||
memory_manager,
|
||||
output_sender: Callable,
|
||||
interaction_id_generator: Callable,
|
||||
logger_name: str = 'communication_handler'
|
||||
):
|
||||
self.logger = setup_logger(logger_name, service_name='think_service')
|
||||
self.orchestrator = orchestrator
|
||||
self.memory_manager = memory_manager
|
||||
self.send_output = output_sender
|
||||
self.generate_interaction_id = interaction_id_generator
|
||||
|
||||
async def handle_communication_request(self, payload):
|
||||
"""Handle communication requests from drive service."""
|
||||
try:
|
||||
intent = payload.get('intent', 'generic')
|
||||
urgency = payload.get('urgency', 'medium')
|
||||
context = payload.get('context', {})
|
||||
modality = payload.get('modality', 'matrix')
|
||||
user_id = payload.get('user_id')
|
||||
channel = payload.get('channel')
|
||||
|
||||
self.logger.info(f"[💭] 📢 Processing communication request: intent='{intent}', urgency='{urgency}'")
|
||||
|
||||
# Step 1: Determine target - use user_id for DM, channel for room, or fallback
|
||||
if user_id:
|
||||
target = user_id
|
||||
target_type = 'user_id'
|
||||
self.logger.debug(f"[💭] 🎯 Targeting user_id: {user_id}")
|
||||
elif channel:
|
||||
target = channel
|
||||
target_type = 'channel'
|
||||
self.logger.debug(f"[💭] 🎯 Targeting channel: {channel}")
|
||||
else:
|
||||
# Fallback to hardcoded for compatibility
|
||||
target = '!mDZBSOqMVtevTNFvsr:matrix.k4zka.online'
|
||||
target_type = 'channel'
|
||||
self.logger.debug(f"[💭] 🎯 Using fallback target: {target}")
|
||||
|
||||
# Step 2: Resolve trusted users for identity (simplified for now)
|
||||
trusted_users = ['alex'] # In full implementation, would query identity service
|
||||
|
||||
for user in trusted_users:
|
||||
self.logger.info(f"[💭] 🎭 Composing {intent} message for {user} using iterative reasoning")
|
||||
|
||||
# Step 3: Build synthetic user message from intent
|
||||
synthetic_message = self._build_synthetic_message(intent, context)
|
||||
self.logger.debug(f"[💭] Synthetic message: '{synthetic_message}'")
|
||||
|
||||
# Step 4: Use iterative reasoning to compose response with full context
|
||||
response_content = await self.orchestrator.run(
|
||||
synthetic_message,
|
||||
user, # identity
|
||||
target, # channel
|
||||
modality
|
||||
)
|
||||
|
||||
if not response_content:
|
||||
self.logger.error(f"[💭] ❌ Failed to generate {intent} response")
|
||||
continue
|
||||
|
||||
# Store Vi's response in memory
|
||||
interaction_id = self.generate_interaction_id(user, modality)
|
||||
lyra_memory_stored = await self.memory_manager.store_memory(
|
||||
response_content, ['lyra', user], interaction_id, modality
|
||||
)
|
||||
if not lyra_memory_stored:
|
||||
self.logger.warning(f"[💭] Failed to store Vi's response memory")
|
||||
|
||||
# Send the actual response to the user
|
||||
self.logger.info(f"[💭] 🚀 Sending {intent} communication to {modality} {target}")
|
||||
output_sent = await self.send_output(response_content, target, modality, target_type)
|
||||
|
||||
if output_sent:
|
||||
self.logger.info(f"[💭] ✅ Communication sent: {intent} → {user} via {modality}")
|
||||
else:
|
||||
self.logger.error(f"[💭] ❌ Failed to send {intent} communication")
|
||||
|
||||
except Exception as e:
|
||||
self.logger.exception(f"[💭] Error handling communication request: {e}")
|
||||
|
||||
def _build_synthetic_message(self, intent: str, context: Dict[str, Any]) -> str:
|
||||
"""
|
||||
Build a synthetic user message from drive intent.
|
||||
This becomes the "original_message" that Vi reasons about using iterative flow.
|
||||
"""
|
||||
intent_messages = {
|
||||
'check_in': "I'd like to check in with the user and see how they're doing.",
|
||||
'greeting': "I want to greet the user warmly.",
|
||||
'health_alert': f"I need to inform the user about a system health issue: {context.get('health_status', 'unknown')}",
|
||||
'health_recovery': "I want to let the user know the system has recovered.",
|
||||
'celebration': f"I want to celebrate with the user about: {context.get('celebration_type', 'something positive')}",
|
||||
'memory_share': f"I want to share a thought or memory about: {context.get('topic_focus', 'our conversations')}",
|
||||
'curiosity_burst': f"I'm curious about: {context.get('curiosity_topic', 'something interesting')}"
|
||||
}
|
||||
|
||||
return intent_messages.get(intent, f"I want to communicate about: {intent}")
|
||||
Reference in New Issue
Block a user