


























@@ -245,6 +245,25 @@ function findMatchingToolCallCloseIndex(text: string, start: number, tagName: st
245245return -1;
246246}
247247248+function findAdjacentOpeningToolCallTag(
249+text: string,
250+start: number,
251+tagName: string,
252+): ParsedToolCallTag | null {
253+let idx = start;
254+while (idx < text.length && /\s/.test(text[idx])) {
255+idx += 1;
256+}
257+if (text[idx] !== "<") {
258+return null;
259+}
260+const tag = parseToolCallTagAt(text, idx);
261+if (!tag || tag.isClose || tag.tagName !== tagName) {
262+return null;
263+}
264+return tag;
265+}
266+248267function parseToolCallTagAt(text: string, start: number): ParsedToolCallTag | null {
249268if (text[start] !== "<") {
250269return null;
@@ -299,7 +318,10 @@ function parseToolCallTagAt(text: string, start: number): ParsedToolCallTag | nu
299318300319export function stripToolCallXmlTags(
301320text: string,
302-options: { stripFunctionCallsXmlPayloads?: boolean } = {},
321+options: {
322+stripFunctionCallsXmlPayloads?: boolean;
323+stripFunctionResponseAfterPluralToolCalls?: boolean;
324+} = {},
303325): string {
304326if (!text || !TOOL_CALL_QUICK_RE.test(text)) {
305327return text;
@@ -355,11 +377,24 @@ export function stripToolCallXmlTags(
355377continue;
356378}
357379const payloadStart = tag.isTruncated ? tag.contentStart : tag.end;
380+const isPluralToolCallWrapper =
381+tag.tagName === "function_calls" || tag.tagName === "tool_calls";
382+const matchingCloseStart = isPluralToolCallWrapper
383+ ? findMatchingToolCallCloseIndex(text, tag.end, tag.tagName)
384+ : -1;
385+const matchingCloseTag =
386+matchingCloseStart === -1 ? null : parseToolCallTagAt(text, matchingCloseStart);
387+const shouldStripPluralWrapperBeforeResponse =
388+options.stripFunctionResponseAfterPluralToolCalls === true &&
389+isPluralToolCallWrapper &&
390+matchingCloseTag !== null &&
391+findAdjacentOpeningToolCallTag(text, matchingCloseTag.end, "function_response") !== null;
358392const shouldDetectXmlPayload =
359393tag.tagName === "tool_call" ||
360394tag.tagName === "function" ||
361-(options.stripFunctionCallsXmlPayloads === true &&
362-(tag.tagName === "function_calls" || tag.tagName === "tool_calls"));
395+((options.stripFunctionCallsXmlPayloads === true ||
396+shouldStripPluralWrapperBeforeResponse) &&
397+isPluralToolCallWrapper);
363398const payloadKind = shouldDetectXmlPayload
364399 ? detectToolCallPayloadKind(text, payloadStart)
365400 : TOOL_CALL_JSON_PAYLOAD_START_RE.test(text.slice(payloadStart))
@@ -732,6 +767,7 @@ type AssistantVisibleTextPipelineOptions = {
732767preserveDowngradedToolText?: boolean;
733768preserveMinimaxToolXml?: boolean;
734769stripFunctionCallsXmlPayloads?: boolean;
770+stripFunctionResponseAfterPluralToolCalls?: boolean;
735771reasoningMode: ReasoningTagMode;
736772reasoningTrim: ReasoningTagTrim;
737773stageOrder: "reasoning-first" | "reasoning-last";
@@ -743,6 +779,7 @@ const ASSISTANT_VISIBLE_TEXT_PIPELINE_OPTIONS: Record<
743779> = {
744780delivery: {
745781finalTrim: "both",
782+stripFunctionResponseAfterPluralToolCalls: true,
746783reasoningMode: "strict",
747784reasoningTrim: "both",
748785stageOrder: "reasoning-last",
@@ -794,6 +831,7 @@ function applyAssistantVisibleTextStagePipeline(
794831cleaned = stripRelevantMemoriesTags(cleaned);
795832cleaned = stripToolCallXmlTags(cleaned, {
796833stripFunctionCallsXmlPayloads: options.stripFunctionCallsXmlPayloads,
834+stripFunctionResponseAfterPluralToolCalls: options.stripFunctionResponseAfterPluralToolCalls,
797835});
798836cleaned = stripLegacyBracketToolCallBlocks(cleaned);
799837cleaned = stripPlainTextToolCallBlocks(cleaned);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。