suna/backend/triggers/services/trigger_service.py

213 lines
7.5 KiB
Python

import uuid
from typing import List, Optional, Dict, Any
from datetime import datetime, timezone
from ..domain.entities import Trigger, TriggerEvent, TriggerResult
from ..domain.value_objects import TriggerIdentity, TriggerConfig, TriggerMetadata, TriggerType
from ..domain.services import TriggerDomainService, ProviderRegistryService
from ..repositories.interfaces import TriggerRepository, TriggerEventLogRepository
class TriggerService:
def __init__(
self,
trigger_repository: TriggerRepository,
event_log_repository: TriggerEventLogRepository,
domain_service: TriggerDomainService,
provider_registry: ProviderRegistryService
):
self._trigger_repo = trigger_repository
self._event_log_repo = event_log_repository
self._domain_service = domain_service
self._provider_registry = provider_registry
async def create_trigger(
self,
agent_id: str,
provider_id: str,
name: str,
config: Dict[str, Any],
description: Optional[str] = None
) -> Trigger:
validated_config = await self._domain_service.validate_trigger_configuration(
provider_id, config
)
provider = await self._provider_registry.get_provider(provider_id)
if not provider:
raise ValueError(f"Provider not found: {provider_id}")
trigger_config = TriggerConfig(
name=name,
description=description,
config={**validated_config, "provider_id": provider_id}
)
identity = TriggerIdentity(
trigger_id=str(uuid.uuid4()),
agent_id=agent_id
)
trigger = Trigger(
identity=identity,
provider_id=provider_id,
trigger_type=provider.trigger_type,
config=trigger_config
)
setup_success = await self._domain_service.setup_trigger(trigger)
if not setup_success:
raise ValueError(f"Failed to setup trigger: {name}")
await self._trigger_repo.save(trigger)
return trigger
async def get_trigger(self, trigger_id: str) -> Optional[Trigger]:
return await self._trigger_repo.find_by_id(trigger_id)
async def get_agent_triggers(self, agent_id: str) -> List[Trigger]:
return await self._trigger_repo.find_by_agent_id(agent_id)
async def get_active_triggers(self) -> List[Trigger]:
return await self._trigger_repo.find_active_triggers()
async def update_trigger(
self,
trigger_id: str,
config: Optional[Dict[str, Any]] = None,
name: Optional[str] = None,
description: Optional[str] = None,
is_active: Optional[bool] = None
) -> Trigger:
trigger = await self._trigger_repo.find_by_id(trigger_id)
if not trigger:
raise ValueError(f"Trigger not found: {trigger_id}")
if config is not None:
validated_config = await self._domain_service.validate_trigger_configuration(
trigger.provider_id, config
)
config = {**validated_config, "provider_id": trigger.provider_id}
updated_config = TriggerConfig(
name=name if name is not None else trigger.config.name,
description=description if description is not None else trigger.config.description,
config=config if config is not None else trigger.config.config,
is_active=is_active if is_active is not None else trigger.config.is_active
)
trigger.update_config(updated_config)
await self._domain_service.teardown_trigger(trigger)
if trigger.is_active:
setup_success = await self._domain_service.setup_trigger(trigger)
if not setup_success:
raise ValueError(f"Failed to update trigger setup: {trigger_id}")
await self._trigger_repo.update(trigger)
return trigger
async def delete_trigger(self, trigger_id: str) -> bool:
trigger = await self._trigger_repo.find_by_id(trigger_id)
if not trigger:
return False
await self._domain_service.teardown_trigger(trigger)
return await self._trigger_repo.delete(trigger_id)
async def activate_trigger(self, trigger_id: str) -> Trigger:
trigger = await self._trigger_repo.find_by_id(trigger_id)
if not trigger:
raise ValueError(f"Trigger not found: {trigger_id}")
if not trigger.is_active:
trigger.activate()
setup_success = await self._domain_service.setup_trigger(trigger)
if not setup_success:
raise ValueError(f"Failed to activate trigger: {trigger_id}")
await self._trigger_repo.update(trigger)
return trigger
async def deactivate_trigger(self, trigger_id: str) -> Trigger:
trigger = await self._trigger_repo.find_by_id(trigger_id)
if not trigger:
raise ValueError(f"Trigger not found: {trigger_id}")
if trigger.is_active:
await self._domain_service.teardown_trigger(trigger)
trigger.deactivate()
await self._trigger_repo.update(trigger)
return trigger
async def health_check_trigger(self, trigger_id: str) -> bool:
trigger = await self._trigger_repo.find_by_id(trigger_id)
if not trigger:
return False
return await self._domain_service.health_check_trigger(trigger)
async def health_check_agent_triggers(self, agent_id: str) -> Dict[str, bool]:
triggers = await self._trigger_repo.find_by_agent_id(agent_id)
results = {}
for trigger in triggers:
results[trigger.trigger_id] = await self._domain_service.health_check_trigger(trigger)
return results
async def process_trigger_event(
self,
trigger_id: str,
raw_data: Dict[str, Any]
) -> TriggerResult:
trigger = await self._trigger_repo.find_by_id(trigger_id)
if not trigger:
return TriggerResult(
success=False,
error_message=f"Trigger not found: {trigger_id}"
)
event = TriggerEvent(
trigger_id=trigger_id,
agent_id=trigger.agent_id,
trigger_type=trigger.trigger_type,
raw_data=raw_data
)
result = await self._domain_service.process_trigger_event(trigger, event)
return result
async def get_trigger_logs(
self,
trigger_id: str,
limit: int = 100,
offset: int = 0
) -> List[Dict[str, Any]]:
return await self._event_log_repo.find_logs_by_trigger_id(trigger_id, limit, offset)
async def get_agent_trigger_logs(
self,
agent_id: str,
limit: int = 100,
offset: int = 0
) -> List[Dict[str, Any]]:
return await self._event_log_repo.find_logs_by_agent_id(agent_id, limit, offset)
async def get_trigger_stats(
self,
trigger_id: str,
hours: int = 24
) -> Dict[str, Any]:
return await self._event_log_repo.get_execution_stats(trigger_id, hours)
async def get_failed_events(
self,
since: Optional[datetime] = None,
limit: int = 100
) -> List[Dict[str, Any]]:
return await self._event_log_repo.find_failed_events(since, limit)