- 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 🦊
205 lines
8.7 KiB
Python
205 lines
8.7 KiB
Python
"""
|
|
Iterative reasoning orchestrator.
|
|
|
|
This module orchestrates the main iterative reasoning loop, coordinating
|
|
between Oracle, step execution, and output sending.
|
|
"""
|
|
|
|
from typing import Optional, Callable, Any
|
|
from datetime import datetime
|
|
|
|
from core.logger import setup_logger
|
|
from core.nats_event_bus import nats_bus as event_bus
|
|
|
|
from .models import IterativeContext, StepAction
|
|
from .oracle_client import OracleClient
|
|
from .step_executor import StepExecutor
|
|
from .formatters import KnowledgeFormatter
|
|
|
|
|
|
class IterativeOrchestrator:
|
|
"""Orchestrates the iterative reasoning process"""
|
|
|
|
def __init__(
|
|
self,
|
|
oracle_client: OracleClient,
|
|
step_executor: StepExecutor,
|
|
formatter: KnowledgeFormatter,
|
|
output_sender: Callable,
|
|
logger_name: str = 'orchestrator'
|
|
):
|
|
self.logger = setup_logger(logger_name, service_name='think_service')
|
|
self.oracle_client = oracle_client
|
|
self.step_executor = step_executor
|
|
self.formatter = formatter
|
|
self.send_output = output_sender
|
|
|
|
async def run(
|
|
self,
|
|
user_message: str,
|
|
identity: str,
|
|
channel: str,
|
|
modality: str
|
|
) -> Optional[str]:
|
|
"""
|
|
Generate response using iterative step-by-step reasoning.
|
|
|
|
Oracle decides one step at a time and sophisticated stopping criteria
|
|
ensure efficient completion.
|
|
"""
|
|
self.logger.info(f"[💭] 🔄 Starting iterative reasoning for: '{user_message[:50]}...'")
|
|
|
|
# Initialize iterative context
|
|
context = IterativeContext(user_message, identity, channel, modality)
|
|
|
|
try:
|
|
# Send initial status to Matrix
|
|
await self.send_output(
|
|
f"🔄 **Iterative Reasoning Started**\n\nAnalyzing your request step by step...",
|
|
channel, modality
|
|
)
|
|
|
|
# Main iterative reasoning loop
|
|
while True:
|
|
# Check stopping criteria
|
|
should_stop, stop_reason = context.should_stop()
|
|
if should_stop:
|
|
self.logger.info(f"[💭] 🛑 Stopping iteration: {stop_reason}")
|
|
await self.send_output(
|
|
f"🎯 **Reasoning Complete**\n\n{stop_reason}\n\n{context.get_summary()}",
|
|
channel, modality
|
|
)
|
|
break
|
|
|
|
# Goal satisfaction check (every 3 steps)
|
|
if context.step_count > 0 and context.step_count % 3 == 0:
|
|
can_answer = await self.oracle_client.check_goal_satisfaction(context)
|
|
if can_answer:
|
|
context.goal_satisfied = True
|
|
self.logger.info(f"[💭] ✅ Goal satisfaction confirmed at step {context.step_count}")
|
|
# Will be caught by stopping criteria on next iteration
|
|
continue
|
|
|
|
# Ask Oracle for next step
|
|
self.logger.info(f"[💭] 🔮 Requesting step {context.step_count + 1} from Oracle")
|
|
next_step = await self.oracle_client.request_next_step(context)
|
|
if not next_step:
|
|
self.logger.warning(f"[💭] ⚠️ Oracle failed to provide next step")
|
|
await self.send_output(
|
|
f"❌ **Oracle Error**: Failed to get next step decision",
|
|
channel, modality
|
|
)
|
|
break
|
|
|
|
self.logger.info(f"[💭] 📋 Step {context.step_count + 1}: {next_step.action} -> {next_step.target} (args: {getattr(next_step, 'function_args', {})})")
|
|
|
|
# Show Oracle's decision to user
|
|
await self.send_output(
|
|
f"🧠 **Oracle Decision**: {next_step.action}\n"
|
|
f"**Target**: {next_step.target or 'N/A'}\n"
|
|
f"**Reasoning**: {next_step.reasoning}",
|
|
channel, modality
|
|
)
|
|
|
|
# Check if Oracle signals readiness for synthesis
|
|
if next_step.action == StepAction.SYNTHESIZE_FINAL.value or next_step.ready:
|
|
self.logger.info(f"[💭] 🎯 Oracle signals ready for synthesis")
|
|
await self.send_output(
|
|
f"🎯 **Ready for Synthesis**: Oracle indicates sufficient information gathered",
|
|
channel, modality
|
|
)
|
|
break
|
|
|
|
# Execute the step directly
|
|
self.logger.info(f"[💭] ⚙️ Executing step {context.step_count + 1}")
|
|
step_result = await self.step_executor.execute_step(next_step, context)
|
|
self.logger.info(f"[💭] ✅ Step {context.step_count + 1} execution completed, success={step_result.success}")
|
|
|
|
context.add_step_result(step_result)
|
|
self.logger.info(f"[💭] 📝 Step {context.step_count} result added to context (total: {len(context.completed_steps)} completed)")
|
|
|
|
# Send detailed execution result to Matrix
|
|
if step_result.success:
|
|
result_summary = self.formatter.format_step_result_for_matrix(step_result)
|
|
await self.send_output(
|
|
f"✅ **Step {context.step_count} Completed**: {next_step.action}\n"
|
|
f"**Execution Time**: {step_result.execution_time_ms:.0f}ms\n"
|
|
f"**Result**: {result_summary}",
|
|
channel, modality
|
|
)
|
|
else:
|
|
await self.send_output(
|
|
f"❌ **Step {context.step_count} Failed**: {step_result.error_message}\n"
|
|
f"**Execution Time**: {step_result.execution_time_ms:.0f}ms",
|
|
channel, modality
|
|
)
|
|
|
|
# Final synthesis
|
|
await self.send_output(
|
|
f"🔮 **Synthesizing Final Response**\n\nCombining insights from {len(context.completed_steps)} successful steps...",
|
|
channel, modality
|
|
)
|
|
|
|
final_response = await self.oracle_client.synthesize_final_response(context)
|
|
|
|
if final_response:
|
|
# Finalize interaction with sentiment/depth detection
|
|
await self._finalize_interaction(
|
|
identity,
|
|
user_message,
|
|
final_response,
|
|
context
|
|
)
|
|
|
|
self.logger.info(f"[💭] ✅ Iterative reasoning completed successfully")
|
|
return final_response
|
|
else:
|
|
self.logger.error(f"[💭] ⚠️ Final synthesis failed")
|
|
await self.send_output(
|
|
f"❌ **Synthesis Failed**\n\nI gathered information but couldn't synthesize a final response. Please try rephrasing your question.",
|
|
channel, modality
|
|
)
|
|
return None
|
|
|
|
except Exception as e:
|
|
self.logger.exception(f"[💭] Error in iterative reasoning: {e}")
|
|
await self.send_output(
|
|
f"❌ **Iterative Reasoning Error**\n\nSomething went wrong during the reasoning process: {str(e)}",
|
|
channel, modality
|
|
)
|
|
return None
|
|
|
|
async def _finalize_interaction(
|
|
self,
|
|
identity: str,
|
|
user_message: str,
|
|
response_content: str,
|
|
context: IterativeContext
|
|
):
|
|
"""
|
|
Finalize interaction by asking oracle to analyze sentiment/depth.
|
|
Called after synthesis completes.
|
|
"""
|
|
try:
|
|
# Ask Oracle to analyze the interaction
|
|
self.logger.debug(f"[💭] Requesting interaction analysis from oracle...")
|
|
analysis = await self.oracle_client.analyze_interaction(context, user_message, response_content)
|
|
|
|
sentiment = analysis.get("sentiment", "positive")
|
|
depth = analysis.get("depth", 0.3)
|
|
|
|
# Publish interaction completion event for identity service
|
|
await event_bus.emit("vi.interaction.completed", {
|
|
"internal_id": identity,
|
|
"sentiment": sentiment, # positive, neutral, negative
|
|
"depth": depth, # 0.0-1.0
|
|
"summary": f"Conversation with {len(context.completed_steps)} reasoning steps",
|
|
"timestamp": datetime.utcnow().isoformat()
|
|
})
|
|
|
|
self.logger.info(f"[💭] 📊 Interaction finalized: {identity} (sentiment={sentiment}, depth={depth:.2f})")
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"[💭] Error finalizing interaction: {e}")
|
|
# Don't publish event if we couldn't analyze properly
|