'use client'; import React, { Suspense, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Button } from '@/components/ui/button'; import { ChevronRight, ChevronDown, ExternalLink } from 'lucide-react'; import OverviewCards from './components/overview-cards/OverviewCards'; import MonitoringFilters from './components/filters/MonitoringFilters'; import { ExportDropdown } from './components/ExportDropdown'; import { useMonitoringFilters } from './hooks/useMonitoringFilters'; import { useMonitoringData } from './hooks/useMonitoringData'; import { MessageDetailsCard } from './components/MessageDetailsCard'; import { MessageContentRenderer } from './components/MessageContentRenderer'; import { MessageDetails } from './types/monitoring'; import { httpClient } from '@/app/infra/http/HttpClient'; import { LoadingSpinner, LoadingPage } from '@/components/ui/loading-spinner'; interface RawMessageData { id: string; timestamp: string; bot_id: string; bot_name: string; pipeline_id: string; pipeline_name: string; message_content: string; session_id: string; status: string; level: string; platform: string; user_id: string; runner_name: string; variables: Record; } interface RawLLMCallData { id: string; timestamp: string; model_name: string; status: string; duration: number; error_message: string | null; input_tokens: number; output_tokens: number; total_tokens: number; } interface RawLLMStatsData { total_calls: number; total_input_tokens: number; total_output_tokens: number; total_tokens: number; total_duration_ms: number; average_duration_ms: number; } interface RawErrorData { id: string; timestamp: string; error_type: string; error_message: string; stack_trace: string | null; } function MonitoringPageContent() { const { t } = useTranslation(); const { filterState, setSelectedBots, setSelectedPipelines, setTimeRange } = useMonitoringFilters(); const { data, loading, refetch } = useMonitoringData(filterState); const [expandedMessageId, setExpandedMessageId] = useState( null, ); const [messageDetails, setMessageDetails] = useState< Record >({}); const [loadingDetails, setLoadingDetails] = useState>( {}, ); // State for expanded errors const [expandedErrorId, setExpandedErrorId] = useState(null); // State for controlled tabs const [activeTab, setActiveTab] = useState('messages'); // Function to jump to a message record const jumpToMessage = async (messageId: string) => { setActiveTab('messages'); // Small delay to ensure tab switch completes setTimeout(() => { toggleMessageExpand(messageId); }, 100); }; const toggleMessageExpand = async (messageId: string) => { if (expandedMessageId === messageId) { // Collapse setExpandedMessageId(null); } else { // Expand setExpandedMessageId(messageId); // Fetch details if not already loaded if (!messageDetails[messageId]) { setLoadingDetails({ ...loadingDetails, [messageId]: true }); try { // httpClient.get() returns the inner data directly (response.data.data) const result = await httpClient.get<{ message_id: string; found: boolean; message: RawMessageData | null; llm_calls: RawLLMCallData[]; llm_stats: RawLLMStatsData; errors: RawErrorData[]; }>(`/api/v1/monitoring/messages/${messageId}/details`); if (result) { setMessageDetails((prev) => ({ ...prev, [messageId]: { messageId: result.message_id, found: result.found, message: result.message ? { id: result.message.id, timestamp: new Date(result.message.timestamp), botId: result.message.bot_id, botName: result.message.bot_name, pipelineId: result.message.pipeline_id, pipelineName: result.message.pipeline_name, messageContent: result.message.message_content, sessionId: result.message.session_id, status: result.message.status, level: result.message.level, platform: result.message.platform, userId: result.message.user_id, runnerName: result.message.runner_name, variables: result.message.variables, } : undefined, llmCalls: result.llm_calls.map((call: RawLLMCallData) => ({ id: call.id, timestamp: new Date(call.timestamp), modelName: call.model_name, status: call.status, duration: call.duration, errorMessage: call.error_message, tokens: { input: call.input_tokens || 0, output: call.output_tokens || 0, total: call.total_tokens || 0, }, })), errors: result.errors.map((error: RawErrorData) => ({ id: error.id, timestamp: new Date(error.timestamp), errorType: error.error_type, errorMessage: error.error_message, stackTrace: error.stack_trace, })), llmStats: { totalCalls: result.llm_stats.total_calls, totalInputTokens: result.llm_stats.total_input_tokens, totalOutputTokens: result.llm_stats.total_output_tokens, totalTokens: result.llm_stats.total_tokens, totalDurationMs: result.llm_stats.total_duration_ms, averageDurationMs: result.llm_stats.average_duration_ms, }, } as MessageDetails, })); } } catch (error) { console.error('Failed to fetch message details:', error); } finally { setLoadingDetails({ ...loadingDetails, [messageId]: false }); } } } }; const toggleErrorExpand = (errorId: string) => { if (expandedErrorId === errorId) { setExpandedErrorId(null); } else { setExpandedErrorId(errorId); } }; return (
{/* Filters and Refresh Button - Sticky */}
{/* Content Area */}
{/* Overview Section */} {/* Tabs Section */}
{t('monitoring.tabs.messages')} {t('monitoring.tabs.modelCalls')} {t('monitoring.tabs.errors')}
{loading && (
)} {!loading && data && data.messages && data.messages.length > 0 && (
{data.messages .filter((msg) => { // Filter out messages with empty content const content = msg.messageContent?.trim(); return ( content && content !== '[]' && content !== '""' ); }) .map((msg) => (
{/* Message Header - Always Visible */}
toggleMessageExpand(msg.id)} >
{/* Expand Icon */}
{expandedMessageId === msg.id ? ( ) : ( )}
{/* Message Info */}
ID: {msg.id}
{msg.botName} {msg.pipelineName} {msg.runnerName && ( <> {msg.runnerName} )}
{/* Status and Timestamp */}
{msg.timestamp.toLocaleString()} {msg.level}
{/* Expanded Details */} {expandedMessageId === msg.id && (
{loadingDetails[msg.id] && (
)} {!loadingDetails[msg.id] && messageDetails[msg.id] && ( )}
)}
))}
)} {!loading && (!data || !data.messages || data.messages.length === 0) && (

{t('monitoring.messageList.noMessages')}

{t('monitoring.messageList.noMessagesDescription')}

)}
{loading && (
)} {!loading && data && data.modelCalls && data.modelCalls.length > 0 && (
{data.modelCalls.map((call) => (
{/* Query ID - only show if messageId exists */} {call.messageId && (
Query ID: {call.messageId}
)}
{/* Model Type Badge */} {call.modelType === 'llm' ? t('monitoring.modelCalls.llmModel') : t('monitoring.modelCalls.embeddingModel')} {/* Call Type Badge for Embedding */} {call.modelType === 'embedding' && call.callType && ( {call.callType === 'retrieve' ? t( 'monitoring.modelCalls.retrieveCall', ) : t( 'monitoring.modelCalls.embeddingCall', )} )} {/* Status Badge */} {call.status}
{/* Model Name */}
{call.modelName}
{/* Context Info - only for LLM calls */} {call.modelType === 'llm' && call.botName && call.pipelineName && (
{call.botName} → {call.pipelineName}
)} {/* Token Info */}
{call.modelType === 'llm' && call.tokens && ( <> {t('monitoring.llmCalls.inputTokens')}:{' '} {call.tokens.input} {t('monitoring.llmCalls.outputTokens')}:{' '} {call.tokens.output} {t('monitoring.llmCalls.totalTokens')}:{' '} {call.tokens.total} )} {call.modelType === 'embedding' && ( <> {t( 'monitoring.embeddingCalls.promptTokens', )} : {call.promptTokens} {t( 'monitoring.embeddingCalls.totalTokens', )} : {call.totalTokens} {t( 'monitoring.embeddingCalls.inputCount', )} : {call.inputCount} )} {t('monitoring.llmCalls.duration')}:{' '} {call.duration}ms {call.cost && ( {t('monitoring.llmCalls.cost')}: $ {call.cost.toFixed(4)} )}
{/* Knowledge Base Info for Embedding */} {call.modelType === 'embedding' && call.knowledgeBaseId && (
{t( 'monitoring.embeddingCalls.knowledgeBase', )} : {call.knowledgeBaseId}
)} {/* Query Text for Embedding Retrieve */} {call.modelType === 'embedding' && call.queryText && (
{t( 'monitoring.embeddingCalls.queryText', )} :{' '} {call.queryText.length > 100 ? call.queryText.substring(0, 100) + '...' : call.queryText}
)}
{call.errorMessage && (
Error: {call.errorMessage}
)}
{call.timestamp.toLocaleString()}
))}
)} {!loading && (!data || !data.modelCalls || data.modelCalls.length === 0) && (

{t('monitoring.modelCalls.noData')}

)}
{loading && (
)} {!loading && data && data.errors && data.errors.length > 0 && (
{data.errors.map((error) => (
{/* Error Header - Always Visible */}
toggleErrorExpand(error.id)} >
{/* Expand Icon */}
{expandedErrorId === error.id ? ( ) : ( )}
{/* Error Info */}
{/* Query ID */}
Query ID: {error.messageId || '-'} {error.messageId && ( )}
{error.errorType} {error.botName} {error.pipelineName}

{error.errorMessage}

{/* Timestamp */}
{error.timestamp.toLocaleString()}
{/* Expanded Details */} {expandedErrorId === error.id && (
{/* Error Details */}

{t('monitoring.errors.errorMessage')}

{error.errorMessage}
{/* Context Info */}

{t('monitoring.messageList.viewDetails')}

{t('monitoring.messageList.bot')}
{error.botName}
{t('monitoring.messageList.pipeline')}
{error.pipelineName}
{error.sessionId && (
{t('monitoring.sessions.sessionId')}
{error.sessionId}
)}
{/* Stack Trace */} {error.stackTrace && (

{t('monitoring.errors.stackTrace')}

                                    {error.stackTrace}
                                  
)}
)}
))}
)} {!loading && (!data || !data.errors || data.errors.length === 0) && (

{t('monitoring.errors.noErrors')}

)}
); } export default function MonitoringPage() { return ( }> ); }