Merge pull request #116 from Aarondio/fix/tolerant-json-parser

fix(shim): implement tolerant bracket balancer for truncated tool JSON
This commit is contained in:
Kevin Codex
2026-04-02 17:12:44 +08:00
committed by GitHub

View File

@@ -382,7 +382,7 @@ async function* openaiStreamToAnthropic(
): AsyncGenerator<AnthropicStreamEvent> { ): AsyncGenerator<AnthropicStreamEvent> {
const messageId = makeMessageId() const messageId = makeMessageId()
let contentBlockIndex = 0 let contentBlockIndex = 0
const activeToolCalls = new Map<number, { id: string; name: string; index: number }>() const activeToolCalls = new Map<number, { id: string; name: string; index: number; jsonBuffer: string }>()
let hasEmittedContentStart = false let hasEmittedContentStart = false
let lastStopReason: 'tool_use' | 'max_tokens' | 'end_turn' | null = null let lastStopReason: 'tool_use' | 'max_tokens' | 'end_turn' | null = null
let hasEmittedFinalUsage = false let hasEmittedFinalUsage = false
@@ -477,6 +477,7 @@ async function* openaiStreamToAnthropic(
id: tc.id, id: tc.id,
name: tc.function.name, name: tc.function.name,
index: toolBlockIndex, index: toolBlockIndex,
jsonBuffer: tc.function.arguments ?? '',
}) })
yield { yield {
@@ -507,6 +508,9 @@ async function* openaiStreamToAnthropic(
// Continuation of existing tool call // Continuation of existing tool call
const active = activeToolCalls.get(tc.index) const active = activeToolCalls.get(tc.index)
if (active) { if (active) {
if (tc.function.arguments) {
active.jsonBuffer += tc.function.arguments
}
yield { yield {
type: 'content_block_delta', type: 'content_block_delta',
index: active.index, index: active.index,
@@ -534,6 +538,36 @@ async function* openaiStreamToAnthropic(
} }
// Close active tool calls // Close active tool calls
for (const [, tc] of activeToolCalls) { for (const [, tc] of activeToolCalls) {
let suffixToAdd = ''
if (tc.jsonBuffer) {
try {
JSON.parse(tc.jsonBuffer)
} catch {
const str = tc.jsonBuffer.trimEnd()
const combinations = [
'}', '"}', ']}', '"]}', '}}', '"}}', ']}}', '"]}}', '"]}]}', '}]}'
]
for (const combo of combinations) {
try {
JSON.parse(str + combo)
suffixToAdd = combo
break
} catch {}
}
}
}
if (suffixToAdd) {
yield {
type: 'content_block_delta',
index: tc.index,
delta: {
type: 'input_json_delta',
partial_json: suffixToAdd,
},
}
}
yield { type: 'content_block_stop', index: tc.index } yield { type: 'content_block_stop', index: tc.index }
} }