""" 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 from .utils.timezone import now_cn_aware 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: now_cn_aware().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)