""" Neural Sync - Event Bus System 实现事件驱动架构的核心组件,支持异步事件发布/订阅模式。 """ from enum import Enum from dataclasses import dataclass, field from datetime import datetime from typing import Callable, Dict, List, Any, Optional import asyncio import logging from contextlib import asynccontextmanager logger = logging.getLogger(__name__) class EventType(Enum): """系统事件类型定义""" # 信号事件 SIGNAL_INGESTED = "signal.ingested" SIGNAL_ANALYZED = "signal.analyzed" SIGNAL_FEEDBACK = "signal.feedback" SIGNAL_PRIORITY_CHANGED = "signal.priority_changed" # 任务事件 TASK_ASSIGNED = "task.assigned" TASK_STARTED = "task.started" TASK_COMPLETED = "task.completed" TASK_FAILED = "task.failed" # 代码事件 CODE_COMMIT = "code.commit" CODE_REVIEW_REQUESTED = "code.review_requested" CODE_DRAFT_GENERATED = "code.draft_generated" # 审计事件 AUDIT_STARTED = "audit.started" AUDIT_COMPLETED = "audit.completed" AUDIT_FAILED = "audit.failed" # 合并事件 AUTO_MERGE_REQUESTED = "auto_merge.requested" AUTO_MERGE_SUCCESS = "auto_merge.success" AUTO_MERGE_FAILED = "auto_merge.failed" AUTO_MERGE_COOLDOWN = "auto_merge.cooldown" # 集成事件 PLANE_SYNC_SUCCESS = "integration.plane.success" PLANE_SYNC_FAILED = "integration.plane.failed" LARK_NOTIFY_SUCCESS = "integration.lark.success" LARK_NOTIFY_FAILED = "integration.lark.failed" # 项目域事件 DOMAIN_CREATED = "domain.created" DOMAIN_UPDATED = "domain.updated" DOMAIN_DELETED = "domain.deleted" DOMAIN_SWITCHED = "domain.switched" DOMAIN_SYNCING = "domain.syncing" DOMAIN_SYNCED = "domain.synced" # 项目事件 PROJECT_CREATED = "project.created" PROJECT_UPDATED = "project.updated" PROJECT_DELETED = "project.deleted" SIGNAL_LINKED = "signal.linked" SIGNAL_UNLINKED = "signal.unlinked" @dataclass class Event: """事件数据结构""" type: EventType data: Dict[str, Any] timestamp: float = field(default_factory=lambda: datetime.utcnow().timestamp()) source: Optional[str] = None correlation_id: Optional[str] = None metadata: Dict[str, Any] = field(default_factory=dict) def to_dict(self) -> Dict[str, Any]: """转换为字典格式""" return { "type": self.type.value, "data": self.data, "timestamp": self.timestamp, "source": self.source, "correlation_id": self.correlation_id, "metadata": self.metadata, } class EventBus: """ 事件总线 - 核心事件分发系统 特性: - 异步事件发布 - 多订阅者支持 - 事件过滤 - 错误处理 - 性能监控 """ def __init__(self): self._subscribers: Dict[EventType, List[Callable]] = {} self._wildcard_subscribers: List[Callable] = [] self._event_history: List[Event] = [] self._max_history = 1000 self._metrics = { "published": 0, "delivered": 0, "failed": 0, } self._lock = asyncio.Lock() self._running = False @property def metrics(self) -> Dict[str, int]: """获取事件指标""" return self._metrics.copy() @property def history(self) -> List[Dict[str, Any]]: """获取事件历史""" return [e.to_dict() for e in self._event_history] async def start(self): """启动事件总线""" self._running = True logger.info("EventBus started") async def stop(self): """停止事件总线""" self._running = False logger.info("EventBus stopped") def subscribe( self, event_type: EventType, handler: Callable, filter_func: Optional[Callable[[Event], bool]] = None, ) -> Callable: """ 订阅事件 Args: event_type: 事件类型 handler: 事件处理函数 (async def handler(event: Event) -> None) filter_func: 事件过滤函数 (def filter(event: Event) -> bool) Returns: 取消订阅的函数 """ async def wrapped_handler(event: Event): if filter_func and not filter_func(event): return try: await handler(event) self._metrics["delivered"] += 1 except Exception as e: logger.error( f"Error in event handler for {event_type.value}: {e}", exc_info=True, ) self._metrics["failed"] += 1 # 包装为原始 handler 用于取消订阅 wrapped_handler.original = handler # type: ignore async def _subscribe(): async with self._lock: if event_type not in self._subscribers: self._subscribers[event_type] = [] self._subscribers[event_type].append(wrapped_handler) asyncio.create_task(_subscribe()) def unsubscribe(): asyncio.create_task(self._unsubscribe(event_type, wrapped_handler)) return unsubscribe def subscribe_wildcard( self, handler: Callable, filter_func: Optional[Callable[[Event], bool]] = None, ) -> Callable: """订阅所有事件(通配符订阅)""" async def wrapped_handler(event: Event): if filter_func and not filter_func(event): return try: await handler(event) self._metrics["delivered"] += 1 except Exception as e: logger.error( f"Error in wildcard event handler: {e}", exc_info=True, ) self._metrics["failed"] += 1 wrapped_handler.original = handler # type: ignore async def _subscribe(): async with self._lock: self._wildcard_subscribers.append(wrapped_handler) asyncio.create_task(_subscribe()) def unsubscribe(): asyncio.create_task(self._unsubscribe_wildcard(wrapped_handler)) return unsubscribe async def _unsubscribe(self, event_type: EventType, handler: Callable): """取消订阅""" async with self._lock: if event_type in self._subscribers: self._subscribers[event_type] = [ h for h in self._subscribers[event_type] if h != handler ] async def _unsubscribe_wildcard(self, handler: Callable): """取消通配符订阅""" async with self._lock: self._wildcard_subscribers = [ h for h in self._wildcard_subscribers if h != handler ] async def publish(self, event: Event) -> bool: """ 发布事件 Args: event: 事件对象 Returns: 是否成功发布 """ if not self._running: logger.warning(f"EventBus not running, discarding event: {event.type.value}") return False self._metrics["published"] += 1 # 记录历史 self._event_history.append(event) if len(self._event_history) > self._max_history: self._event_history.pop(0) # 通知订阅者 tasks = [] # 特定事件订阅者 if event.type in self._subscribers: for handler in self._subscribers[event.type]: tasks.append(handler(event)) # 通配符订阅者 for handler in self._wildcard_subscribers: tasks.append(handler(event)) if tasks: results = await asyncio.gather(*tasks, return_exceptions=True) failed = sum(1 for r in results if isinstance(r, Exception)) if failed > 0: self._metrics["failed"] += failed logger.debug( f"Published event {event.type.value} to {len(tasks)} subscribers" ) return True async def publish_and_wait( self, event: Event, timeout: float = 5.0 ) -> List[Any]: """ 发布事件并等待所有处理完成 Args: event: 事件对象 timeout: 超时时间(秒) Returns: 所有处理器的返回值列表 """ if not self._running: raise RuntimeError("EventBus not running") self._metrics["published"] += 1 self._event_history.append(event) tasks = [] if event.type in self._subscribers: tasks.extend(self._subscribers[event.type]) for handler in self._wildcard_subscribers: tasks.append(handler(event)) if not tasks: return [] try: results = await asyncio.wait_for( asyncio.gather(*tasks, return_exceptions=True), timeout=timeout, ) return results except asyncio.TimeoutError: logger.warning( f"Event {event.type.value} processing timed out after {timeout}s" ) raise def clear_history(self): """清空事件历史""" self._event_history.clear() def get_subscriber_count(self, event_type: Optional[EventType] = None) -> int: """获取订阅者数量""" if event_type: return len(self._subscribers.get(event_type, [])) return sum(len(subs) for subs in self._subscribers.values()) + len( self._wildcard_subscribers ) # 全局事件总线实例 _global_event_bus: Optional[EventBus] = None def get_event_bus() -> EventBus: """获取全局事件总线实例""" global _global_event_bus if _global_event_bus is None: _global_event_bus = EventBus() return _global_event_bus @asynccontextmanager async def event_bus_context(): """事件总线上下文管理器""" bus = get_event_bus() await bus.start() try: yield bus finally: await bus.stop() # 便捷函数 async def publish_event(event_type: EventType, data: Dict[str, Any], **kwargs) -> bool: """发布事件的便捷函数""" event = Event(type=event_type, data=data, **kwargs) return await get_event_bus().publish(event) def subscribe_event( event_type: EventType, handler: Callable ) -> Callable: """订阅事件的便捷函数""" return get_event_bus().subscribe(event_type, handler) def subscribe_all_events(handler: Callable) -> Callable: """订阅所有事件的便捷函数""" return get_event_bus().subscribe_wildcard(handler)