"""AI Assistant plugin — multi-provider LLM chat, agents, tools.""" from __future__ import annotations import logging from typing import Any from app.plugins.base import BasePlugin from app.plugins.manifest import PluginManifest, PluginRouteDef logger = logging.getLogger(__name__) class AIAssistantPlugin(BasePlugin): """AI Assistant plugin: multi-provider LLM chat with agents and tools.""" manifest = PluginManifest( name="ai_assistant", version="1.0.0", display_name="KI Assistent", description=( "AI Assistant with multi-provider LLM support, custom agents, " "plugin tools, and streaming chat." ), dependencies=["kommunikation"], routes=[ PluginRouteDef( path="/api/v1/ai", module="app.plugins.builtins.ai_assistant.routes", router_attr="router", ), ], events=[], migrations=["0001_initial.sql", "0002_folders_attachments.sql"], permissions=[ "ai:read", "ai:write", "ai:config", "ai:agents", "ai:tools", ], is_core=True, ) def __init__(self) -> None: super().__init__() self._ai_handler = None self._msg_handler = None async def on_install(self, db, service_container) -> None: """Seed default provider and agent.""" from app.plugins.builtins.ai_assistant.services import seed_defaults await seed_defaults(db) async def on_activate(self, db, service_container, event_bus) -> None: """Activate plugin: register context tools and participant handler.""" await super().on_activate(db, service_container, event_bus) # Register as participant in the kommunikation system try: from app.plugins.builtins.ai_assistant.participant_handler import ( AIParticipantHandler, ) from app.plugins.builtins.kommunikation.participant_registry import ( get_participant_registry, ) self._ai_handler = AIParticipantHandler(service_container) get_participant_registry().register("ai", self._ai_handler) print("[STARTUP] AI Assistant registered as participant 'ai'", flush=True) logger.info("AI Assistant registered as participant 'ai'") except Exception as e: print(f"[STARTUP] Failed to register AI Assistant as participant: {e}", flush=True) logger.exception("Failed to register AI Assistant as participant") # Subscribe to message.received events try: self._msg_handler = self._on_message_received event_bus.subscribe("message.received", self._msg_handler) logger.info("AI Assistant subscribed to message.received events") except Exception: logger.exception("Failed to subscribe to message.received events") async def _on_message_received(self, payload: dict[str, Any]) -> None: """Handle message.received event by delegating to the AI participant handler.""" if self._ai_handler is None: return try: await self._ai_handler.handle_event(payload) except Exception: logger.exception("Error in AI participant handler for message.received") async def on_deactivate(self, db, service_container, event_bus) -> None: """Deactivate plugin: unregister participant and event subscriptions.""" # Unregister from participant registry try: from app.plugins.builtins.kommunikation.participant_registry import ( get_participant_registry, ) get_participant_registry().unregister("ai") logger.info("AI Assistant unregistered as participant 'ai'") except Exception: logger.exception("Failed to unregister AI Assistant as participant") # Unsubscribe from message.received events if self._msg_handler: try: event_bus.unsubscribe("message.received", self._msg_handler) except Exception: logger.exception("Failed to unsubscribe from message.received events") self._msg_handler = None self._ai_handler = None await super().on_deactivate(db, service_container, event_bus) def get_notification_types(self) -> list[dict[str, Any]]: return [ { "type_key": "ai_response_error", "category": "ai", "label": "KI Antwort-Fehler", "description": "Fehler bei der KI-Antwortgenerierung", "is_enabled_by_default": True, }, ]