171 lines · 6.1 KB · TypeScript
RawDownload
import { parseToolCallsFromContent } from "./tool-call-parser"; const stripToolCallXmlBlocks = (text: string): string => {  if (!text) return "";  let cleaned = text;  cleaned = cleaned.replace(/<tool_call>[\s\S]*?<\/tool_call>/gi, "");  cleaned = cleaned.replace(/<?use_mcp[\s_]*tool>[\s\S]*?<\/use_mcp[\s_]*tool>/gi, "");  cleaned = cleaned.replace(/\n{3,}/g, "\n\n");  return cleaned.trim();}; const collapseRepeatedVisibleContent = (text: string): string => {  const trimmed = text.trim();  if (trimmed.length < 80) return text;  for (let separatorLength = 0; separatorLength <= 4; separatorLength += 1) {    const contentLength = trimmed.length - separatorLength;    if (contentLength <= 0 || contentLength % 2 !== 0) continue;    const midpoint = contentLength / 2;    const first = trimmed.slice(0, midpoint).trimEnd();    const second = trimmed.slice(midpoint + separatorLength).trimStart();    if (first.length >= 40 && first === second) return first;  }  return text;}; const extractThinkBlocks = (text: string): { cleaned: string; extracted: string[] } => {  if (!text) return { cleaned: "", extracted: [] };   const extracted: string[] = [];  const visibleParts: string[] = [];  let remaining = String(text);   const openPrefixes = ["<think", "<thinking", "<analysis"];  const closePrefixes = ["</think", "</thinking", "</analysis"];   const findNextTag = (lower: string): { kind: "open" | "close"; index: number } | null => {    let openIndex = -1;    for (const prefix of openPrefixes) {      const index = lower.indexOf(prefix);      if (index >= 0) openIndex = openIndex === -1 ? index : Math.min(openIndex, index);    }    let closeIndex = -1;    for (const prefix of closePrefixes) {      const index = lower.indexOf(prefix);      if (index >= 0) closeIndex = closeIndex === -1 ? index : Math.min(closeIndex, index);    }    if (openIndex === -1 && closeIndex === -1) return null;    if (openIndex !== -1 && (closeIndex === -1 || openIndex < closeIndex))      return { kind: "open", index: openIndex };    return { kind: "close", index: closeIndex };  };   const parseTag = (    input: string,    start: number  ): { name: "think" | "thinking" | "analysis"; end: number } | null => {    const closeIndex = input.indexOf(">", start);    if (closeIndex < 0) return null;    const tag = input.slice(start, closeIndex + 1);    const open = tag.match(/^<(think|thinking|analysis)(?:\s+[^>]*)?>$/i);    if (open)      return {        name: open[1]!.toLowerCase() as "think" | "thinking" | "analysis",        end: closeIndex + 1,      };    const close = tag.match(/^<\/(think|thinking|analysis)(?:\s+[^>]*)?>$/i);    if (close)      return {        name: close[1]!.toLowerCase() as "think" | "thinking" | "analysis",        end: closeIndex + 1,      };    return null;  };   while (remaining) {    const lower = remaining.toLowerCase();    const next = findNextTag(lower);    if (!next) {      visibleParts.push(remaining);      break;    }     if (next.kind === "open") {      if (next.index > 0) visibleParts.push(remaining.slice(0, next.index));       const openTag = parseTag(remaining, next.index);      if (!openTag) {        visibleParts.push(remaining.slice(0, next.index + 1));        remaining = remaining.slice(next.index + 1);        continue;      }       remaining = remaining.slice(openTag.end);      const lowerAfter = remaining.toLowerCase();      const closeStart = lowerAfter.indexOf(`</${openTag.name}`);      if (closeStart < 0) {        const value = remaining.trim();        if (value) extracted.push(value);        remaining = "";        break;      }       const inner = remaining.slice(0, closeStart);      const value = inner.trim();      if (value) extracted.push(value);       const closeTag = parseTag(remaining, closeStart);      if (!closeTag) {        remaining = remaining.slice(closeStart + 1);        continue;      }      remaining = remaining.slice(closeTag.end);      continue;    }     if (next.index > 0) {      const value = remaining.slice(0, next.index).trim();      if (value) extracted.push(value);    }    const closeTag = parseTag(remaining, next.index);    remaining = closeTag ? remaining.slice(closeTag.end) : remaining.slice(next.index + 1);  }   return { cleaned: visibleParts.join("").trim(), extracted };}; export const normalizeReasoningAndContentInMessage = (message: Record<string, unknown>): void => {  const contentRaw = typeof message["content"] === "string" ? String(message["content"]) : "";  const reasoningRaw =    typeof message["reasoning_content"] === "string" ? String(message["reasoning_content"]) : "";   const contentThink = extractThinkBlocks(contentRaw);  const reasoningThink = extractThinkBlocks(reasoningRaw);  const extracted = [...contentThink.extracted, ...reasoningThink.extracted].filter(Boolean);   const nextReasoning = [reasoningThink.cleaned, extracted.join("\n")]    .filter((v) => v.trim().length > 0)    .join("\n");  const nextContent = contentThink.cleaned;   if (nextContent !== contentRaw) message["content"] = nextContent;  if (nextReasoning !== reasoningRaw) message["reasoning_content"] = nextReasoning;   const strippedContent = stripToolCallXmlBlocks(    typeof message["content"] === "string" ? String(message["content"]) : ""  );  const strippedReasoning = stripToolCallXmlBlocks(    typeof message["reasoning_content"] === "string" ? String(message["reasoning_content"]) : ""  );  message["content"] = collapseRepeatedVisibleContent(strippedContent);  if (strippedReasoning) {    message["reasoning_content"] = strippedReasoning;  } else {    delete message["reasoning_content"];  }}; export const normalizeToolCallsInMessage = (message: Record<string, unknown>): boolean => {  const existing = message["tool_calls"];  const hasToolCalls = Array.isArray(existing) && existing.length > 0;  if (hasToolCalls) {    return false;  }  const content = typeof message["content"] === "string" ? String(message["content"]) : "";  const parsed = parseToolCallsFromContent(content);  if (parsed.length > 0) {    message["tool_calls"] = parsed;    return true;  }  return false;};