from __future__ import annotations import re _CONTROL_CHAR_PATTERN = re.compile(r"[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]") _MULTI_NEWLINE_PATTERN = re.compile(r"\n{3,}") def sanitize_text(text: str) -> str: cleaned = _CONTROL_CHAR_PATTERN.sub("", text) cleaned = _MULTI_NEWLINE_PATTERN.sub("\n\n", cleaned) return cleaned.strip() def chunk_markdown_text(text: str, limit: int = 4000) -> list[str]: if len(text) <= limit: return [text] chunks = [] lines = text.split("\n") current = "" for line in lines: if len(current) + len(line) + 1 > limit: if current: chunks.append(current.strip()) current = "" if len(line) > limit: while len(line) > limit: split_at = _find_code_block_split(line, limit) chunks.append(line[:split_at].strip()) line = line[split_at:].strip() if line: current = line else: current = line else: if current: current += "\n" + line else: current = line if current.strip(): chunks.append(current.strip()) return chunks def _find_code_block_split(line: str, limit: int) -> int: in_block = False chars = 0 for i, ch in enumerate(line): if ch == "`": if line[i:i+3] == "```": in_block = not in_block if not in_block and chars >= limit: return i chars += 1 return limit def format_conversation_label(user_name: str, space_name: str) -> str: if space_name: return f"googlechat::{space_name}" return f"googlechat::{user_name}" def build_typing_indicator_text(bot_name: str) -> str: return f"_{bot_name} is typing..._" def resolve_bot_display_name(account: dict) -> str: resolved = account.get("resolved") if resolved and resolved.name: return resolved.name return account.get("name", "Bot") def format_thread_context(recent_messages: list[dict]) -> str: if not recent_messages: return "" lines = [ "[Thread conversation - previous replies. You are participating in this thread.]", "", "[Previous messages]", ] for msg in recent_messages[-10:]: author = msg.get("author", "Unknown") content = msg.get("content", "") if content: lines.append(f"{author}: {content}") lines.append("") lines.append("[Current message]") return "\n".join(lines)