Initial commit: breakpilot-lehrer - Lehrer KI Platform
Services: Admin-Lehrer, Backend-Lehrer, Studio v2, Website, Klausur-Service, School-Service, Voice-Service, Geo-Service, BreakPilot Drive, Agent-Core Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
232
backend-lehrer/alerts_agent/actions/dispatcher.py
Normal file
232
backend-lehrer/alerts_agent/actions/dispatcher.py
Normal file
@@ -0,0 +1,232 @@
|
||||
"""
|
||||
Action Dispatcher für Alerts Agent.
|
||||
|
||||
Verteilt Aktionen an die entsprechenden Handler.
|
||||
"""
|
||||
import logging
|
||||
from typing import Dict, Any, List, Optional
|
||||
from datetime import datetime
|
||||
|
||||
from .base import ActionHandler, ActionResult, ActionType, AlertContext
|
||||
from .email_action import EmailAction
|
||||
from .webhook_action import WebhookAction
|
||||
from .slack_action import SlackAction
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ActionDispatcher:
|
||||
"""
|
||||
Zentrale Verteilung von Aktionen an Handler.
|
||||
|
||||
Registriert Handler für verschiedene Aktionstypen und
|
||||
führt Aktionen basierend auf Regel-Konfigurationen aus.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialisiert den Dispatcher mit Standard-Handlern."""
|
||||
self._handlers: Dict[ActionType, ActionHandler] = {}
|
||||
|
||||
# Standard-Handler registrieren
|
||||
self.register_handler(EmailAction())
|
||||
self.register_handler(WebhookAction())
|
||||
self.register_handler(SlackAction())
|
||||
|
||||
def register_handler(self, handler: ActionHandler) -> None:
|
||||
"""
|
||||
Registriert einen Action-Handler.
|
||||
|
||||
Args:
|
||||
handler: Handler-Instanz
|
||||
"""
|
||||
self._handlers[handler.action_type] = handler
|
||||
logger.debug(f"Registered action handler: {handler.action_type.value}")
|
||||
|
||||
def get_handler(self, action_type: ActionType) -> Optional[ActionHandler]:
|
||||
"""
|
||||
Gibt den Handler für einen Aktionstyp zurück.
|
||||
|
||||
Args:
|
||||
action_type: Aktionstyp
|
||||
|
||||
Returns:
|
||||
Handler oder None
|
||||
"""
|
||||
return self._handlers.get(action_type)
|
||||
|
||||
def list_handlers(self) -> List[str]:
|
||||
"""Gibt Liste der registrierten Handler zurück."""
|
||||
return [at.value for at in self._handlers.keys()]
|
||||
|
||||
async def dispatch(
|
||||
self,
|
||||
action_type: str,
|
||||
context: AlertContext,
|
||||
config: Dict[str, Any],
|
||||
) -> ActionResult:
|
||||
"""
|
||||
Führt eine Aktion aus.
|
||||
|
||||
Args:
|
||||
action_type: Aktionstyp als String (email, webhook, slack)
|
||||
context: Alert-Kontext
|
||||
config: Aktionsspezifische Konfiguration
|
||||
|
||||
Returns:
|
||||
ActionResult
|
||||
"""
|
||||
try:
|
||||
# ActionType aus String
|
||||
at = ActionType(action_type.lower())
|
||||
except ValueError:
|
||||
return ActionResult(
|
||||
success=False,
|
||||
action_type=ActionType.WEBHOOK, # Fallback
|
||||
message=f"Unbekannter Aktionstyp: {action_type}",
|
||||
error="Unknown action type",
|
||||
)
|
||||
|
||||
handler = self.get_handler(at)
|
||||
|
||||
if not handler:
|
||||
return ActionResult(
|
||||
success=False,
|
||||
action_type=at,
|
||||
message=f"Kein Handler für {action_type} registriert",
|
||||
error="No handler registered",
|
||||
)
|
||||
|
||||
# Konfiguration validieren
|
||||
if not handler.validate_config(config):
|
||||
required = handler.get_required_config_fields()
|
||||
return ActionResult(
|
||||
success=False,
|
||||
action_type=at,
|
||||
message=f"Ungültige Konfiguration für {action_type}",
|
||||
error=f"Required fields: {required}",
|
||||
)
|
||||
|
||||
# Aktion ausführen
|
||||
logger.info(f"Dispatching {action_type} action for alert {context.alert_id[:8]}")
|
||||
result = await handler.execute(context, config)
|
||||
|
||||
return result
|
||||
|
||||
async def dispatch_multiple(
|
||||
self,
|
||||
actions: List[Dict[str, Any]],
|
||||
context: AlertContext,
|
||||
) -> List[ActionResult]:
|
||||
"""
|
||||
Führt mehrere Aktionen aus.
|
||||
|
||||
Args:
|
||||
actions: Liste von Aktionen [{type, config}, ...]
|
||||
context: Alert-Kontext
|
||||
|
||||
Returns:
|
||||
Liste von ActionResults
|
||||
"""
|
||||
results = []
|
||||
|
||||
for action in actions:
|
||||
action_type = action.get("type", action.get("action_type", ""))
|
||||
config = action.get("config", action.get("action_config", {}))
|
||||
|
||||
result = await self.dispatch(action_type, context, config)
|
||||
results.append(result)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
# Singleton-Instanz
|
||||
_dispatcher: Optional[ActionDispatcher] = None
|
||||
|
||||
|
||||
def get_dispatcher() -> ActionDispatcher:
|
||||
"""Gibt den globalen ActionDispatcher zurück."""
|
||||
global _dispatcher
|
||||
if _dispatcher is None:
|
||||
_dispatcher = ActionDispatcher()
|
||||
return _dispatcher
|
||||
|
||||
|
||||
async def execute_action(
|
||||
action_type: str,
|
||||
alert_id: str,
|
||||
title: str,
|
||||
url: str,
|
||||
snippet: str,
|
||||
topic_name: str,
|
||||
config: Dict[str, Any],
|
||||
relevance_score: Optional[float] = None,
|
||||
relevance_decision: Optional[str] = None,
|
||||
matched_rule: Optional[str] = None,
|
||||
tags: Optional[List[str]] = None,
|
||||
) -> ActionResult:
|
||||
"""
|
||||
Convenience-Funktion zum Ausführen einer Aktion.
|
||||
|
||||
Erstellt den Kontext und ruft den Dispatcher auf.
|
||||
"""
|
||||
context = AlertContext(
|
||||
alert_id=alert_id,
|
||||
title=title,
|
||||
url=url,
|
||||
snippet=snippet,
|
||||
topic_name=topic_name,
|
||||
relevance_score=relevance_score,
|
||||
relevance_decision=relevance_decision,
|
||||
matched_rule=matched_rule,
|
||||
tags=tags or [],
|
||||
)
|
||||
|
||||
dispatcher = get_dispatcher()
|
||||
return await dispatcher.dispatch(action_type, context, config)
|
||||
|
||||
|
||||
async def execute_rule_actions(
|
||||
alert_id: str,
|
||||
title: str,
|
||||
url: str,
|
||||
snippet: str,
|
||||
topic_name: str,
|
||||
rule_action: str,
|
||||
rule_config: Dict[str, Any],
|
||||
rule_name: str,
|
||||
) -> ActionResult:
|
||||
"""
|
||||
Führt die Aktion einer gematschten Regel aus.
|
||||
|
||||
Args:
|
||||
alert_id: Alert-ID
|
||||
title: Alert-Titel
|
||||
url: Alert-URL
|
||||
snippet: Alert-Snippet
|
||||
topic_name: Topic-Name
|
||||
rule_action: Aktionstyp der Regel
|
||||
rule_config: Aktions-Konfiguration
|
||||
rule_name: Name der Regel
|
||||
|
||||
Returns:
|
||||
ActionResult
|
||||
"""
|
||||
# Nur externe Aktionen (email, webhook, slack) hier behandeln
|
||||
# keep/drop/tag werden direkt von der Rule Engine behandelt
|
||||
if rule_action not in ["email", "webhook", "slack"]:
|
||||
return ActionResult(
|
||||
success=True,
|
||||
action_type=ActionType.TAG, # Dummy
|
||||
message=f"Interne Aktion {rule_action} von Rule Engine behandelt",
|
||||
)
|
||||
|
||||
return await execute_action(
|
||||
action_type=rule_action,
|
||||
alert_id=alert_id,
|
||||
title=title,
|
||||
url=url,
|
||||
snippet=snippet,
|
||||
topic_name=topic_name,
|
||||
config=rule_config,
|
||||
matched_rule=rule_name,
|
||||
)
|
||||
Reference in New Issue
Block a user