






















@@ -76,6 +76,10 @@ const TOOLCALL_REPAIR_FREEFORM_SUCCESSOR_KEYS: Record<string, string> = {
7676old_string: "new_string",
7777oldText: "newText",
7878};
79+const TOOLCALL_REPAIR_TOOL_VALUE_SUCCESSOR_KEYS = new Map<
80+string,
81+ReadonlyMap<string, readonly string[]>
82+>([["read", new Map([["path", ["offset", "limit"]]])]]);
7983const TOOLCALL_REPAIR_JSON_STRING_ESCAPES: Record<string, string> = {
8084'"': '"',
8185"\\": "\\",
@@ -229,7 +233,41 @@ function readObjectMemberKeyAfterComma(raw: string, commaIndex: number): string
229233return key.value;
230234}
231235232-function shouldCloseSmartQuotedValueAt(raw: string, quoteIndex: number, valueKey: string): boolean {
236+function normalizeToolCallRepairToolName(value: string): string | undefined {
237+const trimmed = value.trim();
238+if (!/^[a-z0-9_-]{1,128}$/i.test(trimmed)) {
239+return undefined;
240+}
241+return trimmed.toLowerCase();
242+}
243+244+function extractToolNameFromLeadingPrefix(prefix: string): string | undefined {
245+const match = /(?:^|[.\s])(?:functions?|tools?)[._:/-]?([a-z0-9_-]+)/i.exec(prefix);
246+return match?.[1] ? normalizeToolCallRepairToolName(match[1]) : undefined;
247+}
248+249+function isToolSpecificValueSuccessor(params: {
250+toolName?: string;
251+valueKey: string;
252+nextKey: string;
253+}): boolean {
254+const toolName = params.toolName;
255+if (!toolName) {
256+return false;
257+}
258+return (
259+TOOLCALL_REPAIR_TOOL_VALUE_SUCCESSOR_KEYS.get(toolName)
260+?.get(params.valueKey)
261+?.includes(params.nextKey) ?? false
262+);
263+}
264+265+function shouldCloseSmartQuotedValueAt(
266+raw: string,
267+quoteIndex: number,
268+valueKey: string,
269+toolName?: string,
270+): boolean {
233271const nextIndex = skipWhitespace(raw, quoteIndex + 1);
234272const nextChar = raw[nextIndex];
235273if (nextIndex >= raw.length || nextChar === "}") {
@@ -244,7 +282,10 @@ function shouldCloseSmartQuotedValueAt(raw: string, quoteIndex: number, valueKey
244282return false;
245283}
246284if (!TOOLCALL_REPAIR_FREEFORM_VALUE_KEYS.has(valueKey)) {
247-return TOOLCALL_REPAIR_KNOWN_ARG_KEYS.has(nextKey);
285+return (
286+TOOLCALL_REPAIR_KNOWN_ARG_KEYS.has(nextKey) ||
287+isToolSpecificValueSuccessor({ toolName, valueKey, nextKey })
288+);
248289}
249290return TOOLCALL_REPAIR_FREEFORM_SUCCESSOR_KEYS[valueKey] === nextKey;
250291}
@@ -261,11 +302,12 @@ function readSmartQuotedValue(
261302raw: string,
262303startIndex: number,
263304key: string,
305+toolName?: string,
264306): ToolCallRepairJsonValue | undefined {
265307let value = "";
266308for (let i = startIndex + 1; i < raw.length; i += 1) {
267309const char = raw[i];
268-if (isToolCallRepairSmartQuote(char) && shouldCloseSmartQuotedValueAt(raw, i, key)) {
310+if (isToolCallRepairSmartQuote(char) && shouldCloseSmartQuotedValueAt(raw, i, key, toolName)) {
269311return { value: decodeSmartQuotedJsonStringEscapes(value), endIndex: i + 1 };
270312}
271313value += char;
@@ -366,13 +408,14 @@ function readObjectValue(
366408raw: string,
367409startIndex: number,
368410key: string,
411+toolName?: string,
369412): ToolCallRepairJsonValue | undefined {
370413const char = raw[startIndex];
371414if (char === '"') {
372415return readAsciiQuotedString(raw, startIndex);
373416}
374417if (isToolCallRepairSmartQuote(char)) {
375-return readSmartQuotedValue(raw, startIndex, key);
418+return readSmartQuotedValue(raw, startIndex, key, toolName);
376419}
377420if (key === "edits" && char === "[") {
378421return readSmartQuotedEditArray(raw, startIndex);
@@ -383,6 +426,7 @@ function readObjectValue(
383426function parseSmartQuotedToolCallObject(
384427raw: string,
385428startIndex: number,
429+toolName?: string,
386430): ToolCallRepairParsedObject | undefined {
387431if (raw[startIndex] !== "{") {
388432return undefined;
@@ -406,7 +450,7 @@ function parseSmartQuotedToolCallObject(
406450return undefined;
407451}
408452409-const value = readObjectValue(raw, skipWhitespace(raw, index + 1), key.value);
453+const value = readObjectValue(raw, skipWhitespace(raw, index + 1), key.value, toolName);
410454if (!value) {
411455return undefined;
412456}
@@ -460,7 +504,10 @@ function tryExtractUsableToolCallArgumentsFromJson(
460504};
461505}
462506463-function tryExtractSmartQuotedToolCallArguments(raw: string): ToolCallArgumentRepair | undefined {
507+function tryExtractSmartQuotedToolCallArguments(
508+raw: string,
509+toolNameFromContext?: string,
510+): ToolCallArgumentRepair | undefined {
464511if (!/[\u201c\u201d\u201e\u201f]/.test(raw)) {
465512return undefined;
466513}
@@ -472,7 +519,11 @@ function tryExtractSmartQuotedToolCallArguments(raw: string): ToolCallArgumentRe
472519if (!isAllowedToolCallRepairLeadingPrefix(leadingPrefix)) {
473520return undefined;
474521}
475-const parsed = parseSmartQuotedToolCallObject(raw, startIndex);
522+const parsed = parseSmartQuotedToolCallObject(
523+raw,
524+startIndex,
525+toolNameFromContext ?? extractToolNameFromLeadingPrefix(leadingPrefix),
526+);
476527if (!parsed) {
477528return undefined;
478529}
@@ -491,7 +542,10 @@ function tryExtractSmartQuotedToolCallArguments(raw: string): ToolCallArgumentRe
491542};
492543}
493544494-function tryExtractUsableToolCallArguments(raw: string): ToolCallArgumentRepair | undefined {
545+function tryExtractUsableToolCallArguments(
546+raw: string,
547+toolNameFromContext?: string,
548+): ToolCallArgumentRepair | undefined {
495549if (!raw.trim()) {
496550return undefined;
497551}
@@ -506,10 +560,30 @@ function tryExtractUsableToolCallArguments(raw: string): ToolCallArgumentRepair
506560}
507561508562return (
509-tryExtractUsableToolCallArgumentsFromJson(raw) ?? tryExtractSmartQuotedToolCallArguments(raw)
563+tryExtractUsableToolCallArgumentsFromJson(raw) ??
564+tryExtractSmartQuotedToolCallArguments(raw, toolNameFromContext)
510565);
511566}
512567568+function readToolCallNameInMessage(message: unknown, contentIndex: number): string | undefined {
569+if (!message || typeof message !== "object") {
570+return undefined;
571+}
572+const content = (message as { content?: unknown }).content;
573+if (!Array.isArray(content)) {
574+return undefined;
575+}
576+const block = content[contentIndex];
577+if (!block || typeof block !== "object") {
578+return undefined;
579+}
580+const typedBlock = block as { type?: unknown; name?: unknown };
581+if (!isToolCallBlockType(typedBlock.type) || typeof typedBlock.name !== "string") {
582+return undefined;
583+}
584+return normalizeToolCallRepairToolName(typedBlock.name);
585+}
586+513587function repairToolCallArgumentsInMessage(
514588message: unknown,
515589contentIndex: number,
@@ -635,7 +709,10 @@ function wrapStreamRepairMalformedToolCallArguments(
635709repairedArgsByIndex.has(event.contentIndex);
636710if (shouldReevaluateRepair) {
637711const hadRepairState = repairedArgsByIndex.has(event.contentIndex);
638-const repair = tryExtractUsableToolCallArguments(nextPartialJson);
712+const toolName =
713+readToolCallNameInMessage(event.partial, event.contentIndex) ??
714+readToolCallNameInMessage(event.message, event.contentIndex);
715+const repair = tryExtractUsableToolCallArguments(nextPartialJson, toolName);
639716if (repair) {
640717if (
641718!hadRepairState &&
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。