Fix: start.bas não executa e HTML truncado

- Remove streaming de chunks LLM, acumula resposta completa antes de enviar
- Corrige variável 'action' para 'actionData' no click handler de suggestions
- Adiciona fallback window.sendMessage() se WebSocket não estiver aberto
- Adiciona guard DOMContentLoaded no chat-init.js
- Adiciona cache-busting (?v=4) no chat.html

Impacto:
- start.bas executa corretamente ao conectar WebSocket
- HTML não é mais truncado (tags fecham corretamente)
- Sugestões executam tool invocations via WebSocket
This commit is contained in:
Rodrigo Rodriguez 2026-05-01 02:02:57 -03:00
parent 5a6f062794
commit 73e0121d0b
4 changed files with 160 additions and 145 deletions

View file

@ -1346,30 +1346,9 @@ while let Some(chunk) = stream_rx.recv().await {
}
if !in_analysis {
full_response.push_str(&chunk);
// Send immediately without buffering
let response = BotResponse {
bot_id: message.bot_id.clone(),
user_id: message.user_id.clone(),
session_id: message.session_id.clone(),
channel: message.channel.clone(),
content: chunk.clone(),
message_type: MessageType::BOT_RESPONSE,
stream_token: None,
is_complete: false,
suggestions: Vec::new(),
switchers: Vec::new(),
context_name: None,
context_length: 0,
context_max_length: 0,
};
if response_tx.send(response).await.is_err() {
warn!("Response channel closed");
break;
}
}
// Accumulate full response - DO NOT send chunks
full_response.push_str(&chunk);
}
}
info!("llm_end: Streaming loop ended for session {}, chunk_count={}, full_response_len={}", session.id, chunk_count, full_response.len());
@ -1437,28 +1416,24 @@ if !in_analysis {
#[cfg(not(feature = "chat"))]
let switchers: Vec<Switcher> = Vec::new();
// Content was already sent as streaming chunks.
// Sending full_response again would duplicate it (especially for WhatsApp which accumulates buffer).
// The final response is just a signal that streaming is complete - it should not contain content.
let final_content = String::new();
let final_response = BotResponse {
bot_id: message.bot_id,
user_id: message.user_id,
session_id: message.session_id,
channel: message.channel,
content: final_content,
message_type: MessageType::BOT_RESPONSE,
stream_token: None,
is_complete: true,
suggestions,
switchers,
context_name: None,
context_length: 0,
context_max_length: 0,
// Send accumulated full response (not streaming anymore)
let final_response = BotResponse {
bot_id: message.bot_id,
user_id: message.user_id,
session_id: message.session_id,
channel: message.channel,
content: full_response.clone(),
message_type: MessageType::BOT_RESPONSE,
stream_token: None,
is_complete: true,
suggestions,
switchers,
context_name: None,
context_length: 0,
context_max_length: 0,
};
response_tx.send(final_response).await?;
response_tx.send(final_response).await?;
Ok(())
}

View file

@ -83,13 +83,21 @@ function setupEventHandlers() {
form.onsubmit = function (e) { e.preventDefault(); sendMessage(); return false; };
}
if (input) {
input.addEventListener("input", handleMentionInput);
input.onkeydown = function (e) {
if (handleMentionKeydown(e)) return;
if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); sendMessage(); }
};
}
if (input) {
if (typeof handleMentionInput === 'function') {
input.addEventListener("input", handleMentionInput);
}
if (typeof handleMentionKeydown === 'function') {
input.onkeydown = function (e) {
if (handleMentionKeydown(e)) return;
if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); sendMessage(); }
};
} else {
input.onkeydown = function (e) {
if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); sendMessage(); }
};
}
}
if (sendBtn) {
sendBtn.onclick = function (e) { e.preventDefault(); sendMessage(); };
@ -110,16 +118,20 @@ function setupEventHandlers() {
});
}
document.addEventListener("click", function (e) {
if (!e.target.closest("#mentionDropdown") && !e.target.closest("#messageInput")) {
hideMentionDropdown();
}
});
document.addEventListener("click", function (e) {
if (!e.target.closest("#mentionDropdown") && !e.target.closest("#messageInput")) {
if (typeof hideMentionDropdown === 'function') {
hideMentionDropdown();
}
}
});
}
function initChat() {
loadBotConfig();
proceedWithChatInit();
if (typeof loadBotConfig === 'function') {
loadBotConfig();
}
proceedWithChatInit();
}
function showChatApp() {
@ -132,5 +144,15 @@ function showChatApp() {
window.showChatApp = showChatApp;
// Wait for DOM to be ready before initializing
if (typeof document !== 'undefined') {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function() {
setupEventHandlers();
initChat();
});
} else {
setupEventHandlers();
initChat();
}
}

