mirror of
https://github.com/zadam/trilium.git
synced 2025-10-20 15:19:01 +02:00
I think this is close
This commit is contained in:
parent
91c31d279b
commit
16604c85a6
@ -27,16 +27,6 @@ const TPL = `
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
<span class="ms-2">${t('ai_llm.agent.processing')}</span>
|
||||
<div class="tool-execution-info mt-2" style="display: none;">
|
||||
<!-- Tool execution status will be shown here -->
|
||||
<div class="tool-execution-status small p-2 bg-light rounded" style="max-height: 150px; overflow-y: auto;">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="bx bx-code-block text-primary me-2"></i>
|
||||
<span class="fw-bold">Tool Execution:</span>
|
||||
</div>
|
||||
<div class="tool-execution-steps ps-3 pt-1"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -98,8 +88,6 @@ export default class LlmChatPanel extends BasicWidget {
|
||||
private noteContextChatSendButton!: HTMLButtonElement;
|
||||
private chatContainer!: HTMLElement;
|
||||
private loadingIndicator!: HTMLElement;
|
||||
private toolExecutionInfo!: HTMLElement;
|
||||
private toolExecutionSteps!: HTMLElement;
|
||||
private sourcesList!: HTMLElement;
|
||||
private useAdvancedContextCheckbox!: HTMLInputElement;
|
||||
private showThinkingCheckbox!: HTMLInputElement;
|
||||
@ -157,8 +145,6 @@ export default class LlmChatPanel extends BasicWidget {
|
||||
this.noteContextChatSendButton = element.querySelector('.note-context-chat-send-button') as HTMLButtonElement;
|
||||
this.chatContainer = element.querySelector('.note-context-chat-container') as HTMLElement;
|
||||
this.loadingIndicator = element.querySelector('.loading-indicator') as HTMLElement;
|
||||
this.toolExecutionInfo = element.querySelector('.tool-execution-info') as HTMLElement;
|
||||
this.toolExecutionSteps = element.querySelector('.tool-execution-steps') as HTMLElement;
|
||||
this.sourcesList = element.querySelector('.sources-list') as HTMLElement;
|
||||
this.useAdvancedContextCheckbox = element.querySelector('.use-advanced-context-checkbox') as HTMLInputElement;
|
||||
this.showThinkingCheckbox = element.querySelector('.show-thinking-checkbox') as HTMLInputElement;
|
||||
@ -210,12 +196,16 @@ export default class LlmChatPanel extends BasicWidget {
|
||||
}
|
||||
|
||||
try {
|
||||
// Extract current tool execution steps if any exist
|
||||
const toolSteps = this.extractInChatToolSteps();
|
||||
|
||||
const dataToSave = {
|
||||
messages: this.messages,
|
||||
sessionId: this.sessionId
|
||||
sessionId: this.sessionId,
|
||||
toolSteps: toolSteps // Save tool execution steps alongside messages
|
||||
};
|
||||
|
||||
console.log(`Saving chat data with sessionId: ${this.sessionId}`);
|
||||
console.log(`Saving chat data with sessionId: ${this.sessionId} and ${toolSteps.length} tool steps`);
|
||||
|
||||
await this.onSaveData(dataToSave);
|
||||
} catch (error) {
|
||||
@ -223,6 +213,61 @@ export default class LlmChatPanel extends BasicWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract tool execution steps from the DOM that are within the chat flow
|
||||
*/
|
||||
private extractInChatToolSteps(): Array<{type: string, name?: string, content: string}> {
|
||||
const steps: Array<{type: string, name?: string, content: string}> = [];
|
||||
|
||||
// Look for tool execution in the chat flow
|
||||
const toolExecutionElement = this.noteContextChatMessages.querySelector('.chat-tool-execution');
|
||||
|
||||
if (toolExecutionElement) {
|
||||
// Find all tool step elements
|
||||
const stepElements = toolExecutionElement.querySelectorAll('.tool-step');
|
||||
|
||||
stepElements.forEach(stepEl => {
|
||||
const stepHtml = stepEl.innerHTML;
|
||||
|
||||
// Determine the step type based on icons or classes present
|
||||
let type = 'info';
|
||||
let name: string | undefined;
|
||||
let content = '';
|
||||
|
||||
if (stepHtml.includes('bx-code-block')) {
|
||||
type = 'executing';
|
||||
content = 'Executing tools...';
|
||||
} else if (stepHtml.includes('bx-terminal')) {
|
||||
type = 'result';
|
||||
// Extract the tool name from the step
|
||||
const nameMatch = stepHtml.match(/<span[^>]*>Tool: ([^<]+)<\/span>/);
|
||||
name = nameMatch ? nameMatch[1] : 'unknown';
|
||||
|
||||
// Extract the content from the div with class mt-1 ps-3
|
||||
const contentEl = stepEl.querySelector('.mt-1.ps-3');
|
||||
content = contentEl ? contentEl.innerHTML : '';
|
||||
} else if (stepHtml.includes('bx-error-circle')) {
|
||||
type = 'error';
|
||||
const nameMatch = stepHtml.match(/<span[^>]*>Tool: ([^<]+)<\/span>/);
|
||||
name = nameMatch ? nameMatch[1] : 'unknown';
|
||||
|
||||
const contentEl = stepEl.querySelector('.mt-1.ps-3.text-danger');
|
||||
content = contentEl ? contentEl.innerHTML : '';
|
||||
} else if (stepHtml.includes('bx-message-dots')) {
|
||||
type = 'generating';
|
||||
content = 'Generating response with tool results...';
|
||||
} else if (stepHtml.includes('bx-loader-alt')) {
|
||||
// Skip the initializing spinner
|
||||
return;
|
||||
}
|
||||
|
||||
steps.push({ type, name, content });
|
||||
});
|
||||
}
|
||||
|
||||
return steps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load saved chat data from the note attribute
|
||||
*/
|
||||
@ -246,6 +291,12 @@ export default class LlmChatPanel extends BasicWidget {
|
||||
this.addMessageToChat(role, message.content);
|
||||
});
|
||||
|
||||
// Restore tool execution steps if they exist
|
||||
if (savedData.toolSteps && Array.isArray(savedData.toolSteps) && savedData.toolSteps.length > 0) {
|
||||
console.log(`Restoring ${savedData.toolSteps.length} saved tool steps`);
|
||||
this.restoreInChatToolSteps(savedData.toolSteps);
|
||||
}
|
||||
|
||||
// Load session ID if available
|
||||
if (savedData.sessionId) {
|
||||
try {
|
||||
@ -261,7 +312,7 @@ export default class LlmChatPanel extends BasicWidget {
|
||||
await this.createChatSession();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`Error checking saved session ${savedData.sessionId}, will create new one`);
|
||||
console.log(`Error checking saved session ${savedData.sessionId}, creating a new one`);
|
||||
this.sessionId = null;
|
||||
await this.createChatSession();
|
||||
}
|
||||
@ -280,6 +331,54 @@ export default class LlmChatPanel extends BasicWidget {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore tool execution steps in the chat UI
|
||||
*/
|
||||
private restoreInChatToolSteps(steps: Array<{type: string, name?: string, content: string}>) {
|
||||
if (!steps || steps.length === 0) return;
|
||||
|
||||
// Create the tool execution element
|
||||
const toolExecutionElement = document.createElement('div');
|
||||
toolExecutionElement.className = 'chat-tool-execution mb-3';
|
||||
|
||||
// Insert before the assistant message if it exists
|
||||
const assistantMessage = this.noteContextChatMessages.querySelector('.assistant-message:last-child');
|
||||
if (assistantMessage) {
|
||||
this.noteContextChatMessages.insertBefore(toolExecutionElement, assistantMessage);
|
||||
} else {
|
||||
// Otherwise append to the end
|
||||
this.noteContextChatMessages.appendChild(toolExecutionElement);
|
||||
}
|
||||
|
||||
// Fill with tool execution content
|
||||
toolExecutionElement.innerHTML = `
|
||||
<div class="tool-execution-container p-2 rounded mb-2">
|
||||
<div class="tool-execution-header d-flex align-items-center justify-content-between mb-2">
|
||||
<div>
|
||||
<i class="bx bx-code-block text-primary me-2"></i>
|
||||
<span class="fw-bold">Tool Execution</span>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-link p-0 text-muted tool-execution-chat-clear" title="Clear tool execution history">
|
||||
<i class="bx bx-x"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="tool-execution-chat-steps">
|
||||
${this.renderToolStepsHtml(steps)}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Add event listener for the clear button
|
||||
const clearButton = toolExecutionElement.querySelector('.tool-execution-chat-clear');
|
||||
if (clearButton) {
|
||||
clearButton.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
toolExecutionElement.remove();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async refresh() {
|
||||
if (!this.isVisible()) {
|
||||
return;
|
||||
@ -731,32 +830,83 @@ export default class LlmChatPanel extends BasicWidget {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we already have an assistant message element to update
|
||||
const assistantElement = this.noteContextChatMessages.querySelector('.assistant-message:last-child .message-content');
|
||||
// Extract the tool execution steps and final response
|
||||
const toolSteps = this.extractToolExecutionSteps(assistantResponse);
|
||||
const finalResponseText = this.extractFinalResponse(assistantResponse);
|
||||
|
||||
if (assistantElement) {
|
||||
console.log(`[${logId}] Found existing assistant message element, updating content`);
|
||||
try {
|
||||
// Format markdown and update the element
|
||||
const formattedContent = this.formatMarkdown(assistantResponse);
|
||||
// Find existing assistant message or create one if needed
|
||||
let assistantElement = this.noteContextChatMessages.querySelector('.assistant-message:last-child .message-content');
|
||||
|
||||
// Ensure content is properly formatted
|
||||
if (!formattedContent || formattedContent.trim() === '') {
|
||||
console.warn(`[${logId}] Formatted content is empty, using original content`);
|
||||
assistantElement.textContent = assistantResponse;
|
||||
// First, check if we need to add the tool execution steps to the chat flow
|
||||
if (toolSteps.length > 0) {
|
||||
// Look for an existing tool execution element in the chat flow
|
||||
let toolExecutionElement = this.noteContextChatMessages.querySelector('.chat-tool-execution');
|
||||
|
||||
if (!toolExecutionElement) {
|
||||
// Create a new tool execution element in the chat flow
|
||||
// Place it right before the assistant message if it exists, or at the end of chat
|
||||
toolExecutionElement = document.createElement('div');
|
||||
toolExecutionElement.className = 'chat-tool-execution mb-3';
|
||||
|
||||
// If there's an assistant message, insert before it
|
||||
const assistantMessage = this.noteContextChatMessages.querySelector('.assistant-message:last-child');
|
||||
if (assistantMessage) {
|
||||
this.noteContextChatMessages.insertBefore(toolExecutionElement, assistantMessage);
|
||||
} else {
|
||||
assistantElement.innerHTML = formattedContent;
|
||||
// Otherwise append to the end
|
||||
this.noteContextChatMessages.appendChild(toolExecutionElement);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the tool execution content
|
||||
toolExecutionElement.innerHTML = `
|
||||
<div class="tool-execution-container p-2 rounded mb-2">
|
||||
<div class="tool-execution-header d-flex align-items-center justify-content-between mb-2">
|
||||
<div>
|
||||
<i class="bx bx-code-block text-primary me-2"></i>
|
||||
<span class="fw-bold">Tool Execution</span>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-link p-0 text-muted tool-execution-chat-clear" title="Clear tool execution history">
|
||||
<i class="bx bx-x"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="tool-execution-chat-steps">
|
||||
${this.renderToolStepsHtml(toolSteps)}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Add event listener for the clear button
|
||||
const clearButton = toolExecutionElement.querySelector('.tool-execution-chat-clear');
|
||||
if (clearButton) {
|
||||
clearButton.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
toolExecutionElement?.remove();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Now update or create the assistant message with the final response
|
||||
if (finalResponseText) {
|
||||
if (assistantElement) {
|
||||
console.log(`[${logId}] Found existing assistant message element, updating with final response`);
|
||||
try {
|
||||
// Format the final response with markdown
|
||||
const formattedResponse = this.formatMarkdown(finalResponseText);
|
||||
|
||||
// Update the content
|
||||
assistantElement.innerHTML = formattedResponse || '';
|
||||
|
||||
// Apply syntax highlighting to any code blocks in the updated content
|
||||
applySyntaxHighlight($(assistantElement as HTMLElement));
|
||||
|
||||
console.log(`[${logId}] Successfully updated existing element with ${formattedContent.length} chars of HTML`);
|
||||
console.log(`[${logId}] Successfully updated existing element with final response`);
|
||||
} catch (err) {
|
||||
console.error(`[${logId}] Error updating existing element:`, err);
|
||||
// Fallback to text content if HTML update fails
|
||||
try {
|
||||
assistantElement.textContent = assistantResponse;
|
||||
assistantElement.textContent = finalResponseText;
|
||||
console.log(`[${logId}] Fallback to text content successful`);
|
||||
} catch (fallbackErr) {
|
||||
console.error(`[${logId}] Even fallback update failed:`, fallbackErr);
|
||||
@ -765,7 +915,28 @@ export default class LlmChatPanel extends BasicWidget {
|
||||
} else {
|
||||
console.log(`[${logId}] No existing assistant message element found, creating new one`);
|
||||
try {
|
||||
this.addMessageToChat('assistant', assistantResponse);
|
||||
// Create new message element
|
||||
const messageElement = document.createElement('div');
|
||||
messageElement.className = 'chat-message assistant-message mb-3 d-flex';
|
||||
|
||||
const avatarElement = document.createElement('div');
|
||||
avatarElement.className = 'message-avatar d-flex align-items-center justify-content-center me-2 assistant-avatar';
|
||||
avatarElement.innerHTML = '<i class="bx bx-bot"></i>';
|
||||
|
||||
const contentElement = document.createElement('div');
|
||||
contentElement.className = 'message-content p-3 rounded flex-grow-1 assistant-content';
|
||||
|
||||
// Only show the final response in the message content
|
||||
contentElement.innerHTML = this.formatMarkdown(finalResponseText) || '';
|
||||
|
||||
messageElement.appendChild(avatarElement);
|
||||
messageElement.appendChild(contentElement);
|
||||
|
||||
this.noteContextChatMessages.appendChild(messageElement);
|
||||
|
||||
// Apply syntax highlighting to any code blocks in the message
|
||||
applySyntaxHighlight($(contentElement));
|
||||
|
||||
console.log(`[${logId}] Successfully added new assistant message`);
|
||||
} catch (err) {
|
||||
console.error(`[${logId}] Error adding new message:`, err);
|
||||
@ -780,7 +951,7 @@ export default class LlmChatPanel extends BasicWidget {
|
||||
<i class="bx bx-bot"></i>
|
||||
</div>
|
||||
<div class="message-content p-3 rounded flex-grow-1 assistant-content">
|
||||
${assistantResponse}
|
||||
${finalResponseText}
|
||||
</div>
|
||||
`;
|
||||
this.noteContextChatMessages.appendChild(emergencyElement);
|
||||
@ -790,6 +961,7 @@ export default class LlmChatPanel extends BasicWidget {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Always try to scroll to the latest content
|
||||
try {
|
||||
@ -802,6 +974,131 @@ export default class LlmChatPanel extends BasicWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render tool steps as HTML for display in chat
|
||||
*/
|
||||
private renderToolStepsHtml(steps: Array<{type: string, name?: string, content: string}>): string {
|
||||
if (!steps || steps.length === 0) return '';
|
||||
|
||||
let html = '';
|
||||
|
||||
steps.forEach(step => {
|
||||
let icon, labelClass, content;
|
||||
|
||||
switch (step.type) {
|
||||
case 'executing':
|
||||
icon = 'bx-code-block text-primary';
|
||||
labelClass = '';
|
||||
content = `<div class="d-flex align-items-center">
|
||||
<i class="bx ${icon} me-1"></i>
|
||||
<span>${step.content}</span>
|
||||
</div>`;
|
||||
break;
|
||||
|
||||
case 'result':
|
||||
icon = 'bx-terminal text-success';
|
||||
labelClass = 'fw-bold';
|
||||
content = `<div class="d-flex align-items-center">
|
||||
<i class="bx ${icon} me-1"></i>
|
||||
<span class="${labelClass}">Tool: ${step.name || 'unknown'}</span>
|
||||
</div>
|
||||
<div class="mt-1 ps-3">${step.content}</div>`;
|
||||
break;
|
||||
|
||||
case 'error':
|
||||
icon = 'bx-error-circle text-danger';
|
||||
labelClass = 'fw-bold text-danger';
|
||||
content = `<div class="d-flex align-items-center">
|
||||
<i class="bx ${icon} me-1"></i>
|
||||
<span class="${labelClass}">Tool: ${step.name || 'unknown'}</span>
|
||||
</div>
|
||||
<div class="mt-1 ps-3 text-danger">${step.content}</div>`;
|
||||
break;
|
||||
|
||||
case 'generating':
|
||||
icon = 'bx-message-dots text-info';
|
||||
labelClass = '';
|
||||
content = `<div class="d-flex align-items-center">
|
||||
<i class="bx ${icon} me-1"></i>
|
||||
<span>${step.content}</span>
|
||||
</div>`;
|
||||
break;
|
||||
|
||||
default:
|
||||
icon = 'bx-info-circle text-muted';
|
||||
labelClass = '';
|
||||
content = `<div class="d-flex align-items-center">
|
||||
<i class="bx ${icon} me-1"></i>
|
||||
<span>${step.content}</span>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
html += `<div class="tool-step my-1">${content}</div>`;
|
||||
});
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract tool execution steps from the response
|
||||
*/
|
||||
private extractToolExecutionSteps(content: string): Array<{type: string, name?: string, content: string}> {
|
||||
if (!content) return [];
|
||||
|
||||
const steps = [];
|
||||
|
||||
// Check for executing tools marker
|
||||
if (content.includes('[Executing tools...]')) {
|
||||
steps.push({
|
||||
type: 'executing',
|
||||
content: 'Executing tools...'
|
||||
});
|
||||
}
|
||||
|
||||
// Extract tool results with regex
|
||||
const toolResultRegex = /\[Tool: ([^\]]+)\]([\s\S]*?)(?=\[|$)/g;
|
||||
let match;
|
||||
|
||||
while ((match = toolResultRegex.exec(content)) !== null) {
|
||||
const toolName = match[1];
|
||||
const toolContent = match[2].trim();
|
||||
|
||||
steps.push({
|
||||
type: toolContent.includes('Error:') ? 'error' : 'result',
|
||||
name: toolName,
|
||||
content: toolContent
|
||||
});
|
||||
}
|
||||
|
||||
// Check for generating response marker
|
||||
if (content.includes('[Generating response with tool results...]')) {
|
||||
steps.push({
|
||||
type: 'generating',
|
||||
content: 'Generating response with tool results...'
|
||||
});
|
||||
}
|
||||
|
||||
return steps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the final response without tool execution steps
|
||||
*/
|
||||
private extractFinalResponse(content: string): string {
|
||||
if (!content) return '';
|
||||
|
||||
// Remove all tool execution markers and their content
|
||||
let finalResponse = content
|
||||
.replace(/\[Executing tools\.\.\.\]\n*/g, '')
|
||||
.replace(/\[Tool: [^\]]+\][\s\S]*?(?=\[|$)/g, '')
|
||||
.replace(/\[Generating response with tool results\.\.\.\]\n*/g, '');
|
||||
|
||||
// Trim any extra whitespace
|
||||
finalResponse = finalResponse.trim();
|
||||
|
||||
return finalResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle general errors in the send message flow
|
||||
*/
|
||||
@ -903,40 +1200,22 @@ export default class LlmChatPanel extends BasicWidget {
|
||||
|
||||
private showLoadingIndicator() {
|
||||
const logId = `ui-${Date.now()}`;
|
||||
console.log(`[${logId}] Showing loading indicator and preparing tool execution display`);
|
||||
console.log(`[${logId}] Showing loading indicator`);
|
||||
|
||||
// Ensure elements exist before trying to modify them
|
||||
if (!this.loadingIndicator || !this.toolExecutionInfo || !this.toolExecutionSteps) {
|
||||
console.error(`[${logId}] UI elements not properly initialized`);
|
||||
// Ensure the loading indicator element exists
|
||||
if (!this.loadingIndicator) {
|
||||
console.error(`[${logId}] Loading indicator element not properly initialized`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Force display of loading indicator
|
||||
// Show the loading indicator
|
||||
try {
|
||||
this.loadingIndicator.style.display = 'flex';
|
||||
|
||||
// Make sure tool execution info area is always visible even before we get the first event
|
||||
// This helps avoid the UI getting stuck in "Processing..." state
|
||||
this.toolExecutionInfo.style.display = 'block';
|
||||
|
||||
// Clear previous tool steps but add a placeholder
|
||||
this.toolExecutionSteps.innerHTML = `
|
||||
<div class="tool-step my-1">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="bx bx-loader-alt bx-spin text-primary me-1"></i>
|
||||
<span>Initializing...</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Force a UI update by accessing element properties
|
||||
// Force a UI update
|
||||
const forceUpdate = this.loadingIndicator.offsetHeight;
|
||||
|
||||
// Verify display states
|
||||
console.log(`[${logId}] Loading indicator display state: ${this.loadingIndicator.style.display}`);
|
||||
console.log(`[${logId}] Tool execution info display state: ${this.toolExecutionInfo.style.display}`);
|
||||
|
||||
console.log(`[${logId}] Loading indicator and tool execution area initialized`);
|
||||
console.log(`[${logId}] Loading indicator initialized`);
|
||||
} catch (err) {
|
||||
console.error(`[${logId}] Error showing loading indicator:`, err);
|
||||
}
|
||||
@ -944,47 +1223,24 @@ export default class LlmChatPanel extends BasicWidget {
|
||||
|
||||
private hideLoadingIndicator() {
|
||||
const logId = `ui-${Date.now()}`;
|
||||
console.log(`[${logId}] Hiding loading indicator and tool execution area`);
|
||||
console.log(`[${logId}] Hiding loading indicator`);
|
||||
|
||||
// Ensure elements exist before trying to modify them
|
||||
if (!this.loadingIndicator || !this.toolExecutionInfo) {
|
||||
console.error(`[${logId}] UI elements not properly initialized`);
|
||||
if (!this.loadingIndicator) {
|
||||
console.error(`[${logId}] Loading indicator element not properly initialized`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Properly reset DOM elements
|
||||
try {
|
||||
// First hide the tool execution info area
|
||||
this.toolExecutionInfo.style.display = 'none';
|
||||
// Hide just the loading indicator but NOT the tool execution info
|
||||
this.loadingIndicator.style.display = 'none';
|
||||
|
||||
// Force a UI update by accessing element properties
|
||||
const forceUpdate1 = this.toolExecutionInfo.offsetHeight;
|
||||
const forceUpdate = this.loadingIndicator.offsetHeight;
|
||||
|
||||
// Then hide the loading indicator
|
||||
this.loadingIndicator.style.display = 'none';
|
||||
|
||||
// Force another UI update
|
||||
const forceUpdate2 = this.loadingIndicator.offsetHeight;
|
||||
|
||||
// Verify display states immediately
|
||||
console.log(`[${logId}] Loading indicator display state: ${this.loadingIndicator.style.display}`);
|
||||
console.log(`[${logId}] Tool execution info display state: ${this.toolExecutionInfo.style.display}`);
|
||||
|
||||
// Add a delay to double-check that UI updates are complete
|
||||
setTimeout(() => {
|
||||
console.log(`[${logId}] Verification after hide timeout: loading indicator display=${this.loadingIndicator.style.display}, tool execution info display=${this.toolExecutionInfo.style.display}`);
|
||||
|
||||
// Force display none again in case something changed it
|
||||
if (this.loadingIndicator.style.display !== 'none') {
|
||||
console.log(`[${logId}] Loading indicator still visible after timeout, forcing hidden`);
|
||||
this.loadingIndicator.style.display = 'none';
|
||||
}
|
||||
|
||||
if (this.toolExecutionInfo.style.display !== 'none') {
|
||||
console.log(`[${logId}] Tool execution info still visible after timeout, forcing hidden`);
|
||||
this.toolExecutionInfo.style.display = 'none';
|
||||
}
|
||||
}, 100);
|
||||
// Tool execution info is now independent and may remain visible
|
||||
console.log(`[${logId}] Loading indicator hidden, tool execution info remains visible if needed`);
|
||||
} catch (err) {
|
||||
console.error(`[${logId}] Error hiding loading indicator:`, err);
|
||||
}
|
||||
@ -996,62 +1252,22 @@ export default class LlmChatPanel extends BasicWidget {
|
||||
private showToolExecutionInfo(toolExecutionData: any) {
|
||||
console.log(`Showing tool execution info: ${JSON.stringify(toolExecutionData)}`);
|
||||
|
||||
// Make sure tool execution info section is visible
|
||||
this.toolExecutionInfo.style.display = 'block';
|
||||
this.loadingIndicator.style.display = 'flex'; // Ensure loading indicator is shown during tool execution
|
||||
// We'll update the in-chat tool execution area in the updateStreamingUI method
|
||||
// This method is now just a legacy hook for the WebSocket handlers
|
||||
|
||||
// Create a new step element to show the tool being executed
|
||||
const stepElement = document.createElement('div');
|
||||
stepElement.className = 'tool-step my-1';
|
||||
|
||||
// Basic styling for the step
|
||||
let stepHtml = '';
|
||||
|
||||
if (toolExecutionData.action === 'start') {
|
||||
// Tool execution starting
|
||||
stepHtml = `
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="bx bx-play-circle text-primary me-1"></i>
|
||||
<span class="fw-bold">${this.escapeHtml(toolExecutionData.tool || 'Unknown tool')}</span>
|
||||
</div>
|
||||
<div class="tool-args small text-muted ps-3">
|
||||
${this.formatToolArgs(toolExecutionData.args || {})}
|
||||
</div>
|
||||
`;
|
||||
} else if (toolExecutionData.action === 'complete') {
|
||||
// Tool execution completed
|
||||
const resultPreview = this.formatToolResult(toolExecutionData.result);
|
||||
stepHtml = `
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="bx bx-check-circle text-success me-1"></i>
|
||||
<span>${this.escapeHtml(toolExecutionData.tool || 'Unknown tool')} completed</span>
|
||||
</div>
|
||||
${resultPreview ? `<div class="tool-result small ps-3 text-muted">${resultPreview}</div>` : ''}
|
||||
`;
|
||||
} else if (toolExecutionData.action === 'error') {
|
||||
// Tool execution error
|
||||
stepHtml = `
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="bx bx-error-circle text-danger me-1"></i>
|
||||
<span class="text-danger">${this.escapeHtml(toolExecutionData.tool || 'Unknown tool')} error</span>
|
||||
</div>
|
||||
<div class="tool-error small text-danger ps-3">
|
||||
${this.escapeHtml(toolExecutionData.error || 'Unknown error')}
|
||||
</div>
|
||||
`;
|
||||
// Make sure the loading indicator is shown during tool execution
|
||||
this.loadingIndicator.style.display = 'flex';
|
||||
}
|
||||
|
||||
if (stepHtml) {
|
||||
stepElement.innerHTML = stepHtml;
|
||||
this.toolExecutionSteps.appendChild(stepElement);
|
||||
/**
|
||||
* Show thinking state in the UI
|
||||
*/
|
||||
private showThinkingState(thinkingData: string) {
|
||||
// Thinking state is now updated via the in-chat UI in updateStreamingUI
|
||||
// This method is now just a legacy hook for the WebSocket handlers
|
||||
|
||||
// Scroll to bottom of tool execution steps
|
||||
this.toolExecutionSteps.scrollTop = this.toolExecutionSteps.scrollHeight;
|
||||
|
||||
console.log(`Added new tool execution step to UI`);
|
||||
} else {
|
||||
console.log(`No HTML generated for tool execution data:`, toolExecutionData);
|
||||
}
|
||||
// Show the loading indicator
|
||||
this.loadingIndicator.style.display = 'flex';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1081,49 +1297,6 @@ export default class LlmChatPanel extends BasicWidget {
|
||||
.join(', ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Format tool results for display
|
||||
*/
|
||||
private formatToolResult(result: any): string {
|
||||
if (result === undefined || result === null) return '';
|
||||
|
||||
// Try to format as JSON if it's an object
|
||||
if (typeof result === 'object') {
|
||||
try {
|
||||
// Get a preview of structured data
|
||||
const entries = Object.entries(result);
|
||||
if (entries.length === 0) return 'Empty result';
|
||||
|
||||
// Just show first 2 key-value pairs if there are many
|
||||
const preview = entries.slice(0, 2).map(([key, val]) => {
|
||||
let valPreview;
|
||||
if (typeof val === 'string') {
|
||||
valPreview = val.length > 30 ? `"${val.substring(0, 27)}..."` : `"${val}"`;
|
||||
} else if (Array.isArray(val)) {
|
||||
valPreview = `[${val.length} items]`;
|
||||
} else if (typeof val === 'object' && val !== null) {
|
||||
valPreview = '{...}';
|
||||
} else {
|
||||
valPreview = String(val);
|
||||
}
|
||||
return `${key}: ${valPreview}`;
|
||||
}).join(', ');
|
||||
|
||||
return entries.length > 2 ? `${preview}, ... (${entries.length} properties)` : preview;
|
||||
} catch (e) {
|
||||
return String(result).substring(0, 100) + (String(result).length > 100 ? '...' : '');
|
||||
}
|
||||
}
|
||||
|
||||
// For string results
|
||||
if (typeof result === 'string') {
|
||||
return result.length > 100 ? result.substring(0, 97) + '...' : result;
|
||||
}
|
||||
|
||||
// Default formatting
|
||||
return String(result).substring(0, 100) + (String(result).length > 100 ? '...' : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple HTML escaping for safer content display
|
||||
*/
|
||||
@ -1200,26 +1373,6 @@ export default class LlmChatPanel extends BasicWidget {
|
||||
return processedContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show thinking state in the UI
|
||||
*/
|
||||
private showThinkingState(thinkingData: string) {
|
||||
// Update the UI to show thinking indicator
|
||||
const thinking = typeof thinkingData === 'string' ? thinkingData : 'Thinking...';
|
||||
const toolExecutionStep = document.createElement('div');
|
||||
toolExecutionStep.className = 'tool-step my-1';
|
||||
toolExecutionStep.innerHTML = `
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="bx bx-bulb text-warning me-1"></i>
|
||||
<span>${this.escapeHtml(thinking)}</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
this.toolExecutionInfo.style.display = 'block';
|
||||
this.toolExecutionSteps.appendChild(toolExecutionStep);
|
||||
this.toolExecutionSteps.scrollTop = this.toolExecutionSteps.scrollHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate embedding providers configuration
|
||||
* Check if there are issues with the embedding providers that might affect LLM functionality
|
||||
|
@ -43,6 +43,55 @@
|
||||
border: 1px solid var(--subtle-border-color, var(--main-border-color));
|
||||
}
|
||||
|
||||
/* Tool Execution Styling */
|
||||
.tool-execution-info {
|
||||
margin-top: 0.75rem;
|
||||
margin-bottom: 1.5rem;
|
||||
border: 1px solid var(--subtle-border-color);
|
||||
border-radius: 0.5rem;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
|
||||
background-color: var(--main-background-color);
|
||||
/* Add a subtle transition effect */
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.tool-execution-status {
|
||||
background-color: var(--accented-background-color, rgba(0, 0, 0, 0.03)) !important;
|
||||
border-radius: 0 !important;
|
||||
padding: 0.5rem !important;
|
||||
max-height: 250px !important;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.tool-execution-status .d-flex {
|
||||
border-bottom: 1px solid var(--subtle-border-color);
|
||||
padding-bottom: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.tool-step {
|
||||
padding: 0.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
border-radius: 0.375rem;
|
||||
background-color: var(--main-background-color);
|
||||
border: 1px solid var(--subtle-border-color);
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.tool-step:hover {
|
||||
background-color: rgba(0, 0, 0, 0.01);
|
||||
}
|
||||
|
||||
.tool-step:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Make error text more visible */
|
||||
.text-danger {
|
||||
color: #dc3545 !important;
|
||||
}
|
||||
|
||||
/* Sources Styling */
|
||||
.sources-container {
|
||||
background-color: var(--accented-background-color, var(--main-background-color));
|
||||
@ -117,3 +166,30 @@
|
||||
padding: 1rem;
|
||||
color: var(--muted-text-color);
|
||||
}
|
||||
|
||||
/* Tool Execution in Chat Styling */
|
||||
.chat-tool-execution {
|
||||
padding: 0 0 0 36px; /* Aligned with message content, accounting for avatar width */
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tool-execution-container {
|
||||
background-color: var(--accented-background-color, rgba(245, 247, 250, 0.7));
|
||||
border: 1px solid var(--subtle-border-color);
|
||||
border-radius: 0.375rem;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||
overflow: hidden;
|
||||
max-width: calc(100% - 20px);
|
||||
}
|
||||
|
||||
.tool-execution-header {
|
||||
border-bottom: 1px solid var(--subtle-border-color);
|
||||
padding-bottom: 0.5rem;
|
||||
color: var(--muted-text-color);
|
||||
}
|
||||
|
||||
.tool-execution-chat-steps {
|
||||
padding: 0.5rem;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user