Skip to content

Commit 093084e

Browse files
committed
feat: 更新工具调用处理逻辑,支持新旧格式兼容并优化工具调用参数合并
1 parent 107ec04 commit 093084e

File tree

4 files changed

+151
-9
lines changed

4 files changed

+151
-9
lines changed

docs/advanced/document-processing.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ uv run scripts/batch_upload.py \
131131
--wait-for-completion \
132132
--poll-interval 5 \
133133
--recursive \
134+
--enable-ocr mineru_ocr \ # mineru_official, paddlex_ocr, onnx_rapid_ocr
134135
--record-file scripts/tmp/batch_processed_files_1029.txt
135136
```
136137

web/src/components/AgentChatComponent.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@ const resetOnGoingConv = (threadId = null, preserveMessages = false) => {
388388
const _processStreamChunk = (chunk, threadId) => {
389389
const { status, msg, request_id, message } = chunk;
390390
const threadState = getThreadState(threadId);
391+
// console.log('msg:', msg);
391392
392393
if (!threadState) return false;
393394

web/src/components/AgentMessageComponent.vue

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@
3333
<span v-else-if="message.error_type === 'unexpect'">生成过程中出现异常</span>
3434
</div>
3535

36-
<div v-if="message.tool_calls && Object.keys(message.tool_calls).length > 0" class="tool-calls-container">
37-
<div v-for="(toolCall, index) in message.tool_calls || {}" :key="index" class="tool-call-container">
36+
<div v-if="validToolCalls && validToolCalls.length > 0" class="tool-calls-container">
37+
<div v-for="(toolCall, index) in validToolCalls" :key="toolCall.id || index" class="tool-call-container">
3838
<div v-if="toolCall" class="tool-call-display" :class="{ 'is-collapsed': !expandedToolCalls.has(toolCall.id) }">
3939
<div class="tool-header" @click="toggleToolCall(toolCall.id)">
4040
<span v-if="!toolCall.tool_call_result">
@@ -47,15 +47,17 @@
4747
</span>
4848
</div>
4949
<div class="tool-content" v-show="expandedToolCalls.has(toolCall.id)">
50-
<div class="tool-params" v-if="toolCall.args || toolCall.function.arguments">
50+
<div class="tool-params" v-if="toolCall.args || toolCall.function?.arguments">
5151
<div class="tool-params-content">
52-
<strong>参数:</strong> {{ toolCall.args || toolCall.function.arguments }}
52+
<strong>参数:</strong>
53+
<span v-if="getFormattedToolArgs(toolCall)">{{ getFormattedToolArgs(toolCall) }}</span>
54+
<span v-else>{{ toolCall.args || toolCall.function?.arguments }}</span>
5355
</div>
5456
</div>
5557
<div class="tool-result" v-if="toolCall.tool_call_result && toolCall.tool_call_result.content">
5658
<div class="tool-result-content" :data-tool-call-id="toolCall.id">
5759
<ToolResultRenderer
58-
:tool-name="toolCall.name || toolCall.function.name"
60+
:tool-name="toolCall.name || toolCall.function?.name"
5961
:result-content="toolCall.tool_call_result.content"
6062
/>
6163
</div>
@@ -146,13 +148,51 @@ const { availableTools } = storeToRefs(agentStore);
146148
147149
// 工具相关方法
148150
const getToolNameByToolCall = (toolCall) => {
149-
const toolId = toolCall.name || toolCall.function.name;
151+
const toolId = toolCall.name || toolCall.function?.name;
150152
const toolsList = availableTools.value ? Object.values(availableTools.value) : [];
151153
const tool = toolsList.find(t => t.id === toolId);
152154
return tool ? tool.name : toolId;
153155
};
154156
157+
const getFormattedToolArgs = (toolCall) => {
158+
const args = toolCall.args || toolCall.function?.arguments;
159+
if (!args) return '';
160+
161+
try {
162+
// 尝试解析JSON格式的参数
163+
if (typeof args === 'string' && args.trim().startsWith('{')) {
164+
const parsed = JSON.parse(args);
165+
return JSON.stringify(parsed, null, 2);
166+
}
167+
} catch (e) {
168+
// 如果解析失败,直接返回原始字符串
169+
}
170+
171+
return args;
172+
};
173+
174+
// 过滤有效的工具调用
175+
const validToolCalls = computed(() => {
176+
if (!props.message.tool_calls || !Array.isArray(props.message.tool_calls)) {
177+
return [];
178+
}
179+
180+
return props.message.tool_calls.filter(toolCall => {
181+
// 过滤掉无效的工具调用
182+
return toolCall &&
183+
(toolCall.id || toolCall.name) &&
184+
(toolCall.args !== undefined ||
185+
toolCall.function?.arguments !== undefined ||
186+
toolCall.tool_call_result !== undefined);
187+
});
188+
});
189+
155190
const parsedData = computed(() => {
191+
// 调试工具调用处理
192+
if (validToolCalls.value && validToolCalls.value.length > 0) {
193+
console.log('Valid tool calls in message:', validToolCalls.value);
194+
}
195+
156196
// Start with default values from the prop to avoid mutation.
157197
let content = props.message.content.trim() || '';
158198
let reasoning_content = props.message.additional_kwargs?.reasoning_content || '';

web/src/utils/messageProcessor.js

Lines changed: 103 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,12 @@ export class MessageProcessor {
1212

1313
// 构建工具响应映射
1414
for (const item of msgs) {
15-
if (item.type === 'tool' && item.tool_call_id) {
16-
toolResponseMap.set(item.tool_call_id, item);
15+
if (item.type === 'tool') {
16+
// 使用多种可能的ID字段来匹配工具调用
17+
const toolCallId = item.tool_call_id || item.id;
18+
if (toolCallId) {
19+
toolResponseMap.set(toolCallId, item);
20+
}
1721
}
1822
}
1923

@@ -127,13 +131,14 @@ export class MessageProcessor {
127131
result.additional_kwargs.reasoning_content += chunk.additional_kwargs.reasoning_content;
128132
}
129133

130-
// 合并tool_calls
134+
// 合并tool_calls (处理新的数据结构)
131135
MessageProcessor._mergeToolCalls(result, chunk);
132136
}
133137

134138
// 处理AIMessageChunk类型
135139
if (result.type === 'AIMessageChunk') {
136140
result.type = 'ai';
141+
// 将tool_calls从additional_kwargs移到顶层,并确保格式正确
137142
if (result.additional_kwargs?.tool_calls) {
138143
result.tool_calls = result.additional_kwargs.tool_calls;
139144
}
@@ -149,6 +154,7 @@ export class MessageProcessor {
149154
* @param {Object} chunk - 当前块
150155
*/
151156
static _mergeToolCalls(result, chunk) {
157+
// 1. 处理 additional_kwargs.tool_calls (旧格式,保持兼容性)
152158
if (chunk.additional_kwargs?.tool_calls) {
153159
if (!result.additional_kwargs) result.additional_kwargs = {};
154160
if (!result.additional_kwargs.tool_calls) result.additional_kwargs.tool_calls = [];
@@ -169,6 +175,100 @@ export class MessageProcessor {
169175
}
170176
}
171177
}
178+
179+
// 2. 处理顶层的 tool_calls (新格式)
180+
if (chunk.tool_calls) {
181+
if (!result.tool_calls) result.tool_calls = [];
182+
183+
for (const toolCall of chunk.tool_calls) {
184+
// 过滤掉无效的工具调用(没有id或name的空对象)
185+
if (!toolCall.id && !toolCall.name) {
186+
continue;
187+
}
188+
189+
const existingToolCall = result.tool_calls.find(t => t.id === toolCall.id);
190+
191+
if (existingToolCall) {
192+
// 合并相同ID的tool call的args
193+
if (toolCall.args && existingToolCall.args !== undefined) {
194+
existingToolCall.args += toolCall.args;
195+
}
196+
} else {
197+
// 添加新的tool call
198+
result.tool_calls.push(JSON.parse(JSON.stringify(toolCall)));
199+
}
200+
}
201+
}
202+
203+
// 3. 处理 tool_call_chunks (分片的工具调用参数)
204+
if (chunk.tool_call_chunks) {
205+
if (!result.tool_call_chunks) result.tool_call_chunks = [];
206+
207+
for (const toolCallChunk of chunk.tool_call_chunks) {
208+
// 过滤掉无效的chunk(没有id、name和args的空对象)
209+
if (!toolCallChunk.id && !toolCallChunk.name && !toolCallChunk.args) {
210+
continue;
211+
}
212+
213+
const existingChunk = result.tool_call_chunks.find(
214+
t => (t.id === toolCallChunk.id || (t.index === toolCallChunk.index && t.index !== null))
215+
);
216+
217+
if (existingChunk) {
218+
// 合并参数字符串
219+
if (toolCallChunk.args && existingChunk.args !== undefined) {
220+
existingChunk.args += toolCallChunk.args;
221+
}
222+
// 更新工具名称
223+
if (toolCallChunk.name && !existingChunk.name) {
224+
existingChunk.name = toolCallChunk.name;
225+
}
226+
} else {
227+
// 添加新的chunk
228+
result.tool_call_chunks.push(JSON.parse(JSON.stringify(toolCallChunk)));
229+
}
230+
}
231+
}
232+
233+
// 4. 同步 tool_call_chunks 到 tool_calls (构建完整的工具调用)
234+
if (result.tool_call_chunks && result.tool_call_chunks.length > 0) {
235+
if (!result.tool_calls) result.tool_calls = [];
236+
237+
for (const chunk of result.tool_call_chunks) {
238+
if (chunk.name && chunk.args) {
239+
const existingToolCall = result.tool_calls.find(t => t.id === chunk.id);
240+
241+
if (!existingToolCall) {
242+
// 创建新的完整工具调用
243+
result.tool_calls.push({
244+
id: chunk.id,
245+
name: chunk.name,
246+
args: chunk.args,
247+
type: 'tool_call'
248+
});
249+
} else {
250+
// 更新现有工具调用的参数
251+
existingToolCall.args = chunk.args;
252+
}
253+
}
254+
}
255+
}
256+
257+
// 5. 清理无效的工具调用
258+
if (result.tool_calls) {
259+
result.tool_calls = result.tool_calls.filter(toolCall => {
260+
// 保留有id或有name的工具调用,并且不是空的args对象
261+
return (toolCall.id || toolCall.name) &&
262+
(toolCall.args !== undefined || toolCall.function?.arguments);
263+
});
264+
}
265+
266+
// 清理空的tool_call_chunks
267+
if (result.tool_call_chunks) {
268+
result.tool_call_chunks = result.tool_call_chunks.filter(chunk => {
269+
return (chunk.id || chunk.name || chunk.args);
270+
});
271+
}
172272
}
173273

174274
/**

0 commit comments

Comments
 (0)