Merge branch 'main-gitlab-aux' into 'main'

Integrate changes and various fixes, towards 0.5.1

See merge request aliasrobotics/alias_research/cai!232
This commit is contained in:
Víctor Mayoral Vilches 2025-06-20 09:54:00 +00:00
commit ae5ae21df6
4 changed files with 88 additions and 39 deletions

View File

@ -598,42 +598,47 @@ class CompactCommand(Command):
original_model = os.environ.get("CAI_MODEL", "alias0")
os.environ["CAI_MODEL"] = self.compact_model
try:
result = MEMORY_COMMAND_INSTANCE.handle_save([memory_name])
result = MEMORY_COMMAND_INSTANCE.handle_save([memory_name], preserve_history=False)
finally:
os.environ["CAI_MODEL"] = original_model
else:
result = MEMORY_COMMAND_INSTANCE.handle_save([memory_name])
result = MEMORY_COMMAND_INSTANCE.handle_save([memory_name], preserve_history=False)
if result:
console.print(f"\n[green]✓ Conversation compacted successfully![/green]")
console.print("[dim]The memory has been saved and applied to the agent[/dim]")
console.print("[dim]Use '/memory list' to see all saved memories[/dim]")
# Clear the agent's message history after successful compaction
# IMPORTANT: Explicitly clear the history after compaction
# The handle_save with preserve_history=False doesn't always clear properly
console.print("\n[cyan]Clearing conversation history...[/cyan]")
# Find the matching model instance
model_instance = None
for (name, inst_id), model_ref in ACTIVE_MODEL_INSTANCES.items():
if name == agent_name:
model = model_ref() if model_ref else None
if model:
model_instance = model
break
if model_instance:
# Clear the model's message history
model_instance.message_history.clear()
# Reset context usage since we cleared the history
os.environ['CAI_CONTEXT_USAGE'] = '0.0'
console.print("[green]✓ Conversation history cleared[/green]")
# Clear using AGENT_MANAGER (this uses .clear() to maintain reference)
AGENT_MANAGER.clear_history(agent_name)
# Also clear persistent history
if agent_name in PERSISTENT_MESSAGE_HISTORIES:
PERSISTENT_MESSAGE_HISTORIES[agent_name].clear()
# Clear in AGENT_MANAGER as well using the proper method
AGENT_MANAGER.clear_history(agent_name)
# Get the current active agent and clear its model history too
current_agent = AGENT_MANAGER.get_active_agent()
if current_agent and hasattr(current_agent, 'model') and hasattr(current_agent.model, 'message_history'):
current_agent.model.message_history.clear()
# Reset context usage since we cleared the history
os.environ['CAI_CONTEXT_USAGE'] = '0.0'
console.print("[green]✓ Conversation history cleared[/green]")
# Debug: Verify histories are actually cleared
if os.getenv("CAI_DEBUG", "1") == "2":
# Check AGENT_MANAGER
manager_history = AGENT_MANAGER.get_message_history(agent_name)
console.print(f"[dim]Debug: AGENT_MANAGER history length: {len(manager_history)}[/dim]")
# Check active agent (re-fetch to ensure we have the current one)
current_active_agent = AGENT_MANAGER.get_active_agent()
if current_active_agent and hasattr(current_active_agent, 'model') and hasattr(current_active_agent.model, 'message_history'):
console.print(f"[dim]Debug: Active agent model history length: {len(current_active_agent.model.message_history)}[/dim]")
else:
console.print(f"[red]Failed to compact conversation[/red]")

View File

