""" Agent Event Handler - Handles agent events and thinking process output """ from common import const from common.log import logger # Cap intermediate thinking messages on weixin to stay within send quota. WEIXIN_THINKING_INSTANT_MAX = 7 class AgentEventHandler: """ Handles agent events and optionally sends intermediate messages to channel """ def __init__(self, context=None, original_callback=None): self.context = context self.original_callback = original_callback self.channel = None if context: self.channel = context.kwargs.get("channel") if hasattr(context, "kwargs") else None self.current_content = "" self.turn_number = 0 channel_type = "" if context and hasattr(context, "kwargs"): channel_type = context.kwargs.get("channel_type", "") or "" self._is_weixin = channel_type == const.WEIXIN self._thinking_sent_count = 0 self._merged_buf: list[str] = [] def handle_event(self, event): event_type = event.get("type") data = event.get("data", {}) if event_type == "turn_start": self._handle_turn_start(data) elif event_type == "message_update": self._handle_message_update(data) elif event_type == "message_end": self._handle_message_end(data) elif event_type == "reasoning_update": pass elif event_type == "tool_execution_start": self._handle_tool_execution_start(data) elif event_type == "tool_execution_end": self._handle_tool_execution_end(data) elif event_type == "agent_end": self._handle_agent_end(data) if self.original_callback: self.original_callback(event) def _handle_turn_start(self, data): self.turn_number = data.get("turn", 0) self.current_content = "" def _handle_message_update(self, data): delta = data.get("delta", "") self.current_content += delta def _handle_message_end(self, data): tool_calls = data.get("tool_calls", []) if tool_calls: if self.current_content.strip(): logger.info(f"💭 {self.current_content.strip()[:200]}{'...' if len(self.current_content) > 200 else ''}") self._send_to_channel(self.current_content.strip()) else: if self.current_content.strip(): logger.debug(f"💬 {self.current_content.strip()[:200]}{'...' if len(self.current_content) > 200 else ''}") # Drain weixin buffer before final reply leaves chat_channel self._flush_merged_now() self.current_content = "" def _handle_agent_end(self, data): self._flush_merged_now() def _handle_tool_execution_start(self, data): pass def _handle_tool_execution_end(self, data): pass def _send_to_channel(self, message): if self.context and self.context.get("on_event"): return if not self.channel: return if not self._is_weixin: self._do_send(message) return if self._thinking_sent_count < WEIXIN_THINKING_INSTANT_MAX: self._do_send(message) self._thinking_sent_count += 1 return self._merged_buf.append(message) def _flush_merged_now(self): if not self._merged_buf: return merged = "\n\n".join(self._merged_buf) count = len(self._merged_buf) self._merged_buf = [] logger.debug(f"[AgentEventHandler] Flushing {count} merged thinking msgs, len={len(merged)}") self._do_send(merged) self._thinking_sent_count += 1 def _do_send(self, message): try: from bridge.reply import Reply, ReplyType reply = Reply(ReplyType.TEXT, message) self.channel._send(reply, self.context) except Exception as e: logger.debug(f"[AgentEventHandler] Failed to send to channel: {e}") def log_summary(self): pass