Files
temp/src/cleveragents/interactive/session.py
T

212 lines
7.0 KiB
Python

"""
Interactive session module for CleverAgents.
This module defines the InteractiveSession class which handles an interactive command-line session
with the agent network.
"""
import asyncio
import json
import os
import readline
import sys
from pathlib import Path
from typing import Any
from typing import Dict
from typing import List
from typing import Optional
from cleveragents.core.exceptions import InteractiveSessionError
from cleveragents.interactive.commands import execute_command
from cleveragents.interactive.commands import parse_command
from cleveragents.routing.router import Router
class InteractiveSession:
"""
InteractiveSession manages an interactive chat session with the agent network.
Attributes:
router (Router): The router for processing messages.
history_file (Optional[Path]): Path to the history file.
history (List[Dict[str, Any]]): The conversation history.
context (Dict[str, Any]): The current context for message processing.
verbose (bool): Whether to enable verbose output.
"""
def __init__(
self,
routers: Dict[str, Router],
initial_route_name: str,
history_file: Optional[Path] = None,
verbose: bool = False,
initial_context: Optional[Dict[str, Any]] = None,
):
"""
Initialize the interactive session.
Args:
routers: Dictionary of routers keyed by route name.
initial_route_name: Route to start the session with.
history_file: Conversation history file.
verbose: Verbose flag.
"""
self.routers = routers
self.active_route_name = initial_route_name
self.router = self.routers[self.active_route_name]
self.history_file = history_file
self.history: List[Dict[str, Any]] = []
# Use supplied initial context (with routers, globals, etc.) or an empty dict
self.context: Dict[str, Any] = initial_context or {}
self.verbose = verbose
self.running = False
def load_history(self) -> None:
"""
Load conversation history from the history file if it exists.
Raises:
InteractiveSessionError: If the history file cannot be loaded.
"""
if self.history_file and self.history_file.exists():
try:
with open(self.history_file, "r") as f:
self.history = json.load(f)
except Exception as e:
raise InteractiveSessionError(f"Failed to load history file: {str(e)}")
def save_history(self) -> None:
"""
Save conversation history to the history file.
Raises:
InteractiveSessionError: If the history file cannot be saved.
"""
if self.history_file:
try:
with open(self.history_file, "w") as f:
json.dump(self.history, f, indent=2)
except Exception as e:
raise InteractiveSessionError(f"Failed to save history file: {str(e)}")
async def process_message(self, message: str) -> str:
"""
Process a message through the agent network.
Args:
message: The message to process.
Returns:
The response from the agent network.
Raises:
InteractiveSessionError: If message processing fails.
"""
try:
return await self.router.process_message(message, self.context)
except Exception as e:
raise InteractiveSessionError(f"Failed to process message: {str(e)}") from e
def add_to_history(self, role: str, content: str) -> None:
"""
Add a message to the conversation history.
Args:
role: The role of the message sender ('user' or 'assistant').
content: The content of the message.
"""
self.history.append({"role": role, "content": content})
def display_history(self, limit: int = 10) -> None:
"""
Display the conversation history.
Args:
limit: Maximum number of messages to display.
"""
start = max(0, len(self.history) - limit)
for i, entry in enumerate(self.history[start:], start=start + 1):
role = entry["role"]
content = entry["content"]
prefix = "You: " if role == "user" else "Agent: "
print(f"{i}. {prefix}{content}")
def process_command(self, command_str: str) -> bool:
"""
Parse & execute a slash-command.
Args:
command_str: Raw user input beginning with '/'.
Returns:
True if the session should continue, False otherwise.
"""
command_dict = parse_command(command_str)
result = execute_command(command_dict, {"session": self})
if result.get("message"):
print(result["message"])
return result.get("continue", True)
async def run(self) -> None:
"""
Start the interactive session.
This method starts a text-based interactive loop.
Raises:
InteractiveSessionError: If the session cannot be started or an error occurs during the session.
"""
try:
self.load_history()
print("Starting interactive session.")
print(f"Active route: '{self.active_route_name}'")
print("Type '/help' for commands, '/exit' to quit.")
self.running = True
while self.running:
try:
user_input = input("You: ")
# Check if this is a command
if user_input.startswith("/"):
self.running = self.process_command(user_input)
continue
# Process the message
self.add_to_history("user", user_input)
# Update the per-turn initial message so that routers and
# template transforms always reference the latest user input.
self.context["initial_message"] = user_input
if self.verbose:
print("Processing message...")
response = await self.process_message(user_input)
print(f"Agent: {response}")
self.add_to_history("assistant", response)
except KeyboardInterrupt:
print("\nInterrupted by user.")
self.running = False
break
except InteractiveSessionError as e:
if e.__cause__:
print(f"Error: {str(e.__cause__)}")
else:
print(f"Error: {str(e)}")
except Exception as e:
print(f"An unexpected error occurred: {e}")
self.save_history()
print("Session ended.")
except Exception as e:
raise InteractiveSessionError(
f"Failed to run interactive session: {str(e)}"
)