@ -396,7 +396,7 @@ class MemoryCommand(Command):
return True
def handle_save(self, args: Optional[List[str]] = None) -> bool:
def handle_save(self, args: Optional[List[str]] = None, preserve_history: bool = True) -> bool:
"""Save current agent history as memory."""
if not args:
# Use current active agent
@ -471,7 +471,7 @@ Model: {get_compact_model() or os.environ.get("CAI_MODEL", "gpt-4")}
os.environ['CAI_MEMORY'] = 'true'
# Reload the agent with the new memory
self._reload_agent_with_memory(agent_name)
self._reload_agent_with_memory(agent_name, preserve_history=preserve_history)
# Show memory panel
console.print(Panel(
@ -1085,7 +1085,7 @@ Model: {get_compact_model() or os.environ.get("CAI_MODEL", "gpt-4")}
console.print(f"[green]✓ Cleared history for {agent_name}[/green]")
# Reload the agent with the new memory
self._reload_agent_with_memory(agent_name)
self._reload_agent_with_memory(agent_name, preserve_history=preserve_history)
# Show memory panel
console.print(Panel(
@ -1357,8 +1357,14 @@ This session is being continued from a previous conversation that ran out of con
return None
def _reload_agent_with_memory(self, agent_name: str):
"""Reload an agent to apply memory changes while preserving message history."""
def _reload_agent_with_memory(self, agent_name: str, preserve_history: bool = True):
"""Reload an agent to apply memory changes.
Args:
agent_name: Name of the agent to reload
preserve_history: Whether to preserve message history (default True).
Set to False when called from compact to avoid restoring cleared history.
"""
try:
# Get the current agent instance and its history
from cai.sdk.agents.simple_agent_manager import AGENT_MANAGER
@ -1385,12 +1391,15 @@ This session is being continued from a previous conversation that ran out of con
return
# Get the current agent's message history before reloading
current_history = get_agent_message_history(agent_name)
if current_history:
# Store a copy of the history
history_backup = list(current_history)
history_backup = []
if preserve_history:
current_history = get_agent_message_history(agent_name)
if current_history:
# Store a copy of the history
history_backup = list(current_history)
else:
history_backup = []
# When not preserving history (e.g., from compact), clear it before creating new agent
AGENT_MANAGER.clear_history(agent_name)
# Get the agent ID
agent_id = AGENT_MANAGER.get_id_by_name(agent_name)
@ -1412,7 +1421,7 @@ This session is being continued from a previous conversation that ran out of con
AGENT_MANAGER.set_parallel_agent(agent_id, new_agent, agent_name)
# Restore the message history to the new agent instance
if hasattr(new_agent, 'model') and hasattr(new_agent.model, 'message_history'):
if preserve_history and hasattr(new_agent, 'model') and hasattr(new_agent.model, 'message_history'):
# The switch_to_single_agent might have already transferred history
# Only restore if the new agent's history is empty or different
if not new_agent.model.message_history and history_backup:
@ -1425,7 +1434,8 @@ This session is being continued from a previous conversation that ran out of con
# Also update PERSISTENT_MESSAGE_HISTORIES if needed
if agent_name in PERSISTENT_MESSAGE_HISTORIES:
PERSISTENT_MESSAGE_HISTORIES[agent_name].clear()
PERSISTENT_MESSAGE_HISTORIES[agent_name].extend(history_backup)
if preserve_history:
PERSISTENT_MESSAGE_HISTORIES[agent_name].extend(history_backup)
# Update the global active agent in CLI if we're in single agent mode
if agent_id == "P1":

View File

@ -404,8 +404,17 @@ class OpenAIChatCompletionsModel(Model):
else:
self.message_history = []
else:
# Start with empty history - do not inherit from previous agents
self.message_history = []
# Get or create history from AGENT_MANAGER to ensure we share the same list reference
# This is critical for proper history clearing to work
existing_history = AGENT_MANAGER.get_message_history(self.agent_name)
if existing_history is not None and isinstance(existing_history, list):
# Use the existing list reference from AGENT_MANAGER
self.message_history = existing_history
else:
# Create new history and ensure AGENT_MANAGER has it too
self.message_history = []
if self.agent_name not in AGENT_MANAGER._message_history:
AGENT_MANAGER._message_history[self.agent_name] = self.message_history
# Register with SimpleAgentManager only when explicitly created
# This prevents phantom instances during module imports
@ -415,6 +424,11 @@ class OpenAIChatCompletionsModel(Model):
AGENT_MANAGER.set_parallel_agent(agent_id, self, self.agent_name)
else:
AGENT_MANAGER.set_active_agent(self, self.agent_name, agent_id)
# CRITICAL: Ensure AGENT_MANAGER uses the same list reference as the model
# This is necessary for proper history clearing to work
if not PARALLEL_ISOLATION.is_parallel_mode():
AGENT_MANAGER._message_history[self.agent_name] = self.message_history
# Instance-based converter
self._converter = _Converter()
@ -488,8 +502,11 @@ class OpenAIChatCompletionsModel(Model):
if not is_duplicate:
self.message_history.append(msg)
# Also update SimpleAgentManager
AGENT_MANAGER.add_to_history(self.agent_name, msg)
# Also update SimpleAgentManager ONLY if they're not the same list reference
# This avoids double-adding when they share the same list
manager_history = AGENT_MANAGER.get_message_history(self.agent_name)
if manager_history is not self.message_history:
AGENT_MANAGER.add_to_history(self.agent_name, msg)
# Update isolated history if in parallel mode
if PARALLEL_ISOLATION.is_parallel_mode() and self.agent_id:
PARALLEL_ISOLATION.update_isolated_history(self.agent_id, msg)

View File

@ -75,7 +75,11 @@ class SimpleAgentManager:
# Initialize message history for this agent if needed
if agent_name not in self._message_history:
self._message_history[agent_name] = []
# Check if the agent's model already has a history and use that reference
if hasattr(agent, 'model') and hasattr(agent.model, 'message_history'):
self._message_history[agent_name] = agent.model.message_history
else:
self._message_history[agent_name] = []
def get_active_agent(self):
"""Get the active agent instance."""
@ -100,7 +104,16 @@ class SimpleAgentManager:
def clear_history(self, agent_name: str):
"""Clear history for an agent."""
if agent_name in self._message_history:
self._message_history[agent_name] = []
# Clear the list in-place to maintain the same reference
# This is critical when the model and manager share the same list
self._message_history[agent_name].clear()
# Also clear the active agent's model instance history if it matches
# This handles cases where they don't share the same reference
if self._active_agent and self._active_agent_name == agent_name:
agent = self._active_agent()
if agent and hasattr(agent, 'model') and hasattr(agent.model, 'message_history'):
agent.model.message_history.clear()
def clear_all_histories(self):
"""Clear all message histories."""
@ -186,7 +199,11 @@ class SimpleAgentManager:
# Initialize message history for this agent if needed
if agent_name not in self._message_history:
self._message_history[agent_name] = []
# Check if the agent's model already has a history and use that reference
if hasattr(agent, 'model') and hasattr(agent.model, 'message_history'):
self._message_history[agent_name] = agent.model.message_history
else:
self._message_history[agent_name] = []
def clear_parallel_agents(self):
"""Clear all parallel agents (when switching to single agent mode)."""