Coverage for src / osiris_cli / agent_manager.py: 0%
122 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-27 17:41 +0200
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-27 17:41 +0200
1#!/usr/bin/env python3
2"""
3Agent Management System for Osiris CLI
4Handles agent selection, creation, and persistence (Letta-inspired)
5"""
7import json
8from pathlib import Path
9from typing import Optional, List, Dict
10from datetime import datetime
11from rich.console import Console
12from rich.table import Table
13from rich.panel import Panel
14from rich.prompt import Prompt, Confirm
16console = Console()
19class AgentManager:
20 """
21 Manages multiple agents with persistent memory (Letta-style)
22 """
24 def __init__(self):
25 self.agents_dir = Path.home() / ".osiris" / "agents"
26 self.agents_dir.mkdir(parents=True, exist_ok=True)
28 def list_agents(self) -> List[Dict]:
29 """List all available agents"""
30 agents = []
32 for agent_file in self.agents_dir.glob("*.json"):
33 try:
34 with open(agent_file, 'r') as f:
35 data = json.load(f)
37 # Calculate last used
38 last_used = data.get("last_used", "never")
39 last_used_dt = None
40 if last_used != "never":
41 try:
42 dt = datetime.fromisoformat(last_used)
43 last_used_dt = dt
44 now = datetime.now()
45 delta = now - dt
47 if delta.seconds < 60:
48 last_used = "just now"
49 elif delta.seconds < 3600:
50 last_used = f"{delta.seconds // 60} min ago"
51 elif delta.days == 0:
52 last_used = f"{delta.seconds // 3600} hours ago"
53 elif delta.days == 1:
54 last_used = "yesterday"
55 else:
56 last_used = f"{delta.days} days ago"
57 except:
58 pass
60 agents.append({
61 "name": agent_file.stem,
62 "persona": data.get("persona", "General assistant"),
63 "skills": data.get("skills", []),
64 "facts": data.get("core_memory", {}),
65 "last_used": last_used,
66 "last_used_dt": last_used_dt,
67 "file": agent_file
68 })
69 except:
70 continue
72 # Sort by last used
73 agents.sort(key=lambda x: x.get("last_used_dt") or datetime.min, reverse=True)
74 return agents
76 def select_agent(self) -> Optional[str]:
77 """Interactive agent selector (Letta-style)"""
78 agents = self.list_agents()
80 console.print("\n[bold cyan]Select Agent[/bold cyan]\n")
82 # Create table
83 table = Table(show_header=True, header_style="bold cyan")
84 table.add_column("#", style="dim", width=3)
85 table.add_column("Name", style="cyan")
86 table.add_column("Persona", style="dim")
87 table.add_column("Skills", style="green")
88 table.add_column("Last Used", style="yellow")
90 for i, agent in enumerate(agents, 1):
91 skills_str = ", ".join(agent["skills"][:2])
92 if len(agent["skills"]) > 2:
93 skills_str += f" +{len(agent['skills']) - 2}"
95 table.add_row(
96 str(i),
97 agent["name"],
98 agent["persona"][:30] + "..." if len(agent["persona"]) > 30 else agent["persona"],
99 skills_str or "none",
100 agent["last_used"]
101 )
103 # Add "create new" option
104 table.add_row(
105 str(len(agents) + 1),
106 "[cyan]+ Create new agent[/cyan]",
107 "",
108 "",
109 ""
110 )
112 console.print(table)
113 console.print()
115 # Get selection
116 try:
117 choice = Prompt.ask(
118 "Choose agent",
119 default="1" if agents else str(len(agents) + 1)
120 )
122 if choice.isdigit():
123 idx = int(choice) - 1
124 if 0 <= idx < len(agents):
125 return agents[idx]["name"]
126 elif idx == len(agents):
127 return self.create_agent()
128 except (ValueError, KeyboardInterrupt):
129 pass
131 return None
133 def create_agent(self) -> Optional[str]:
134 """Create a new agent interactively"""
135 console.print("\n[bold cyan]Create New Agent[/bold cyan]\n")
137 try:
138 # Get agent name
139 name = Prompt.ask("Agent name", default="assistant")
141 # Get persona
142 console.print("\n[dim]Choose a persona:[/dim]")
143 personas = {
144 "1": ("General Assistant", "A helpful general-purpose AI assistant"),
145 "2": ("Code Expert", "Senior software engineer specializing in code review and debugging"),
146 "3": ("DevOps Engineer", "Infrastructure and deployment specialist"),
147 "4": ("Data Analyst", "Data science and analytics expert"),
148 "5": ("Custom", "Define your own persona")
149 }
151 for key, (title, desc) in personas.items():
152 console.print(f" {key}. [cyan]{title}[/cyan] - [dim]{desc}[/dim]")
154 persona_choice = Prompt.ask("\nChoose persona", default="1")
156 if persona_choice in personas:
157 if persona_choice == "5":
158 persona = Prompt.ask("Enter custom persona")
159 else:
160 persona = personas[persona_choice][1]
161 else:
162 persona = personas["1"][1]
164 # Get skills
165 console.print("\n[dim]Add skills (comma-separated, or press Enter to skip):[/dim]")
166 skills_input = Prompt.ask("Skills", default="")
167 skills = [s.strip() for s in skills_input.split(",") if s.strip()]
169 # Create agent data
170 agent_data = {
171 "name": name,
172 "persona": persona,
173 "skills": skills,
174 "core_memory": {},
175 "created": datetime.now().isoformat(),
176 "last_used": datetime.now().isoformat()
177 }
179 # Save agent
180 agent_file = self.agents_dir / f"{name}.json"
181 with open(agent_file, 'w') as f:
182 json.dump(agent_data, f, indent=2)
184 console.print(f"\n[green]✓[/green] Created agent: {name}\n")
185 return name
187 except KeyboardInterrupt:
188 console.print("\n[yellow]Cancelled[/yellow]\n")
189 return None
191 def update_last_used(self, agent_name: str):
192 """Update agent's last used timestamp"""
193 agent_file = self.agents_dir / f"{agent_name}.json"
194 if agent_file.exists():
195 try:
196 with open(agent_file, 'r') as f:
197 data = json.load(f)
199 data["last_used"] = datetime.now().isoformat()
201 with open(agent_file, 'w') as f:
202 json.dump(data, f, indent=2)
203 except:
204 pass
206 def get_agent_data(self, agent_name: str) -> Dict:
207 """Get agent data"""
208 agent_file = self.agents_dir / f"{agent_name}.json"
209 if agent_file.exists():
210 try:
211 with open(agent_file, 'r') as f:
212 return json.load(f)
213 except:
214 pass
216 # Return default agent data
217 return {
218 "name": agent_name,
219 "persona": "A helpful AI assistant",
220 "skills": [],
221 "core_memory": {},
222 "created": datetime.now().isoformat(),
223 "last_used": datetime.now().isoformat()
224 }
226 def save_agent_data(self, agent_name: str, data: Dict):
227 """Save agent data"""
228 agent_file = self.agents_dir / f"{agent_name}.json"
229 with open(agent_file, 'w') as f:
230 json.dump(data, f, indent=2)
233# Global instance
234agent_manager = AgentManager()