from __future__ import annotations from .outbound import EmailSmtpOutboundAdapter class EmailSmtpStreamingAdapter: streaming_mode = "block" preview_stream_throttle_ms = 160 preview_min_initial_chars = 18 block_streaming_enabled = True block_streaming_break = "text_end" block_streaming_chunk_min_chars = 800 block_streaming_chunk_max_chars = 1200 block_streaming_chunk_break_preference = "paragraph" block_streaming_coalesce_defaults = None def __init__(self, outbound: EmailSmtpOutboundAdapter | None = None): self._outbound = outbound or EmailSmtpOutboundAdapter() self._idle_ms = 800 def create_block_chunker(self) -> BlockChunker: return BlockChunker( min_chars=self.block_streaming_chunk_min_chars, max_chars=self.block_streaming_chunk_max_chars, break_preference=self.block_streaming_chunk_break_preference, ) def create_draft_stream_session(self, target_id: str) -> dict: return { "target_id": target_id, "accumulated": "", "chunks_sent": 0, } class BlockChunker: def __init__(self, min_chars: int = 800, max_chars: int = 1200, break_preference: str = "paragraph"): self._min_chars = min_chars self._max_chars = max_chars self._break_preference = break_preference def chunk(self, text: str) -> list[str]: if len(text) <= self._max_chars: return [text] if text else [] chunks = [] remaining = text while len(remaining) > self._max_chars: split_at = self._max_chars if self._break_preference == "paragraph": para_break = remaining.rfind("\n\n", 0, self._max_chars) if para_break > self._min_chars: split_at = para_break + 2 else: line_break = remaining.rfind("\n", 0, self._max_chars) if line_break > self._min_chars: split_at = line_break + 1 chunks.append(remaining[:split_at].strip()) remaining = remaining[split_at:].strip() if remaining: chunks.append(remaining) return chunks