diff --git a/backend/utils/billing.py b/backend/utils/billing.py index 0ddba09c..c4ab9bbf 100644 --- a/backend/utils/billing.py +++ b/backend/utils/billing.py @@ -3,7 +3,7 @@ from typing import Dict, Optional, Tuple # Define subscription tiers and their monthly limits (in minutes) SUBSCRIPTION_TIERS = { - 'price_1RGJ9GG6l1KZGqIroxSqgphC': {'name': 'free', 'minutes': 1000000}, + 'price_1RGJ9GG6l1KZGqIroxSqgphC': {'name': 'free', 'minutes': 10}, 'price_1RGJ9LG6l1KZGqIrd9pwzeNW': {'name': 'base', 'minutes': 300}, # 100 hours = 6000 minutes 'price_1RGJ9JG6l1KZGqIrVUU4ZRv6': {'name': 'extra', 'minutes': 2400} # 100 hours = 6000 minutes } diff --git a/frontend/src/app/(dashboard)/agents/[threadId]/page.tsx b/frontend/src/app/(dashboard)/agents/[threadId]/page.tsx index 693d887e..bf48dbaa 100644 --- a/frontend/src/app/(dashboard)/agents/[threadId]/page.tsx +++ b/frontend/src/app/(dashboard)/agents/[threadId]/page.tsx @@ -243,6 +243,7 @@ export default function ThreadPage({ params }: { params: Promise } const messagesLoadedRef = useRef(false); const agentRunsCheckedRef = useRef(false); const previousAgentStatus = useRef('idle'); + const pollingIntervalRef = useRef(null); const handleProjectRenamed = useCallback((newName: string) => { setProjectName(newName); @@ -968,6 +969,62 @@ export default function ThreadPage({ params }: { params: Promise } } }, [projectName]); + // Set up polling for messages + useEffect(() => { + // Function to fetch messages + const fetchMessages = async () => { + if (!threadId) return; + + try { + console.log('[POLLING] Refetching messages...'); + const messagesData = await getMessages(threadId); + + if (messagesData) { + console.log(`[POLLING] Refetch completed with ${messagesData.length} messages`); + // Map API message type to UnifiedMessage type + const unifiedMessages = (messagesData || []) + .filter(msg => msg.type !== 'status') + .map((msg: ApiMessageType) => ({ + message_id: msg.message_id || null, + thread_id: msg.thread_id || threadId, + type: (msg.type || 'system') as UnifiedMessage['type'], + is_llm_message: Boolean(msg.is_llm_message), + content: msg.content || '', + metadata: msg.metadata || '{}', + created_at: msg.created_at || new Date().toISOString(), + updated_at: msg.updated_at || new Date().toISOString() + })); + + setMessages(unifiedMessages); + + // Only auto-scroll if not manually scrolled up + if (!userHasScrolled) { + scrollToBottom('smooth'); + } + } + } catch (error) { + console.error('[POLLING] Error fetching messages:', error); + } + }; + + // Start polling once initial load is complete + if (initialLoadCompleted.current && !pollingIntervalRef.current) { + // Initial fetch + fetchMessages(); + + // Set up interval (every 2 seconds) + pollingIntervalRef.current = setInterval(fetchMessages, 2000); + } + + // Clean up interval when component unmounts + return () => { + if (pollingIntervalRef.current) { + clearInterval(pollingIntervalRef.current); + pollingIntervalRef.current = null; + } + }; + }, [threadId, userHasScrolled, initialLoadCompleted]); + // Add another useEffect to ensure messages are refreshed when agent status changes to idle useEffect(() => { if (agentStatus === 'idle' && streamHookStatus !== 'streaming' && streamHookStatus !== 'connecting') {