ForcePilot/backend/package/yuxi/channel/extensions/workplace/streaming.py
Kris 7215418610 feat(channel): 添加企业微信、微博、WhatsApp 和 Workplace 渠道扩展
新增企业微信、微博、WhatsApp、Workplace 四个渠道扩展。

企业微信渠道扩展主要模块:config, gateway, webhook, webhook_bot, outbound, streaming, pairing, security, crypto, dedupe, persistent_dedupe, card, directory, events, externalcontact, media, mentions, menu, message, oauth, status

微博渠道扩展主要模块:config, gateway, webhook, outbound, streaming, pairing, security, dedupe, passive_reply, broadcast, message, menu, media, subscription, template, status

WhatsApp 渠道扩展主要模块:config, gateway, webhook, outbound, streaming, pairing, security, dedupe, actions, monitor, status

Workplace 渠道扩展主要模块:config, gateway, webhook, outbound, streaming, pairing, security, dedupe, actions, challenge, groups, media, mentions, menu, monitor, persona, quick_reply, signature, subscriptions, template, threading, users, status
2026-05-21 12:01:56 +08:00

90 lines
2.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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