from __future__ import annotations import logging import time from yuxi.channel.protocols import StreamingProtocol logger = logging.getLogger(__name__) class JiraStreaming(StreamingProtocol): streaming_mode = "block" preview_stream_throttle_ms = 160 preview_min_initial_chars = 18 block_streaming_enabled = True block_streaming_break = "paragraph" block_streaming_chunk_min_chars = 400 block_streaming_chunk_max_chars = 4000 block_streaming_chunk_break_preference = "paragraph" block_streaming_coalesce_defaults = {"min_chars": 200, "max_chars": 2000} block_streaming_update_interval_sec = 3.0 def __init__(self, outbound=None): self._outbound = outbound def create_draft_stream_session(self, target_id: str) -> dict: return { "target_id": target_id, "mode": "block", "chunks_sent": 0, "placeholder_comment_id": None, } def create_block_chunker(self) -> object: return JiraBlockChunkerConfig( min_chars=self.block_streaming_chunk_min_chars, max_chars=self.block_streaming_chunk_max_chars, mode="interval", interval_sec=self.block_streaming_update_interval_sec, ) def create_block_chunker_instance( self, issue_key: str, account: dict, outbound=None, ) -> JiraBlockChunker: ob = outbound or self._outbound return JiraBlockChunker( outbound=ob, issue_key=issue_key, account=account, update_interval_sec=self.block_streaming_update_interval_sec, ) class JiraBlockChunkerConfig: def __init__(self, min_chars: int, max_chars: int, mode: str = "interval", interval_sec: float = 3.0): self.min_chars = min_chars self.max_chars = max_chars self.mode = mode self.interval_sec = interval_sec class JiraBlockChunker: def __init__( self, outbound, issue_key: str, account: dict, update_interval_sec: float = 3.0, max_session_sec: float = 600.0, ): self._outbound = outbound self._issue_key = issue_key self._account = account self._update_interval_sec = update_interval_sec self._max_session_sec = max_session_sec self._buffer = "" self._message_id: str | None = None self._last_update = 0.0 self._start_time = 0.0 self._finished = False async def start(self): result = await self._outbound.send_text( self._issue_key, "🤖 AI 正在分析中...", account_id=self._account.get("account_id"), ) if result and result.get("success"): self._message_id = result["msg_id"] self._start_time = time.monotonic() self._last_update = self._start_time async def append(self, chunk: str) -> None: self._buffer += chunk now = time.monotonic() if now - self._last_update < self._update_interval_sec: return if now - self._start_time > self._max_session_sec: await self.finalize() return if self._message_id: await self._outbound.edit_message( self._issue_key, self._message_id, self._buffer + "\n\n⏳ *分析进行中...*", account_id=self._account.get("account_id"), ) self._last_update = now async def finalize(self) -> None: if self._finished: return self._finished = True if self._message_id and self._buffer: await self._outbound.edit_message( self._issue_key, self._message_id, self._buffer, account_id=self._account.get("account_id"), )