export function extractTextFromChatContent( content: unknown, opts?: { sanitizeText?: (text: string) => string; joinWith?: string; normalizeText?: (text: string) => string; }, ): string | null { const normalizeText = opts?.normalizeText ?? ((text: string) => text.replace(/\s+/g, " ").trim()); const joinWith = opts?.joinWith ?? " "; const coerceText = (value: unknown): string => { if (typeof value === "string") { return value; } if (value == null) { return ""; } if ( typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || typeof value === "symbol" ) { return String(value); } if (typeof value === "object") { try { return JSON.stringify(value) ?? ""; } catch { return ""; } } return ""; }; const sanitize = (text: unknown): string => { const raw = coerceText(text); const sanitized = opts?.sanitizeText ? opts.sanitizeText(raw) : raw; return coerceText(sanitized); }; const normalize = (text: unknown): string => coerceText(normalizeText(coerceText(text))); if (typeof content === "string") { const value = sanitize(content); const normalized = normalize(value); return normalized ? normalized : null; } if (!Array.isArray(content)) { return null; } const chunks: string[] = []; for (const block of content) { if (!block || typeof block !== "object") { continue; } if ((block as { type?: unknown }).type !== "text") { continue; } const text = (block as { text?: unknown }).text; const value = sanitize(text); if (value.trim()) { chunks.push(value); } } const joined = normalize(chunks.join(joinWith)); return joined ? joined : null; }