mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-28 10:03:54 +08:00
增加工具调用日志记录,暂时单工具状态
This commit is contained in:
@@ -21,11 +21,22 @@
|
|||||||
<div v-if="message.type === 'time'" class="time-divider">
|
<div v-if="message.type === 'time'" class="time-divider">
|
||||||
{{ message.content }}
|
{{ message.content }}
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="message-item" :class="{ 'user-message': message.chatType === 1 }">
|
<div v-else class="message-item" :class="{ 'user-message': message.chatType === 1, 'tool-message': message.chatType === 3 }">
|
||||||
<img :src="message.chatType === 1 ? getUserAvatar(currentSessionId) : require('@/assets/xiaozhi-logo.png')"
|
<img :src="message.chatType === 1 ? getUserAvatar(currentSessionId) : require('@/assets/xiaozhi-logo.png')"
|
||||||
class="avatar" />
|
class="avatar" />
|
||||||
<div class="message-content">
|
<div class="message-content">
|
||||||
{{ extractContentFromString(message.content) }}
|
<template v-if="Array.isArray(extractContentFromString(message.content))">
|
||||||
|
<div class="content-wrapper">
|
||||||
|
<div v-for="(item, idx) in extractContentFromString(message.content)" :key="idx">
|
||||||
|
<div v-if="item.type === 'text'" class="text-content">{{ item.text }}</div>
|
||||||
|
<div v-else-if="item.type === 'tool'" class="tool-call-text">{{ item.text }}</div>
|
||||||
|
<div v-else-if="item.type === 'tool_result'" class="tool-call-text">{{ item.text }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
{{ extractContentFromString(message.content) }}
|
||||||
|
</template>
|
||||||
<i v-if="message.audioId" :class="getAudioIconClass(message)"
|
<i v-if="message.audioId" :class="getAudioIconClass(message)"
|
||||||
@click="playAudio(message)" class="audio-icon"></i>
|
@click="playAudio(message)" class="audio-icon"></i>
|
||||||
</div>
|
</div>
|
||||||
@@ -154,6 +165,13 @@ export default {
|
|||||||
// 尝试解析为 JSON
|
// 尝试解析为 JSON
|
||||||
try {
|
try {
|
||||||
const jsonObj = JSON.parse(content);
|
const jsonObj = JSON.parse(content);
|
||||||
|
|
||||||
|
// 如果是数组格式(包含 text 和 tool)
|
||||||
|
if (Array.isArray(jsonObj)) {
|
||||||
|
return jsonObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是对象且有 content 字段
|
||||||
if (jsonObj && typeof jsonObj === 'object' && jsonObj.content) {
|
if (jsonObj && typeof jsonObj === 'object' && jsonObj.content) {
|
||||||
return jsonObj.content;
|
return jsonObj.content;
|
||||||
}
|
}
|
||||||
@@ -442,6 +460,32 @@ export default {
|
|||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.content-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-content {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-call-text {
|
||||||
|
color: #1890ff;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 12px;
|
||||||
|
display: block;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-message .tool-call-text {
|
||||||
|
color: #e6f7ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-message .message-content {
|
||||||
|
background-color: #e6ebff;
|
||||||
|
}
|
||||||
|
|
||||||
.loading,
|
.loading,
|
||||||
.no-more {
|
.no-more {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
@@ -1073,38 +1073,55 @@ class ConnectionHandler:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# 如需要大模型先处理一轮,添加相关处理后的日志情况
|
# 如需要大模型先处理一轮,添加相关处理后的日志情况
|
||||||
|
content_parts = []
|
||||||
if len(response_message) > 0:
|
if len(response_message) > 0:
|
||||||
text_buff = "".join(response_message)
|
text_buff = "".join(response_message)
|
||||||
|
content_parts.append({"type": "text", "text": text_buff})
|
||||||
self.tts_MessageText = text_buff
|
self.tts_MessageText = text_buff
|
||||||
self.dialogue.put(Message(role="assistant", content=text_buff))
|
self.dialogue.put(Message(role="assistant", content=text_buff))
|
||||||
response_message.clear()
|
response_message.clear()
|
||||||
|
|
||||||
|
tool_call_data = tool_calls_list[0] # 只取第一个工具
|
||||||
|
|
||||||
|
# 构建工具调用的显示文本
|
||||||
|
try:
|
||||||
|
tool_input = json.loads(tool_call_data['arguments']) if tool_call_data['arguments'] else {}
|
||||||
|
except:
|
||||||
|
tool_input = {}
|
||||||
|
|
||||||
|
# 格式化工具调用为简洁文本,如: get_weather({"location": "北京"})
|
||||||
|
tool_text = f"{tool_call_data['name']}({json.dumps(tool_input, ensure_ascii=False)})"
|
||||||
|
content_parts.append({"type": "tool", "text": tool_text})
|
||||||
|
|
||||||
|
# 先上报包含文本和工具调用的内容(使用chatType=3表示工具调用)
|
||||||
|
tool_call_timestamp = int(time.time())
|
||||||
|
report_content = json.dumps(content_parts, ensure_ascii=False)
|
||||||
|
self.report_queue.put((3, report_content, None, tool_call_timestamp))
|
||||||
|
|
||||||
self.logger.bind(tag=TAG).debug(
|
self.logger.bind(tag=TAG).debug(
|
||||||
f"检测到 {len(tool_calls_list)} 个工具调用"
|
f"function_name={tool_call_data['name']}, function_id={tool_call_data['id']}, function_arguments={tool_call_data['arguments']}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 收集所有工具调用的 Future
|
# 执行单个工具
|
||||||
futures_with_data = []
|
future = asyncio.run_coroutine_threadsafe(
|
||||||
for tool_call_data in tool_calls_list:
|
self.func_handler.handle_llm_function_call(
|
||||||
self.logger.bind(tag=TAG).debug(
|
self, tool_call_data
|
||||||
f"function_name={tool_call_data['name']}, function_id={tool_call_data['id']}, function_arguments={tool_call_data['arguments']}"
|
),
|
||||||
)
|
self.loop,
|
||||||
|
)
|
||||||
|
result = future.result()
|
||||||
|
tool_results = [(result, tool_call_data)]
|
||||||
|
|
||||||
future = asyncio.run_coroutine_threadsafe(
|
# 工具执行完成后,单独上报结果(时间戳+1确保在工具调用之后)
|
||||||
self.func_handler.handle_llm_function_call(
|
if result and result.result:
|
||||||
self, tool_call_data
|
tool_result_text = str(result.result)
|
||||||
),
|
|
||||||
self.loop,
|
|
||||||
)
|
|
||||||
futures_with_data.append((future, tool_call_data))
|
|
||||||
|
|
||||||
# 等待协程结束(实际等待时长为最慢的那个)
|
# 格式化为 {"result": ...}
|
||||||
tool_results = []
|
result_display = f'{{"result":"{tool_result_text}"}}'
|
||||||
for future, tool_call_data in futures_with_data:
|
result_content = json.dumps([{"type": "tool_result", "text": result_display}], ensure_ascii=False)
|
||||||
result = future.result()
|
self.report_queue.put((3, result_content, None, tool_call_timestamp + 1))
|
||||||
tool_results.append((result, tool_call_data))
|
|
||||||
|
|
||||||
# 统一处理所有工具调用结果
|
# 统一处理工具调用结果
|
||||||
if tool_results:
|
if tool_results:
|
||||||
self._handle_function_result(tool_results, depth=depth)
|
self._handle_function_result(tool_results, depth=depth)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user