fix: 直播聊天时实时渲染工具调用卡片
- 新增 appendToolCallCardToPlaceholder: 工具开始执行时显示 running 卡片 - 新增 updateToolCallCardInPlaceholder: 工具完成后更新为 success/error 卡片 - 修复 onToolCallStart/onToolCallResult/onToolCallError 空回调问题 - 修复历史记录有工具卡片但直播聊天没有的不一致问题
This commit is contained in:
@@ -400,6 +400,43 @@ export function updateLastAssistantMessage(content: string, think: string | null
|
|||||||
scrollToBottom();
|
scrollToBottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 实时追加工具调用卡片到当前 placeholder(直播聊天时使用) */
|
||||||
|
export function appendToolCallCardToPlaceholder(tc: ToolCallRecord): void {
|
||||||
|
if (!currentPlaceholder) return;
|
||||||
|
let container = currentPlaceholder.querySelector('.tool-calls-container');
|
||||||
|
if (!container) {
|
||||||
|
container = document.createElement('div');
|
||||||
|
container.className = 'tool-calls-container';
|
||||||
|
const msgBody = currentPlaceholder.querySelector('.msg-body');
|
||||||
|
if (msgBody) msgBody.appendChild(container);
|
||||||
|
}
|
||||||
|
const cardDiv = document.createElement('div');
|
||||||
|
cardDiv.innerHTML = renderToolCallCard(tc);
|
||||||
|
const card = cardDiv.firstElementChild as HTMLElement;
|
||||||
|
card.dataset.toolName = tc.name;
|
||||||
|
container.appendChild(card);
|
||||||
|
scrollToBottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 更新 placeholder 中已有的工具卡片状态(running → success/error) */
|
||||||
|
export function updateToolCallCardInPlaceholder(tc: ToolCallRecord): void {
|
||||||
|
if (!currentPlaceholder) return;
|
||||||
|
const container = currentPlaceholder.querySelector('.tool-calls-container');
|
||||||
|
if (!container) return;
|
||||||
|
// 找到第一个同名且状态为 running 的卡片
|
||||||
|
const existing = container.querySelector(`.tool-call-card[data-tool-name="${tc.name}"].tool-call-running`) as HTMLElement;
|
||||||
|
if (existing) {
|
||||||
|
const cardDiv = document.createElement('div');
|
||||||
|
cardDiv.innerHTML = renderToolCallCard(tc);
|
||||||
|
const newCard = cardDiv.firstElementChild as HTMLElement;
|
||||||
|
newCard.dataset.toolName = tc.name;
|
||||||
|
existing.replaceWith(newCard);
|
||||||
|
} else {
|
||||||
|
// 没找到 running 的卡片,直接追加
|
||||||
|
appendToolCallCardToPlaceholder(tc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function appendAssistantPlaceholder(): void {
|
export function appendAssistantPlaceholder(): void {
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
div.className = 'message assistant loading';
|
div.className = 'message assistant loading';
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import { detectLanguage, truncate, escapeHtml, formatSize } from '../utils/utils
|
|||||||
import { getSelectedModel, isThinkEnabled, isVisionAvailable, isToolCallingSupported } from './model-bar.js';
|
import { getSelectedModel, isThinkEnabled, isVisionAvailable, isToolCallingSupported } from './model-bar.js';
|
||||||
import {
|
import {
|
||||||
renderMessages, clearMessagesDOM, appendAssistantPlaceholder, appendSystemMessage,
|
renderMessages, clearMessagesDOM, appendAssistantPlaceholder, appendSystemMessage,
|
||||||
updateLastAssistantMessage, clearMessages, safeMarkdown, enableAutoScroll, updateTotalTokens
|
updateLastAssistantMessage, clearMessages, safeMarkdown, enableAutoScroll, updateTotalTokens,
|
||||||
|
appendToolCallCardToPlaceholder, updateToolCallCardInPlaceholder
|
||||||
} from './chat-area.js';
|
} from './chat-area.js';
|
||||||
import { showToast } from './toast.js';
|
import { showToast } from './toast.js';
|
||||||
import { ChatDB } from '../db/chat-db.js';
|
import { ChatDB } from '../db/chat-db.js';
|
||||||
@@ -872,14 +873,23 @@ async function sendMessageWithAgentLoop(text: string, currentSession: ChatSessio
|
|||||||
assistantContent = content;
|
assistantContent = content;
|
||||||
updateLastAssistantMessage(assistantContent, thinkContent || null, null);
|
updateLastAssistantMessage(assistantContent, thinkContent || null, null);
|
||||||
},
|
},
|
||||||
onToolCallStart: (_call) => {
|
onToolCallStart: (call) => {
|
||||||
// 实时更新已由 agent-engine 的 callbacks 处理
|
appendToolCallCardToPlaceholder({
|
||||||
|
name: call.function.name, arguments: call.function.arguments,
|
||||||
|
result: null, status: 'running', timestamp: Date.now()
|
||||||
|
});
|
||||||
},
|
},
|
||||||
onToolCallResult: (_name, _result, _call) => {
|
onToolCallResult: (name, result, call) => {
|
||||||
// 工具结果实时更新
|
updateToolCallCardInPlaceholder({
|
||||||
|
name, arguments: call.function.arguments,
|
||||||
|
result, status: result.success ? 'success' : 'error', timestamp: Date.now()
|
||||||
|
});
|
||||||
},
|
},
|
||||||
onToolCallError: (_name, _error, _call) => {
|
onToolCallError: (name, error, call) => {
|
||||||
// 工具错误实时更新
|
updateToolCallCardInPlaceholder({
|
||||||
|
name, arguments: call.function.arguments,
|
||||||
|
result: { success: false, error }, status: 'error', timestamp: Date.now()
|
||||||
|
});
|
||||||
},
|
},
|
||||||
onConfirmTool: async (call) => {
|
onConfirmTool: async (call) => {
|
||||||
return showToolConfirm(call);
|
return showToolConfirm(call);
|
||||||
|
|||||||
Reference in New Issue
Block a user