mirror of https://github.com/kortix-ai/suna.git
232 lines
9.9 KiB
Python
232 lines
9.9 KiB
Python
from typing import Dict, Any, Optional
|
|
from utils.logger import logger
|
|
from services.supabase import DBConnection
|
|
from datetime import datetime, timezone
|
|
|
|
|
|
class SunaDefaultAgentService:
|
|
"""Simplified Suna agent management service."""
|
|
|
|
def __init__(self, db: DBConnection = None):
|
|
self._db = db or DBConnection()
|
|
logger.debug("🔄 SunaDefaultAgentService initialized (simplified)")
|
|
|
|
async def get_suna_default_config(self) -> Dict[str, Any]:
|
|
"""Get the current Suna configuration."""
|
|
from agent.suna_config import SUNA_CONFIG
|
|
return SUNA_CONFIG.copy()
|
|
|
|
async def install_for_all_users(self) -> Dict[str, Any]:
|
|
"""Install Suna agent for all users who don't have one."""
|
|
logger.debug("🚀 Installing Suna agents for users who don't have them")
|
|
|
|
try:
|
|
client = await self._db.client
|
|
|
|
# Get all personal accounts
|
|
accounts_result = await client.schema('basejump').table('accounts').select('id').eq('personal_account', True).execute()
|
|
all_account_ids = {row['id'] for row in accounts_result.data} if accounts_result.data else set()
|
|
|
|
# Get existing Suna agents
|
|
existing_result = await client.table('agents').select('account_id').eq('metadata->>is_suna_default', 'true').execute()
|
|
existing_account_ids = {row['account_id'] for row in existing_result.data} if existing_result.data else set()
|
|
|
|
# Find accounts without Suna
|
|
missing_accounts = all_account_ids - existing_account_ids
|
|
|
|
if not missing_accounts:
|
|
return {
|
|
"installed_count": 0,
|
|
"failed_count": 0,
|
|
"details": ["All users already have Suna agents"]
|
|
}
|
|
|
|
logger.debug(f"📦 Installing Suna for {len(missing_accounts)} users")
|
|
|
|
success_count = 0
|
|
failed_count = 0
|
|
errors = []
|
|
|
|
for account_id in missing_accounts:
|
|
try:
|
|
await self._create_suna_agent_for_user(account_id)
|
|
success_count += 1
|
|
logger.debug(f"✅ Installed Suna for user {account_id}")
|
|
except Exception as e:
|
|
failed_count += 1
|
|
error_msg = f"Failed to install for user {account_id}: {str(e)}"
|
|
errors.append(error_msg)
|
|
logger.error(error_msg)
|
|
|
|
return {
|
|
"installed_count": success_count,
|
|
"failed_count": failed_count,
|
|
"details": errors if errors else [f"Successfully installed for {success_count} users"]
|
|
}
|
|
|
|
except Exception as e:
|
|
error_msg = f"Installation operation failed: {str(e)}"
|
|
logger.error(error_msg)
|
|
return {
|
|
"installed_count": 0,
|
|
"failed_count": 0,
|
|
"details": [error_msg]
|
|
}
|
|
|
|
async def install_suna_agent_for_user(self, account_id: str, replace_existing: bool = False) -> Optional[str]:
|
|
"""Install Suna agent for a specific user."""
|
|
logger.debug(f"🔄 Installing Suna agent for user: {account_id}")
|
|
|
|
try:
|
|
client = await self._db.client
|
|
|
|
# Check for existing Suna agent
|
|
existing_result = await client.table('agents').select('agent_id').eq('account_id', account_id).eq('metadata->>is_suna_default', 'true').execute()
|
|
|
|
if existing_result.data:
|
|
existing_agent_id = existing_result.data[0]['agent_id']
|
|
|
|
if replace_existing:
|
|
# Delete existing agent
|
|
await self._delete_agent(existing_agent_id)
|
|
logger.debug(f"Deleted existing Suna agent for replacement")
|
|
else:
|
|
logger.debug(f"User {account_id} already has Suna agent: {existing_agent_id}")
|
|
return existing_agent_id
|
|
|
|
# Create new agent
|
|
agent_id = await self._create_suna_agent_for_user(account_id)
|
|
logger.debug(f"Successfully installed Suna agent {agent_id} for user {account_id}")
|
|
return agent_id
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error in install_suna_agent_for_user: {e}")
|
|
return None
|
|
|
|
async def get_suna_agent_stats(self) -> Dict[str, Any]:
|
|
"""Get statistics about Suna agents."""
|
|
try:
|
|
client = await self._db.client
|
|
|
|
# Get total count
|
|
total_result = await client.table('agents').select('agent_id', count='exact').eq('metadata->>is_suna_default', 'true').execute()
|
|
total_count = total_result.count or 0
|
|
|
|
# Get creation dates for last 30 days
|
|
from datetime import timedelta
|
|
thirty_days_ago = (datetime.now(timezone.utc) - timedelta(days=30)).isoformat()
|
|
recent_result = await client.table('agents').select('created_at').eq('metadata->>is_suna_default', 'true').gte('created_at', thirty_days_ago).execute()
|
|
recent_count = len(recent_result.data) if recent_result.data else 0
|
|
|
|
return {
|
|
"total_agents": total_count,
|
|
"recent_installs": recent_count,
|
|
"note": "Suna agents always use current central configuration"
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to get agent stats: {e}")
|
|
return {"error": str(e)}
|
|
|
|
async def _create_suna_agent_for_user(self, account_id: str) -> str:
|
|
"""Create a Suna agent for a user."""
|
|
from agent.suna_config import SUNA_CONFIG
|
|
|
|
client = await self._db.client
|
|
|
|
# Create agent record
|
|
agent_data = {
|
|
"account_id": account_id,
|
|
"name": SUNA_CONFIG["name"],
|
|
"description": SUNA_CONFIG["description"],
|
|
"is_default": True,
|
|
"avatar": SUNA_CONFIG["avatar"],
|
|
"avatar_color": SUNA_CONFIG["avatar_color"],
|
|
"metadata": {
|
|
"is_suna_default": True,
|
|
"centrally_managed": True,
|
|
"installation_date": datetime.now(timezone.utc).isoformat()
|
|
},
|
|
"version_count": 1
|
|
}
|
|
|
|
result = await client.table('agents').insert(agent_data).execute()
|
|
|
|
if not result.data:
|
|
raise Exception("Failed to create agent record")
|
|
|
|
agent_id = result.data[0]['agent_id']
|
|
|
|
# Create initial version
|
|
await self._create_initial_version(agent_id, account_id)
|
|
|
|
return agent_id
|
|
|
|
async def _create_initial_version(self, agent_id: str, account_id: str) -> None:
|
|
"""Create initial version for Suna agent."""
|
|
try:
|
|
from agent.versioning.version_service import get_version_service
|
|
from agent.suna_config import SUNA_CONFIG
|
|
|
|
version_service = await get_version_service()
|
|
await version_service.create_version(
|
|
agent_id=agent_id,
|
|
user_id=account_id,
|
|
system_prompt=SUNA_CONFIG["system_prompt"],
|
|
configured_mcps=SUNA_CONFIG["configured_mcps"],
|
|
custom_mcps=SUNA_CONFIG["custom_mcps"],
|
|
agentpress_tools=SUNA_CONFIG["agentpress_tools"],
|
|
model=SUNA_CONFIG["model"],
|
|
version_name="v1",
|
|
change_description="Initial Suna agent installation"
|
|
)
|
|
|
|
logger.debug(f"Created initial version for Suna agent {agent_id}")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to create initial version for Suna agent {agent_id}: {e}")
|
|
raise
|
|
|
|
async def _delete_agent(self, agent_id: str) -> bool:
|
|
"""Delete an agent and clean up related data."""
|
|
try:
|
|
client = await self._db.client
|
|
|
|
# Clean up triggers first
|
|
try:
|
|
from triggers.trigger_service import get_trigger_service
|
|
trigger_service = get_trigger_service(self._db)
|
|
|
|
triggers_result = await client.table('agent_triggers').select('trigger_id').eq('agent_id', agent_id).execute()
|
|
|
|
if triggers_result.data:
|
|
for trigger_record in triggers_result.data:
|
|
try:
|
|
await trigger_service.delete_trigger(trigger_record['trigger_id'])
|
|
except Exception as e:
|
|
logger.warning(f"Failed to clean up trigger: {str(e)}")
|
|
except Exception as e:
|
|
logger.warning(f"Failed to clean up triggers for agent {agent_id}: {str(e)}")
|
|
|
|
# Delete agent
|
|
result = await client.table('agents').delete().eq('agent_id', agent_id).execute()
|
|
return bool(result.data)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to delete agent {agent_id}: {e}")
|
|
raise
|
|
|
|
# Legacy methods for backward compatibility
|
|
async def sync_all_suna_agents(self) -> Dict[str, Any]:
|
|
"""Legacy method - no longer needed as config is always current."""
|
|
logger.warning("sync_all_suna_agents is deprecated - Suna config is always current")
|
|
return {
|
|
"updated_count": 0,
|
|
"failed_count": 0,
|
|
"details": ["Sync not needed - Suna agents always use current config"]
|
|
}
|
|
|
|
async def update_all_suna_agents(self, target_version: Optional[str] = None) -> Dict[str, Any]:
|
|
"""Legacy method - no longer needed as config is always current."""
|
|
logger.warning("update_all_suna_agents is deprecated - Suna config is always current")
|
|
return await self.sync_all_suna_agents() |