chatbot debug
This commit is contained in:
@@ -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, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
|
||||
// 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;
|
||||
};
|
||||
Reference in New Issue
Block a user