ForcePilot/backend/package/yuxi/channel/extensions/workplace/streaming.py

90 lines
2.9 KiB
Python
Raw Normal View History

from __future__ import annotations
import asyncio
import logging
from yuxi.channel.extensions.workplace.outbound import WorkplaceOutbound
logger = logging.getLogger(__name__)
class WorkplaceStreaming:
streaming_mode: str = "block"
block_streaming_enabled: bool = True
block_streaming_break: str = "text_end"
block_streaming_chunk_min_chars: int = 200
block_streaming_chunk_max_chars: int = 2000
block_streaming_chunk_break_preference: str = "paragraph"
block_streaming_coalesce_defaults: dict = {"min_chars": 200, "max_chars": 2000, "idle_ms": 1000}
def __init__(self, outbound: WorkplaceOutbound | None = None):
self._outbound = outbound
async def send_block_stream(
self,
target_id: str,
full_text: str,
*,
thread_id: str | None = None,
account_id: str | None = None,
) -> list[str]:
chunks = self._chunk_text(full_text)
outbound = self._outbound or WorkplaceOutbound()
message_ids: list[str] = []
for i, chunk in enumerate(chunks):
if not chunk.strip():
continue
results = await outbound.send_text(target_id, chunk, thread_id=thread_id, account_id=account_id)
for r in results:
msg_id = r.get("message_id")
if msg_id:
message_ids.append(msg_id)
if i < len(chunks) - 1:
await asyncio.sleep(0.3)
return message_ids
def create_draft_stream_session(self, **kwargs) -> object | None:
return None
def _chunk_text(self, text: str) -> list[str]:
if not text:
return [""]
if len(text) <= self.block_streaming_chunk_max_chars:
return [text]
chunks: list[str] = []
remaining = text
while remaining:
if len(remaining) <= self.block_streaming_chunk_max_chars:
chunks.append(remaining)
break
cutoff = self.block_streaming_chunk_max_chars
chunk_text = remaining[:cutoff]
para_break = chunk_text.rfind("\n\n")
if para_break > self.block_streaming_chunk_min_chars:
cutoff = para_break + 2
else:
line_break = chunk_text.rfind("\n")
if line_break > self.block_streaming_chunk_min_chars:
cutoff = line_break + 1
else:
sentence_break = max(
chunk_text.rfind(""),
chunk_text.rfind(". "),
chunk_text.rfind(""),
chunk_text.rfind(""),
)
if sentence_break > self.block_streaming_chunk_min_chars:
cutoff = sentence_break + 1
chunks.append(remaining[:cutoff].strip())
remaining = remaining[cutoff:].strip()
return chunks