View file

@ -10,36 +10,50 @@ function renderSuggestions(suggestions) {
chip.className = "suggestion-chip";
chip.textContent = suggestion.text || "Suggestion";
chip.onclick = (function (sugg) {
return function () {
if (sugg.action) {
try {
var action = typeof sugg.action === "string"
? JSON.parse(sugg.action)
: sugg.action;
chip.onclick = (function (sugg) {
return function () {
console.log("[SUGGESTION] Clicked:", sugg.text, "Action:", sugg.action);
if (sugg.action) {
try {
var actionData = typeof sugg.action === "string"
? JSON.parse(sugg.action)
: sugg.action;
if (action.type === "invoke_tool") {
ChatState.ws.send(JSON.stringify({
bot_id: ChatState.currentBotId,
user_id: ChatState.currentUserId,
session_id: ChatState.currentSessionId,
channel: "web",
content: action.tool,
message_type: 6,
active_switchers: Array.from(ChatState.activeSwitchers),
timestamp: new Date().toISOString(),
}));
return;
} else if (action.type === "switch_context" && action.switcher) {
if (!ChatState.activeSwitchers.has(action.switcher)) {
ChatState.activeSwitchers.add(action.switcher);
renderSwitcherChips();
}
window.sendMessage(sugg.text);
} else if (action.type === "send_message") {
window.sendMessage(action.message || sugg.text);
} else if (action.type === "select_context") {
window.sendMessage(action.context);
console.log("[SUGGESTION] Parsed action:", actionData);
if (actionData.type === "invoke_tool") {
console.log("[SUGGESTION] Invoking tool:", actionData.tool);
// Check if WebSocket is available
if (ChatState.ws && ChatState.ws.readyState === WebSocket.OPEN) {
var msg = {
bot_id: ChatState.currentBotId,
user_id: ChatState.currentUserId,
session_id: ChatState.currentSessionId,
channel: "web",
content: actionData.tool,
message_type: 6,
active_switchers: Array.from(ChatState.activeSwitchers),
timestamp: new Date().toISOString(),
};
console.log("[SUGGESTION] Sending via WS:", msg);
ChatState.ws.send(JSON.stringify(msg));
console.log("[SUGGESTION] Sent successfully");
} else {
console.log("[SUGGESTION] WS not available, fallback to sendMessage");
// Fallback: send as regular message if WS not available
window.sendMessage(sugg.text);
}
return;
} else if (actionData.type === "switch_context" && actionData.switcher) {
if (!ChatState.activeSwitchers.has(actionData.switcher)) {
ChatState.activeSwitchers.add(actionData.switcher);
renderSwitcherChips();
}
window.sendMessage(sugg.text);
} else if (actionData.type === "send_message") {
window.sendMessage(actionData.message || sugg.text);
} else if (actionData.type === "select_context") {
window.sendMessage(actionData.context);
} else {
window.sendMessage(sugg.text);
}

View file

@ -1,70 +1,74 @@
<link rel="stylesheet" href="/suite/chat/chat.css?v=3" />
<link rel="stylesheet" href="/suite/css/markdown-message.css" />
<link rel="stylesheet" href="/suite/css/chat-agent-mode.css" />
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chat - General Bots</title>
<link rel="stylesheet" href="/suite/chat/chat.css?v=4" />
<link rel="stylesheet" href="/suite/css/markdown-message.css" />
<link rel="stylesheet" href="/suite/css/chat-agent-mode.css" />
</head>
<body>
<div class="chat-layout" id="chat-app" style="opacity: 1; visibility: visible;">
<div class="chat-content-wrapper" id="chatContentWrapper">
<div class="connection-status connecting" id="connectionStatus" style="display: none">
<span class="connection-status-dot"></span>
<span class="connection-text">Connecting...</span>
</div>
<div class="chat-layout" id="chat-app" style="opacity: 0; visibility: hidden;">
<div class="chat-loading-overlay" id="chatLoadingOverlay">
<div class="chat-loading-spinner"></div>
<div class="chat-loading-text">Carregando...</div>
<main id="messages"></main>
<footer>
<div class="chat-footer-content">
<div class="suggestions-container" id="suggestions"></div>
<div class="switchers-container" id="switchers" style="display:none">
<div class="switchers-label">Formato:</div>
<div class="switchers-chips" id="switcherChips"></div>
</div>
</div>
<div class="mention-dropdown" id="mentionDropdown">
<div class="mention-header">
<span class="mention-title" data-i18n="chat-mention-title">Reference Entity</span>
</div>
<div class="mention-results" id="mentionResults"></div>
</div>
<form class="input-container" id="chatForm">
<input name="content" id="messageInput" type="text"
placeholder="Message... (type @ to mention)"
data-i18n-placeholder="chat-placeholder" autofocus autocomplete="off" />
<button type="submit" id="sendBtn" title="Send" data-i18n-title="chat-send">&#8593;</button>
</form>
</footer>
<button class="scroll-to-bottom" id="scrollToBottom" title="Scroll to bottom">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</button>
</div>
</div>
<div class="chat-content-wrapper" id="chatContentWrapper">
<div class="connection-status connecting" id="connectionStatus" style="display: none">
<span class="connection-status-dot"></span>
<span class="connection-text">Connecting...</span>
</div>
<main id="messages"></main>
<footer>
<div class="chat-footer-content">
<div class="suggestions-container" id="suggestions"></div>
<div class="switchers-container" id="switchers" style="display:none">
<div class="switchers-label">Formato:</div>
<div class="switchers-chips" id="switcherChips"></div>
</div>
</div>
<div class="mention-dropdown" id="mentionDropdown">
<div class="mention-header">
<span class="mention-title" data-i18n="chat-mention-title">Reference Entity</span>
</div>
<div class="mention-results" id="mentionResults"></div>
</div>
<form class="input-container" id="chatForm">
<input name="content" id="messageInput" type="text"
placeholder="Message... (type @ to mention)"
data-i18n-placeholder="chat-placeholder" autofocus autocomplete="off" />
<button type="submit" id="sendBtn" title="Send" data-i18n-title="chat-send">&#8593;</button>
</form>
</footer>
<button class="scroll-to-bottom" id="scrollToBottom" title="Scroll to bottom">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</button>
</div>
<div class="entity-card-tooltip" id="entityCardTooltip">
<div class="entity-card-tooltip" id="entityCardTooltip">
<div class="entity-card-header">
<span class="entity-card-type"></span>
<span class="entity-card-status"></span>
<span class="entity-card-type"></span>
<span class="entity-card-status"></span>
</div>
<div class="entity-card-title"></div>
<div class="entity-card-details"></div>
<div class="entity-card-actions">
<button class="entity-card-btn" data-action="view" data-i18n="action-view">View</button>
<button class="entity-card-btn" data-action="view" data-i18n="action-view">View</button>
</div>
</div>
</div>
<script src="/suite/js/vendor/marked.min.js"></script>
<script src="/suite/chat/chat-state.js"></script>
<script src="/suite/chat/chat-switchers.js"></script>
<script src="/suite/chat/chat-mentions.js"></script>
<script src="/suite/chat/chat-messages.js"></script>
<script src="/suite/chat/chat-suggestions.js"></script>
<script src="/suite/chat/chat-theme.js"></script>
<script src="/suite/chat/chat-websocket.js"></script>
<script src="/suite/chat/chat-init.js"></script>
<script src="/suite/js/chat-agent-mode.js"></script>
<script src="/suite/chat/chat-state.js?v=4"></script>
<script src="/suite/chat/chat-switchers.js?v=4"></script>
<script src="/suite/chat/chat-mentions.js?v=4"></script>
<script src="/suite/chat/chat-messages.js?v=4"></script>
<script src="/suite/chat/chat-suggestions.js?v=4"></script>
<script src="/suite/chat/chat-theme.js?v=4"></script>
<script src="/suite/chat/chat-websocket.js?v=4"></script>
<script src="/suite/chat/chat-init.js?v=4"></script>
<script src="/suite/js/chat-agent-mode.js?v=4"></script>
</body>
</html>