fix: handle multi-part toolUse events (input/stop sent separately)
This commit is contained in:
parent
9a2f0b0c52
commit
55ae7664ec
1 changed files with 71 additions and 17 deletions
|
|
@ -1032,12 +1032,18 @@ async initializeAuth(forceRefresh = false) {
|
||||||
// Kiro 返回格式: {"content":"..."} 或 {"name":"xxx","toolUseId":"xxx",...} 或 {"followupPrompt":"..."}
|
// Kiro 返回格式: {"content":"..."} 或 {"name":"xxx","toolUseId":"xxx",...} 或 {"followupPrompt":"..."}
|
||||||
|
|
||||||
// 搜索所有可能的 JSON payload 开头模式
|
// 搜索所有可能的 JSON payload 开头模式
|
||||||
|
// Kiro 返回的 toolUse 可能分多个事件:
|
||||||
|
// 1. {"name":"xxx","toolUseId":"xxx"} - 开始
|
||||||
|
// 2. {"input":"..."} - input 数据(可能多次)
|
||||||
|
// 3. {"stop":true} - 结束
|
||||||
const contentStart = remaining.indexOf('{"content":', searchStart);
|
const contentStart = remaining.indexOf('{"content":', searchStart);
|
||||||
const nameStart = remaining.indexOf('{"name":', searchStart);
|
const nameStart = remaining.indexOf('{"name":', searchStart);
|
||||||
const followupStart = remaining.indexOf('{"followupPrompt":', searchStart);
|
const followupStart = remaining.indexOf('{"followupPrompt":', searchStart);
|
||||||
|
const inputStart = remaining.indexOf('{"input":', searchStart);
|
||||||
|
const stopStart = remaining.indexOf('{"stop":', searchStart);
|
||||||
|
|
||||||
// 找到最早出现的有效 JSON 模式
|
// 找到最早出现的有效 JSON 模式
|
||||||
const candidates = [contentStart, nameStart, followupStart].filter(pos => pos >= 0);
|
const candidates = [contentStart, nameStart, followupStart, inputStart, stopStart].filter(pos => pos >= 0);
|
||||||
if (candidates.length === 0) break;
|
if (candidates.length === 0) break;
|
||||||
|
|
||||||
const jsonStart = Math.min(...candidates);
|
const jsonStart = Math.min(...candidates);
|
||||||
|
|
@ -1096,7 +1102,7 @@ async initializeAuth(forceRefresh = false) {
|
||||||
decodedContent = decodedContent.replace(/(?<!\\)\\n/g, '\n');
|
decodedContent = decodedContent.replace(/(?<!\\)\\n/g, '\n');
|
||||||
events.push({ type: 'content', data: decodedContent });
|
events.push({ type: 'content', data: decodedContent });
|
||||||
}
|
}
|
||||||
// 处理结构化工具调用事件
|
// 处理结构化工具调用事件 - 开始事件(包含 name 和 toolUseId)
|
||||||
else if (parsed.name && parsed.toolUseId) {
|
else if (parsed.name && parsed.toolUseId) {
|
||||||
events.push({
|
events.push({
|
||||||
type: 'toolUse',
|
type: 'toolUse',
|
||||||
|
|
@ -1108,6 +1114,24 @@ async initializeAuth(forceRefresh = false) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// 处理工具调用的 input 续传事件(只有 input 字段)
|
||||||
|
else if (parsed.input !== undefined && !parsed.name) {
|
||||||
|
events.push({
|
||||||
|
type: 'toolUseInput',
|
||||||
|
data: {
|
||||||
|
input: parsed.input
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 处理工具调用的结束事件(只有 stop 字段)
|
||||||
|
else if (parsed.stop !== undefined) {
|
||||||
|
events.push({
|
||||||
|
type: 'toolUseStop',
|
||||||
|
data: {
|
||||||
|
stop: parsed.stop
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// JSON 解析失败,跳过这个位置继续搜索
|
// JSON 解析失败,跳过这个位置继续搜索
|
||||||
}
|
}
|
||||||
|
|
@ -1174,6 +1198,10 @@ async initializeAuth(forceRefresh = false) {
|
||||||
yield { type: 'content', content: event.data };
|
yield { type: 'content', content: event.data };
|
||||||
} else if (event.type === 'toolUse') {
|
} else if (event.type === 'toolUse') {
|
||||||
yield { type: 'toolUse', toolUse: event.data };
|
yield { type: 'toolUse', toolUse: event.data };
|
||||||
|
} else if (event.type === 'toolUseInput') {
|
||||||
|
yield { type: 'toolUseInput', input: event.data.input };
|
||||||
|
} else if (event.type === 'toolUseStop') {
|
||||||
|
yield { type: 'toolUseStop', stop: event.data.stop };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1263,34 +1291,60 @@ async initializeAuth(forceRefresh = false) {
|
||||||
};
|
};
|
||||||
} else if (event.type === 'toolUse') {
|
} else if (event.type === 'toolUse') {
|
||||||
const tc = event.toolUse;
|
const tc = event.toolUse;
|
||||||
// 结构化工具调用是逐步构建的
|
// 工具调用开始事件(包含 name 和 toolUseId)
|
||||||
if (tc.name && tc.toolUseId) {
|
if (tc.name && tc.toolUseId) {
|
||||||
if (!currentToolCall || currentToolCall.toolUseId !== tc.toolUseId) {
|
// 如果有未完成的工具调用,先保存它
|
||||||
// 新的工具调用
|
if (currentToolCall) {
|
||||||
currentToolCall = {
|
|
||||||
toolUseId: tc.toolUseId,
|
|
||||||
name: tc.name,
|
|
||||||
input: tc.input || ''
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// 累积 input
|
|
||||||
currentToolCall.input += tc.input || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果是最后一个事件,完成工具调用
|
|
||||||
if (tc.stop) {
|
|
||||||
try {
|
try {
|
||||||
currentToolCall.input = JSON.parse(currentToolCall.input);
|
currentToolCall.input = JSON.parse(currentToolCall.input);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// input 不是有效 JSON,保持原样
|
// input 不是有效 JSON,保持原样
|
||||||
}
|
}
|
||||||
toolCalls.push(currentToolCall);
|
toolCalls.push(currentToolCall);
|
||||||
|
}
|
||||||
|
// 开始新的工具调用
|
||||||
|
currentToolCall = {
|
||||||
|
toolUseId: tc.toolUseId,
|
||||||
|
name: tc.name,
|
||||||
|
input: tc.input || ''
|
||||||
|
};
|
||||||
|
// 如果这个事件同时包含 stop,直接完成
|
||||||
|
if (tc.stop) {
|
||||||
|
try {
|
||||||
|
currentToolCall.input = JSON.parse(currentToolCall.input);
|
||||||
|
} catch (e) {}
|
||||||
|
toolCalls.push(currentToolCall);
|
||||||
currentToolCall = null;
|
currentToolCall = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (event.type === 'toolUseInput') {
|
||||||
|
// 工具调用的 input 续传事件
|
||||||
|
if (currentToolCall) {
|
||||||
|
currentToolCall.input += event.input || '';
|
||||||
|
}
|
||||||
|
} else if (event.type === 'toolUseStop') {
|
||||||
|
// 工具调用结束事件
|
||||||
|
if (currentToolCall && event.stop) {
|
||||||
|
try {
|
||||||
|
currentToolCall.input = JSON.parse(currentToolCall.input);
|
||||||
|
} catch (e) {
|
||||||
|
// input 不是有效 JSON,保持原样
|
||||||
|
}
|
||||||
|
toolCalls.push(currentToolCall);
|
||||||
|
currentToolCall = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理未完成的工具调用(如果流提前结束)
|
||||||
|
if (currentToolCall) {
|
||||||
|
try {
|
||||||
|
currentToolCall.input = JSON.parse(currentToolCall.input);
|
||||||
|
} catch (e) {}
|
||||||
|
toolCalls.push(currentToolCall);
|
||||||
|
currentToolCall = null;
|
||||||
|
}
|
||||||
|
|
||||||
// 检查文本内容中的 bracket 格式工具调用
|
// 检查文本内容中的 bracket 格式工具调用
|
||||||
const bracketToolCalls = parseBracketToolCalls(totalContent);
|
const bracketToolCalls = parseBracketToolCalls(totalContent);
|
||||||
if (bracketToolCalls && bracketToolCalls.length > 0) {
|
if (bracketToolCalls && bracketToolCalls.length > 0) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue