chatbot debug

This commit is contained in:
hamid zarghami
2025-06-08 16:21:37 +03:30
parent 4ceb91466e
commit 9334e09ac6
14 changed files with 1585 additions and 3 deletions
+370
View File
@@ -0,0 +1,370 @@
import { useCallback, useRef, useState } from "react";
import { Socket } from "socket.io-client";
import { SOCKET_EVENTS } from "../constants";
import { Message } from "../types";
interface UseSocketHandlersProps {
socket: Socket | null;
setStatus: (status: string) => void;
setIsTyping: (isTyping: boolean) => void;
setIsProcessing: (isProcessing: boolean) => void;
addMessage: (message: Partial<Message>) => Message;
updateMessage: (id: string, updates: Partial<Message>) => void;
addSystemMessage: (text: string, type?: string) => void;
setCurrentSessionId: (sessionId: string | null) => void;
isProcessing: boolean;
}
export const useSocketHandlers = ({
socket,
setStatus,
setIsTyping,
setIsProcessing,
addMessage,
updateMessage,
addSystemMessage,
setCurrentSessionId,
isProcessing,
}: UseSocketHandlersProps) => {
// Keep track of the current streaming message
const currentStreamingMessageRef = useRef<{
id: string;
text: string;
} | null>(null);
const [streamingText, setStreamingText] = useState<string>("");
// Simple function to update streaming message content
const updateStreamingMessage = (id: string, text: string) => {
console.log(
`Updating streaming message ${id} with ${text.length} characters`
);
// Update the state for React
updateMessage(id, { text });
// Also try direct DOM update as a fallback
setTimeout(() => {
try {
const element = document.getElementById(`message-${id}`);
if (element) {
element.innerHTML = formatText(text);
}
} catch (e) {
console.error("Error updating DOM directly:", e);
}
}, 0);
};
const setupSocketHandlers = useCallback(() => {
if (!socket) return () => {};
// Connection events
const onConnect = () => {
console.log("🔌 Socket.IO connected!");
setStatus("متصل شد ✅");
addSystemMessage("اتصال برقرار شد", "success");
};
const onDisconnect = (reason: string) => {
console.log("🔌 Socket.IO disconnected:", reason);
setStatus("قطع ارتباط");
};
const onConnectionError = (error: any) => {
console.error("❌ Socket.IO connection error:", error);
addSystemMessage("خطا در اتصال", "error");
setStatus("خطا در اتصال");
};
// Authentication events
const onAuthenticated = (data: any) => {
console.log("✅ Authenticated:", data);
setStatus("احراز هویت شد ✅");
};
const onUnauthorized = (data: any) => {
console.error("❌ Unauthorized:", data);
addSystemMessage("خطا در احراز هویت", "error");
setStatus("خطا در احراز هویت");
};
// Session events
const onSessionCreated = (data: any) => {
console.log("✅ Session created:", data);
setCurrentSessionId(data.session.id);
addSystemMessage("جلسه ایجاد شد 🎉", "success");
setStatus("آماده برای گفتگو");
// Auto-join the created session
if (socket && data.session.id) {
const userId =
localStorage.getItem(import.meta.env.VITE_USER_ID_KEY || "user_id") ||
localStorage.getItem("userId") ||
"anonymous_user";
socket.emit(SOCKET_EVENTS.JOIN_CHAT, {
sessionId: data.session.id,
userId: userId,
});
}
};
const onChatJoined = (data: any) => {
console.log("✅ Joined chat:", data);
addSystemMessage("به چت متصل شدید", "success");
};
const onSessionError = (data: any) => {
console.error("❌ Session error:", data);
addSystemMessage("خطا در ایجاد جلسه", "error");
};
// Message events
const onMessageReceived = (data: any) => {
console.log("📩 Message received:", data);
// Message is already shown in UI when sent
};
// Bot response streaming
const onBotResponseStart = (data: any) => {
console.log("🤖 Bot response starting:", data);
// Reset streaming text
setStreamingText("");
// Create a new message and store its ID
const newMessage = addMessage({
text: "",
isUser: false,
streaming: true,
});
// Store the message ID and initial text in ref
currentStreamingMessageRef.current = {
id: newMessage.id,
text: "",
};
console.log("Created new streaming message with ID:", newMessage.id);
};
const onBotResponseChunk = (data: any) => {
console.log(
"🤖 Bot response chunk received, data length:",
data?.data?.length || 0
);
if (data.data && currentStreamingMessageRef.current) {
try {
// Update the text in our ref and state
const newText = currentStreamingMessageRef.current.text + data.data;
currentStreamingMessageRef.current.text = newText;
// Update state to trigger re-render
setStreamingText(newText);
// Update the message content
updateStreamingMessage(
currentStreamingMessageRef.current.id,
newText
);
} catch (error) {
console.error("Error processing bot response chunk:", error);
}
} else {
console.warn("Received chunk but no active streaming message found", {
hasData: !!data.data,
hasCurrentMessage: !!currentStreamingMessageRef.current,
data,
});
}
};
const onBotResponseEnd = (data: any) => {
console.log("🤖 Bot response ended:", data);
if (currentStreamingMessageRef.current) {
// Make sure we have the final text
const finalText = currentStreamingMessageRef.current.text;
const messageId = currentStreamingMessageRef.current.id;
console.log(
`Finalizing message ${messageId} with text length: ${finalText.length}`
);
// Final update to the message content
updateStreamingMessage(messageId, finalText);
// Finalize the message by removing streaming flag
setTimeout(() => {
updateMessage(messageId, {
streaming: false,
});
}, 100);
// Clear the ref and state
setStreamingText("");
currentStreamingMessageRef.current = null;
}
setIsTyping(false);
setIsProcessing(false);
};
const onBotResponse = (data: any) => {
console.log("🤖 Bot response (non-streaming):", data);
if (data.message && data.message.content) {
console.log(
`Adding non-streaming message: "${data.message.content.substring(
0,
50
)}${data.message.content.length > 50 ? "..." : ""}"`
);
addMessage({
text: data.message.content,
isUser: false,
metadata: data.message.metadata,
});
} else {
console.warn("Received bot_response event with no content", data);
}
setIsTyping(false);
setIsProcessing(false);
};
// Typing events
const onTypingStart = (data: any) => {
if (data.type === "bot") {
setIsTyping(true);
setStatus("در حال پاسخ...");
}
};
const onTypingStop = (data: any) => {
if (data.type === "bot") {
setIsTyping(false);
setStatus("آماده برای گفتگو");
}
};
// User status events
const onUserJoined = (data: any) => {
console.log("👤 User joined:", data);
addSystemMessage(`کاربر به چت پیوست`, "info");
};
const onUserLeft = (data: any) => {
console.log("👤 User left:", data);
addSystemMessage(`کاربر چت را ترک کرد`, "info");
};
// Error events
const onError = (data: any) => {
console.error("❌ Socket.IO error:", data);
addSystemMessage(data.message || "خطا در پردازش", "error");
setIsProcessing(false);
setIsTyping(false);
};
const onMessageError = (data: any) => {
console.error("❌ Message error:", data);
addSystemMessage(data.message || "خطا در ارسال پیام", "error");
setIsProcessing(false);
setIsTyping(false);
};
// Register event handlers
socket.on(SOCKET_EVENTS.CONNECT, onConnect);
socket.on(SOCKET_EVENTS.DISCONNECT, onDisconnect);
socket.on(SOCKET_EVENTS.CONNECTION_ERROR, onConnectionError);
socket.on(SOCKET_EVENTS.AUTHENTICATED, onAuthenticated);
socket.on(SOCKET_EVENTS.UNAUTHORIZED, onUnauthorized);
socket.on(SOCKET_EVENTS.SESSION_CREATED, onSessionCreated);
socket.on(SOCKET_EVENTS.CHAT_JOINED, onChatJoined);
socket.on(SOCKET_EVENTS.SESSION_ERROR, onSessionError);
socket.on(SOCKET_EVENTS.MESSAGE_RECEIVED, onMessageReceived);
socket.on(SOCKET_EVENTS.BOT_RESPONSE_START, onBotResponseStart);
socket.on(SOCKET_EVENTS.BOT_RESPONSE_CHUNK, onBotResponseChunk);
socket.on(SOCKET_EVENTS.BOT_RESPONSE_END, onBotResponseEnd);
socket.on(SOCKET_EVENTS.BOT_RESPONSE, onBotResponse);
socket.on(SOCKET_EVENTS.TYPING_START, onTypingStart);
socket.on(SOCKET_EVENTS.TYPING_STOP, onTypingStop);
socket.on(SOCKET_EVENTS.USER_JOINED, onUserJoined);
socket.on(SOCKET_EVENTS.USER_LEFT, onUserLeft);
socket.on(SOCKET_EVENTS.ERROR, onError);
socket.on(SOCKET_EVENTS.MESSAGE_ERROR, onMessageError);
socket.on(SOCKET_EVENTS.WEBSOCKET_ERROR, onError);
// Return cleanup function
return () => {
socket.off(SOCKET_EVENTS.CONNECT, onConnect);
socket.off(SOCKET_EVENTS.DISCONNECT, onDisconnect);
socket.off(SOCKET_EVENTS.CONNECTION_ERROR, onConnectionError);
socket.off(SOCKET_EVENTS.AUTHENTICATED, onAuthenticated);
socket.off(SOCKET_EVENTS.UNAUTHORIZED, onUnauthorized);
socket.off(SOCKET_EVENTS.SESSION_CREATED, onSessionCreated);
socket.off(SOCKET_EVENTS.CHAT_JOINED, onChatJoined);
socket.off(SOCKET_EVENTS.SESSION_ERROR, onSessionError);
socket.off(SOCKET_EVENTS.MESSAGE_RECEIVED, onMessageReceived);
socket.off(SOCKET_EVENTS.BOT_RESPONSE_START, onBotResponseStart);
socket.off(SOCKET_EVENTS.BOT_RESPONSE_CHUNK, onBotResponseChunk);
socket.off(SOCKET_EVENTS.BOT_RESPONSE_END, onBotResponseEnd);
socket.off(SOCKET_EVENTS.BOT_RESPONSE, onBotResponse);
socket.off(SOCKET_EVENTS.TYPING_START, onTypingStart);
socket.off(SOCKET_EVENTS.TYPING_STOP, onTypingStop);
socket.off(SOCKET_EVENTS.USER_JOINED, onUserJoined);
socket.off(SOCKET_EVENTS.USER_LEFT, onUserLeft);
socket.off(SOCKET_EVENTS.ERROR, onError);
socket.off(SOCKET_EVENTS.MESSAGE_ERROR, onMessageError);
socket.off(SOCKET_EVENTS.WEBSOCKET_ERROR, onError);
// Clear streaming message ref on cleanup
currentStreamingMessageRef.current = null;
setStreamingText("");
};
}, [
socket,
setStatus,
setIsTyping,
setIsProcessing,
addMessage,
updateMessage,
addSystemMessage,
setCurrentSessionId,
streamingText, // Add dependency to ensure handler is updated when streaming text changes
]);
return { setupSocketHandlers, streamingText };
};
// Helper function to format text with markdown-like syntax
const formatText = (text: string) => {
if (!text) return "";
// Clean the text first to avoid HTML injection
const cleanedText = text
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
// Format with markdown-like syntax and handle newlines properly
const formattedText = cleanedText
// Bold text
.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>")
// Italic text
.replace(/\*(.*?)\*/g, "<em>$1</em>")
// Line breaks - ensure they work properly with proper spacing
.replace(/\n/g, "<br class='message-line-break' />")
// Links (optional)
.replace(
/\[([^\]]+)\]\(([^)]+)\)/g,
'<a href="$2" target="_blank" rel="noopener noreferrer" class="text-blue-500 underline">$1</a>'
);
return formattedText;
};