fix: claude streaming tool use;
This commit is contained in:
parent
fd5ed25230
commit
7d2e0e3832
12
src/query.ts
12
src/query.ts
@ -567,8 +567,16 @@ async function* checkPermissionsAndCallTool(
|
||||
// (surprisingly, the model is not great at generating valid input)
|
||||
const isValidInput = tool.inputSchema.safeParse(input)
|
||||
if (!isValidInput.success) {
|
||||
// Create a more helpful error message for common cases
|
||||
let errorMessage = `InputValidationError: ${isValidInput.error.message}`
|
||||
|
||||
// Special handling for the "View" tool (FileReadTool) being called with empty parameters
|
||||
if (tool.name === 'View' && Object.keys(input).length === 0) {
|
||||
errorMessage = `Error: The View tool requires a 'file_path' parameter to specify which file to read. Please provide the absolute path to the file you want to view. For example: {"file_path": "/path/to/file.txt"}`
|
||||
}
|
||||
|
||||
logEvent('tengu_tool_use_error', {
|
||||
error: `InputValidationError: ${isValidInput.error.message}`,
|
||||
error: errorMessage,
|
||||
messageID: assistantMessage.message.id,
|
||||
toolName: tool.name,
|
||||
toolInput: JSON.stringify(input).slice(0, 200),
|
||||
@ -576,7 +584,7 @@ async function* checkPermissionsAndCallTool(
|
||||
yield createUserMessage([
|
||||
{
|
||||
type: 'tool_result',
|
||||
content: `InputValidationError: ${isValidInput.error.message}`,
|
||||
content: errorMessage,
|
||||
is_error: true,
|
||||
tool_use_id: toolUseID,
|
||||
},
|
||||
|
||||
@ -1471,6 +1471,7 @@ async function queryAnthropicNative(
|
||||
let finalResponse: any | null = null
|
||||
let messageStartEvent: any = null
|
||||
const contentBlocks: any[] = []
|
||||
const inputJSONBuffers = new Map<number, string>()
|
||||
let usage: any = null
|
||||
let stopReason: string | null = null
|
||||
let stopSequence: string | null = null
|
||||
@ -1484,30 +1485,81 @@ async function queryAnthropicNative(
|
||||
})
|
||||
throw new Error('Request was cancelled')
|
||||
}
|
||||
if (event.type === 'message_start') {
|
||||
messageStartEvent = event
|
||||
finalResponse = {
|
||||
...event.message,
|
||||
content: [], // Will be populated from content blocks
|
||||
}
|
||||
} else if (event.type === 'content_block_start') {
|
||||
contentBlocks[event.index] = { ...event.content_block }
|
||||
} else if (event.type === 'content_block_delta') {
|
||||
if (!contentBlocks[event.index]) {
|
||||
contentBlocks[event.index] = {
|
||||
type: event.delta.type === 'text_delta' ? 'text' : 'unknown',
|
||||
text: '',
|
||||
|
||||
switch (event.type) {
|
||||
case 'message_start':
|
||||
messageStartEvent = event
|
||||
finalResponse = {
|
||||
...event.message,
|
||||
content: [], // Will be populated from content blocks
|
||||
}
|
||||
}
|
||||
if (event.delta.type === 'text_delta') {
|
||||
contentBlocks[event.index].text += event.delta.text
|
||||
}
|
||||
} else if (event.type === 'message_delta') {
|
||||
if (event.delta.stop_reason) stopReason = event.delta.stop_reason
|
||||
if (event.delta.stop_sequence)
|
||||
stopSequence = event.delta.stop_sequence
|
||||
if (event.usage) usage = { ...usage, ...event.usage }
|
||||
} else if (event.type === 'message_stop') {
|
||||
break
|
||||
|
||||
case 'content_block_start':
|
||||
contentBlocks[event.index] = { ...event.content_block }
|
||||
// Initialize JSON buffer for tool_use blocks
|
||||
if (event.content_block.type === 'tool_use') {
|
||||
inputJSONBuffers.set(event.index, '')
|
||||
}
|
||||
break
|
||||
|
||||
case 'content_block_delta':
|
||||
const blockIndex = event.index
|
||||
|
||||
// Ensure content block exists
|
||||
if (!contentBlocks[blockIndex]) {
|
||||
contentBlocks[blockIndex] = {
|
||||
type: event.delta.type === 'text_delta' ? 'text' : 'tool_use',
|
||||
text: event.delta.type === 'text_delta' ? '' : undefined,
|
||||
}
|
||||
if (event.delta.type === 'input_json_delta') {
|
||||
inputJSONBuffers.set(blockIndex, '')
|
||||
}
|
||||
}
|
||||
|
||||
if (event.delta.type === 'text_delta') {
|
||||
contentBlocks[blockIndex].text += event.delta.text
|
||||
} else if (event.delta.type === 'input_json_delta') {
|
||||
const currentBuffer = inputJSONBuffers.get(blockIndex) || ''
|
||||
inputJSONBuffers.set(blockIndex, currentBuffer + event.delta.partial_json)
|
||||
}
|
||||
break
|
||||
|
||||
case 'message_delta':
|
||||
if (event.delta.stop_reason) stopReason = event.delta.stop_reason
|
||||
if (event.delta.stop_sequence) stopSequence = event.delta.stop_sequence
|
||||
if (event.usage) usage = { ...usage, ...event.usage }
|
||||
break
|
||||
|
||||
case 'content_block_stop':
|
||||
const stopIndex = event.index
|
||||
const block = contentBlocks[stopIndex]
|
||||
|
||||
if (block?.type === 'tool_use' && inputJSONBuffers.has(stopIndex)) {
|
||||
const jsonStr = inputJSONBuffers.get(stopIndex)
|
||||
if (jsonStr) {
|
||||
try {
|
||||
block.input = JSON.parse(jsonStr)
|
||||
} catch (error) {
|
||||
debugLogger.error('JSON_PARSE_ERROR', {
|
||||
blockIndex: stopIndex,
|
||||
jsonStr,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
})
|
||||
block.input = {}
|
||||
}
|
||||
inputJSONBuffers.delete(stopIndex)
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
case 'message_stop':
|
||||
// Clear any remaining buffers
|
||||
inputJSONBuffers.clear()
|
||||
break
|
||||
}
|
||||
|
||||
if (event.type === 'message_stop') {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user