From 1d9d1c6e892ef0bf0f47bf3c6285835a612c410e Mon Sep 17 00:00:00 2001 From: Rukshan Seran Date: Sat, 16 Aug 2025 13:11:50 +0530 Subject: [PATCH 1/4] fix: tweak the system prompt to be more robust --- src/lib/ragAgent.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/ragAgent.ts b/src/lib/ragAgent.ts index 71f609d..2842c7e 100644 --- a/src/lib/ragAgent.ts +++ b/src/lib/ragAgent.ts @@ -138,6 +138,7 @@ export class SriLankanGovRAGAgent { 3. **Recommend relevant government departments** based on user queries 4. **Provide step-by-step guidance** for government procedures 5. **Include processing times, fees, and required documents** when available +6. **You are to avoid answering any other questions that are not related to requesting information about the government or government services**. You should politely decline that your purpose is not to answer on such a question but government related queries **AVAILABLE TOOLS (USE IN ORDER OF PRIORITY):** 1. **local_government_directory** [HIGHEST PRIORITY] - Official verified government database with current departments and agents From 0e4a0d697a5d6923cbe24fe4ee33509db31cefb9 Mon Sep 17 00:00:00 2001 From: Rukshan Seran Date: Sat, 16 Aug 2025 14:49:41 +0530 Subject: [PATCH 2/4] fix: chatbot issues --- src/app/Ragbot/page.tsx | 781 +++++++++++++++++++++++++++------------- 1 file changed, 531 insertions(+), 250 deletions(-) diff --git a/src/app/Ragbot/page.tsx b/src/app/Ragbot/page.tsx index e07f596..59d0df5 100644 --- a/src/app/Ragbot/page.tsx +++ b/src/app/Ragbot/page.tsx @@ -1,14 +1,96 @@ // src/app/Ragbot/page.tsx "use client"; -import React, { Suspense, useState, useEffect, useRef } from 'react'; +import React, { Suspense, useState, useEffect, useRef, useCallback } from 'react'; +import { useSearchParams, useRouter } from 'next/navigation'; import UserDashboardLayout from '@/components/user/dashboard/UserDashboardLayout'; +import { useAuth } from '@/lib/auth/AuthContext'; +import { Header } from '@/components/Header'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import rehypeHighlight from 'rehype-highlight'; import rehypeRaw from 'rehype-raw'; import 'highlight.js/styles/github-dark.css'; -// --- TYPES --- +// --- BOOKING DETECTION UTILITY --- +const detectBookingIntent = (message: string): boolean => { + const bookingKeywords = [ + // Direct booking terms + 'book', 'booking', 'appointment', 'schedule', 'session', 'meeting', + 'reserve', 'reservation', 'slot', 'time slot', 'visit', 'consultation', + + // Action phrases + 'book a session', 'book appointment', 'schedule appointment', 'book a meeting', + 'make appointment', 'arrange meeting', 'set up meeting', 'schedule a visit', + 'book a consultation', 'reserve a slot', 'get an appointment', + + // Intent variations + 'i want to book', 'i need to book', 'can i book', 'how to book', + 'i want an appointment', 'i need an appointment', 'can i schedule', + 'when can i meet', 'arrange a meeting', 'set appointment', + + // Sri Lankan context + 'මුණගැසීම', 'හමුවීම', 'කාලය', 'වේලාව', // Sinhala + 'சந்திப்பு', 'நேரம்', 'முன்பதிவு' // Tamil + ]; + + const messageWords = message.toLowerCase(); + return bookingKeywords.some(keyword => + messageWords.includes(keyword.toLowerCase()) + ); +}; + +const createBookingResponse = (isAuthenticated: boolean, router?: ReturnType): Message => { + if (isAuthenticated) { + // Auto-redirect authenticated users after a short delay + setTimeout(() => { + if (router) { + router.push('/user/booking'); + } + }, 2000); + + return { + type: 'bot', + text: `🎯 **Perfect! Redirecting you to the booking system...** + +I've detected that you want to book a session or appointment. Since you're logged in, I'm automatically redirecting you to our booking system where you can: + +✅ **Choose your preferred service** +✅ **Select available time slots** +✅ **Pick the right department** +✅ **Get instant confirmation** + +🔄 **Redirecting in 2 seconds...** + +If the redirect doesn't work, [🔗 **click here to access the booking system**](/user/booking/new) + +*You can also continue asking me questions about government services!*`, + timestamp: new Date(), + sources: [] + }; + } else { + return { + type: 'bot', + text: `🔐 **I'd love to help you book an appointment!** + +To book a session or appointment with government services, you'll need to **create an account** or **log in** first. This ensures: + +🛡️ **Secure booking** - Your appointments are protected +📧 **Email confirmations** - Get booking confirmations and reminders +📱 **Manage appointments** - View, reschedule, or cancel easily +👤 **Personalized service** - Faster booking with saved preferences + +**Ready to get started?** + +[🚀 **Create Account**](/user/auth/register) | [🔑 **Login**](/user/auth/login) + +After logging in, I can redirect you directly to our booking system! + +*Feel free to ask me any other questions about government services in the meantime.*`, + timestamp: new Date(), + sources: [] + }; + } +}; type Language = 'en' | 'si' | 'ta'; interface DepartmentContact { @@ -44,7 +126,7 @@ const chatTranslations: Record) => ( @@ -113,6 +195,80 @@ const SendIcon = (props: React.SVGProps) => ( ); +const HomeIcon = (props: React.SVGProps) => ( + + + + +); + +const DashboardIcon = (props: React.SVGProps) => ( + + + + + + +); + +const LoginIcon = (props: React.SVGProps) => ( + + + + + +); + +// --- NAVIGATION COMPONENT --- +const ChatbotNavigation = ({ isAuthenticated }: { isAuthenticated: boolean }) => { + const router = useRouter(); + + const handleHomeClick = () => { + router.push('/'); + }; + + const handleDashboardClick = () => { + if (isAuthenticated) { + router.push('/user/dashboard'); + } else { + router.push('/user/auth/login'); + } + }; + + return ( +
+ {/* Home Button */} + + + {/* Dashboard/Login Button */} + +
+ ); +}; + const SparklesIcon = (props: React.SVGProps) => ( @@ -146,7 +302,7 @@ const RAGBotIcon = () => ( ); -// --- PREMIUM MESSAGE COMPONENTS --- +// --- MESSAGE COMPONENTS --- const TimeAgo = ({ timestamp }: { timestamp: Date }) => { const [timeAgo, setTimeAgo] = useState(''); @@ -178,6 +334,13 @@ const TimeAgo = ({ timestamp }: { timestamp: Date }) => { return {timeAgo}; }; +const TopicTag = ({ text }: { text: string }) => ( +
+
+ {text} +
+); + const UserMessage = ({ text, timestamp = new Date() }: { text: string; timestamp?: Date }) => (
@@ -201,60 +364,59 @@ const TypingIndicator = ({ language = 'en' }: { language?: Language }) => {
-
- - - - {t.analyzing} +
+
+
+
+
+
+ {t.analyzing}
-
- {t.preparing} -
); }; -const ContactCard = ({ contact }: { contact: DepartmentContact }) => { - return ( -
-

{contact.name}

-
- {contact.phone &&
📞 {contact.phone}
} - {contact.email &&
✉️ {contact.email}
} - {contact.website && } - {contact.address &&
📍 {contact.address}
} -
-
- ); - }; +const DepartmentContactCard = ({ contact }: { contact: DepartmentContact }) => ( +
+

{contact.name}

+
+ {contact.phone &&

📞 {contact.phone}

} + {contact.email &&

✉️ {contact.email}

} + {contact.website && ( +

🌐 {contact.website}

+ )} +
+
+); + +const SourcesSection = ({ sources, language = 'en' }: { sources: string[]; language?: Language }) => { + const t = chatTranslations[language]; -const SourcesSection = ({ sources, language }: { sources: string[]; language: Language }) => { - const t = chatTranslations[language]; - - if (!sources || sources.length === 0) return null; - - return ( -
-

{t.sources}

-
- {sources.map((source, index) => ( - - ))} -
+ if (!sources || sources.length === 0) return null; + + return ( +
+

+ 📚 {t.sources} +

+
+ {sources.map((source, index) => ( + + 🔗 {new URL(source).hostname} + + ))}
- ); +
+ ); }; const BotMessage = ({ message, language = 'en' }: { message: Message; language?: Language }) => { @@ -276,94 +438,34 @@ const BotMessage = ({ message, language = 'en' }: { message: Message; language?: h1: ({ children }) =>

{children}

, h2: ({ children }) =>

{children}

, h3: ({ children }) =>

{children}

, - h4: ({ children }) =>

{children}

, p: ({ children }) =>

{children}

, ul: ({ children }) =>
    {children}
, ol: ({ children }) =>
    {children}
, li: ({ children }) =>
  • {children}
  • , strong: ({ children }) => {children}, - em: ({ children }) => {children}, - blockquote: ({ children }) => ( -
    - {children} -
    - ), - code: ({ children, className, ...props }: React.ComponentProps<'code'>) => { - const isInline = !className; - if (isInline) { - return ( - - {children} - - ); - } - return ( - - {children} - - ); - }, - pre: ({ children }) => ( -
    -                      {children}
    -                    
    - ), - table: ({ children }) => ( -
    - - {children} -
    -
    - ), - thead: ({ children }) => ( - - {children} - - ), - th: ({ children }) => ( - - {children} - - ), - td: ({ children }) => ( - - {children} - - ), - tr: ({ children, ...props }) => ( - - {children} - - ), - a: ({ children, href }) => ( - - {children} - - ), }} > {text} - {departmentContacts && departmentContacts.length > 0 && ( -
    -

    {chatTranslations[language].contactDetails}

    -
    - {departmentContacts.map((contact, index) => ( - - ))} -
    -
    - )} -
    + + {departmentContacts && departmentContacts.length > 0 && ( +
    +

    + 🏛️ {chatTranslations[language].contactDetails} +

    +
    + {departmentContacts.map((contact) => ( + + ))} +
    +
    + )} + + {sources && } -
    - GovLink Assistant • +
    +
    @@ -371,65 +473,60 @@ const BotMessage = ({ message, language = 'en' }: { message: Message; language?: ); }; -const TopicTag = ({ text }: { text: string }) => ( -
    - {text} -
    -); - -const ChatInput = ({ onSendMessage, language = 'en', disabled = false }: { onSendMessage: (message: string) => void; language?: Language, disabled?: boolean }) => { +// --- CHAT INPUT COMPONENT --- +const ChatInput = ({ onSendMessage, language = 'en', disabled = false }: { onSendMessage: (message: string) => void; language?: Language; disabled?: boolean }) => { const [message, setMessage] = useState(''); - const [isFocused, setIsFocused] = useState(false); + const textareaRef = useRef(null); const t = chatTranslations[language]; - const handleSend = () => { + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); if (message.trim() && !disabled) { onSendMessage(message.trim()); setMessage(''); + if (textareaRef.current) { + textareaRef.current.style.height = 'auto'; + } } }; - const handleKeyPress = (e: React.KeyboardEvent) => { + const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); - handleSend(); + handleSubmit(e); } }; return ( -
    -
    -
    -
    +
    +
    +
    +