Claude: Streamed tool calls parser
| @@ -136,6 +136,8 @@ export class ToolManager { | ||
| 136 | 136 | */ |
| 137 | 137 | static #tools = new Map(); |
| 138 | 138 | |
| 139 | + static #INPUT_DELTA_KEY = '__input_json_delta'; | |
| 140 | + | |
| 139 | 141 | /** |
| 140 | 142 | * Returns an Array of all tools that have been registered. |
| 141 | 143 | * @type {ToolDefinition[]} |
| @@ -304,20 +306,48 @@ export class ToolManager { | ||
| 304 | 306 | } |
| 305 | 307 | if (typeof parsed?.content_block === 'object') { |
| 306 | 308 | const choiceIndex = 0; |
| 309 | + const toolCallIndex = parsed?.index ?? 0; | |
| 307 | 310 | |
| 308 | 311 | if (parsed?.content_block?.type === 'tool_use') { |
| 309 | 312 | if (!Array.isArray(toolCalls[choiceIndex])) { |
| 310 | 313 | toolCalls[choiceIndex] = []; |
| 311 | 314 | } |
| 312 | - | |
| 313 | - const toolCallIndex = toolCalls[choiceIndex].length; | |
| 314 | - | |
| 315 | 315 | if (toolCalls[choiceIndex][toolCallIndex] === undefined) { |
| 316 | 316 | toolCalls[choiceIndex][toolCallIndex] = {}; |
| 317 | 317 | } |
| 318 | - | |
| 319 | 318 | const targetToolCall = toolCalls[choiceIndex][toolCallIndex]; |
| 320 | 319 | ObjectToolManager.assign#applyToolCallDelta(targetToolCall, parsed.content_block); |
| 320 | + } | |
| 321 | + } | |
| 322 | + if (typeof parsed?.delta === 'object') { | |
| 323 | + const choiceIndex = 0; | |
| 324 | + const toolCallIndex = parsed?.index ?? 0; | |
| 325 | + const targetToolCall = toolCalls[choiceIndex]?.[toolCallIndex]; | |
| 326 | + if (targetToolCall){ | |
| 327 | + if (parsed?.delta?.type === 'input_json_delta') { | |
| 328 | + const jsonDelta = parsed?.delta?.partial_json; | |
| 329 | + if (!targetToolCall[this.#INPUT_DELTA_KEY]) { | |
| 330 | + targetToolCall[this.#INPUT_DELTA_KEY] = ''; | |
| 331 | + } | |
| 332 | + targetToolCall[this.#INPUT_DELTA_KEY] += jsonDelta; | |
| 333 | + } | |
| 334 | + } | |
| 335 | + } | |
| 336 | + if (parsed?.type === 'content_block_stop') { | |
| 337 | + const choiceIndex = 0; | |
| 338 | + const toolCallIndex = parsed?.index ?? 0; | |
| 339 | + const targetToolCall = toolCalls[choiceIndex]?.[toolCallIndex]; | |
| 340 | + if (targetToolCall) { | |
| 341 | + const jsonDeltaString = targetToolCall[this.#INPUT_DELTA_KEY]; | |
| 342 | + if (jsonDeltaString) { | |
| 343 | + try { | |
| 344 | + const jsonDelta = { input: JSON.parse(jsonDeltaString) }; | |
| 345 | + delete targetToolCall[this.#INPUT_DELTA_KEY]; | |
| 346 | + ToolManager.#applyToolCallDelta(targetToolCall, jsonDelta); | |
| 347 | + } catch (error) { | |
| 348 | + console.warn('Failed to apply input JSON delta:', error); | |
| 349 | + } | |
| 350 | + } | |
| 321 | 351 | } |
| 322 | 352 | } |
| 323 | 353 | } |
| @@ -397,9 +427,12 @@ export class ToolManager { | ||
| 397 | 427 | * @returns {any[]} Tool calls from the response data |
| 398 | 428 | */ |
| 399 | 429 | static #getToolCallsFromData(data) { |
| 430 | + const isClaudeToolCall = c => Array.isArray(c) ? c.filter(x => x).every(isClaudeToolCall) : c?.input && c?.name && c?.id; | |
| 431 | + const convertClaudeToolCall = c => ({ id: c.id, function: { name: c.name, arguments: c.input } }); | |
| 432 | + | |
| 400 | 433 | // Parsed tool calls from streaming data |
| 401 | 434 | if (Array.isArray(data) && data.length > 0 && Array.isArray(data[0])) { |
| 402 | - return data[0]; | |
| 435 | + return isClaudeToolCall(data[0]) ? data[0].filter(x => x).map(convertClaudeToolCall) : data[0]; | |
| 403 | 436 | } |
| 404 | 437 | |
| 405 | 438 | // Parsed tool calls from non-streaming data |
| @@ -412,19 +445,13 @@ export class ToolManager { | ||
| 412 | 445 | } |
| 413 | 446 | } |
| 414 | 447 | |
| 448 | + // Claude tool calls to OpenAI tool calls | |
| 415 | 449 | if (Array.isArray(data?.content)) { |
| 416 | - // Claude tool calls to OpenAI tool calls | |
| 450 | + const content = data.content.filter(c => c.type === 'tool_use').map(convertClaudeToolCall); | |
| 417 | - const content = data.content.filter(c => c.type === 'tool_use').map(c => { | |
| 451 | + | |
| 418 | - return { | |
| 452 | + if (content) { | |
| 419 | - id: c.id, | |
| 453 | + return content; | |
| 420 | - function: { | |
| 454 | + } | |
| 421 | - name: c.name, | |
| 422 | - arguments: c.input, | |
| 423 | - }, | |
| 424 | - }; | |
| 425 | - }); | |
| 426 | - | |
| 427 | - return content; | |
| 428 | 455 | } |
| 429 | 456 | } |
| 430 | 457 | |