import { useState } from 'react'; import { ChevronDown, Info, Settings, Cpu, MessageCircle, Brain, User, Bot, Target, Copy, Check, ArrowLeftRight, Activity, Clock, Wifi, Calendar, List, FileText } from 'lucide-react'; import { MessageContent } from './MessageContent'; import { formatJSON } from '../utils/formatters'; interface Request { id: number; timestamp: string; method: string; endpoint: string; headers: Record; body?: { model?: string; messages?: Array<{ role: string; content: any; }>; system?: Array<{ text: string; type: string; cache_control?: { type: string }; }>; max_tokens?: number; temperature?: number; stream?: boolean; }; response?: { statusCode: number; headers: Record; body?: any; bodyText?: string; responseTime: number; streamingChunks?: string[]; isStreaming: boolean; completedAt: string; }; promptGrade?: { score: number; criteria: Record; feedback: string; improvedPrompt: string; gradingTimestamp: string; }; } interface RequestDetailContentProps { request: Request; onGrade: () => void; } export default function RequestDetailContent({ request, onGrade }: RequestDetailContentProps) { const [expandedSections, setExpandedSections] = useState>({ overview: true, conversation: true }); const [copied, setCopied] = useState>({}); const toggleSection = (section: string) => { setExpandedSections(prev => ({ ...prev, [section]: !prev[section] })); }; const handleCopy = async (content: string, key: string) => { try { await navigator.clipboard.writeText(content); setCopied(prev => ({ ...prev, [key]: true })); setTimeout(() => { setCopied(prev => ({ ...prev, [key]: false })); }, 2000); } catch (error) { console.error('Failed to copy to clipboard:', error); } }; const getMethodColor = (method: string) => { const colors = { 'GET': 'bg-green-50 text-green-700 border border-green-200', 'POST': 'bg-blue-50 text-blue-700 border border-blue-200', 'PUT': 'bg-yellow-50 text-yellow-700 border border-yellow-200', 'DELETE': 'bg-red-50 text-red-700 border border-red-200' }; return colors[method as keyof typeof colors] || 'bg-gray-50 text-gray-700 border border-gray-200'; }; const canGradeRequest = (request: Request) => { return request.body && request.body.messages && request.body.messages.some(msg => msg.role === 'user') && request.endpoint.includes('/messages'); }; return (
{/* Request Overview */}

Request Overview

{/* {!request.promptGrade && canGradeRequest(request) && ( )} */}
Method: {request.method}
Endpoint: {request.endpoint}
Timestamp: {new Date(request.timestamp).toLocaleString()}
User Agent: {request.headers['User-Agent']?.[0] || 'N/A'}
{/* Headers */}
toggleSection('headers')} >

Request Headers

{expandedSections.headers && (
Headers
                {formatJSON(request.headers)}
              
)}
{request.body && ( <> {/* System Messages */} {request.body.system && (
toggleSection('system')} >

System Instructions {request.body.system.length} items

{expandedSections.system && (
{request.body.system.map((sys, index) => (
System Message #{index + 1} {sys.cache_control && ( Cache: {sys.cache_control.type} )}
))}
)}
)} {/* Conversation */} {request.body.messages && (
toggleSection('conversation')} >

Conversation {request.body.messages.length} messages

{expandedSections.conversation && (
{request.body.messages.map((message, index) => ( ))}
)}
)} {/* Model Configuration */}
toggleSection('model')} >

Model Configuration

{expandedSections.model && (
Model
{request.body.model || 'N/A'}
Max Tokens
{request.body.max_tokens?.toLocaleString() || 'N/A'}
Temperature
{request.body.temperature ?? 'N/A'}
Stream
{request.body.stream ? '✅ Yes' : '❌ No'}
)}
)} {/* API Response */} {request.response && ( )} {/* Prompt Grading Results */} {request.promptGrade && ( )}
); } // Message bubble component function MessageBubble({ message, index }: { message: any; index: number }) { const roleColors = { 'user': 'bg-blue-50 border border-blue-200', 'assistant': 'bg-gray-50 border border-gray-200', 'system': 'bg-yellow-50 border border-yellow-200' }; const roleIcons = { 'user': User, 'assistant': Bot, 'system': Settings }; const roleIconColors = { 'user': 'text-blue-600', 'assistant': 'text-gray-600', 'system': 'text-yellow-600' }; const Icon = roleIcons[message.role as keyof typeof roleIcons] || User; return (
{message.role} #{index + 1}
); } // Placeholder for prompt grading results - you can expand this function PromptGradingResults({ promptGrade }: { promptGrade: any }) { return (

Prompt Quality Analysis

Overall Score: {promptGrade.score}/5

{promptGrade.feedback}

); } // Response Details Component function ResponseDetails({ response }: { response: NonNullable }) { const [expandedSections, setExpandedSections] = useState>({ overview: true }); const [copied, setCopied] = useState>({}); const toggleSection = (section: string) => { setExpandedSections(prev => ({ ...prev, [section]: !prev[section] })); }; const handleCopy = async (content: string, key: string) => { try { await navigator.clipboard.writeText(content); setCopied(prev => ({ ...prev, [key]: true })); setTimeout(() => { setCopied(prev => ({ ...prev, [key]: false })); }, 2000); } catch (error) { console.error('Failed to copy to clipboard:', error); } }; const getStatusColor = (statusCode: number) => { if (statusCode >= 200 && statusCode < 300) { return { bg: 'bg-green-50', border: 'border-green-200', text: 'text-green-700', icon: 'text-green-600' }; } if (statusCode >= 400 && statusCode < 500) { return { bg: 'bg-yellow-50', border: 'border-yellow-200', text: 'text-yellow-700', icon: 'text-yellow-600' }; } if (statusCode >= 500) { return { bg: 'bg-red-50', border: 'border-red-200', text: 'text-red-700', icon: 'text-red-600' }; } return { bg: 'bg-gray-50', border: 'border-gray-200', text: 'text-gray-700', icon: 'text-gray-600' }; }; // Parse streaming chunks to extract the final assembled text const parseStreamingResponse = (chunks: string[]) => { let assembledText = ''; let rawData = chunks.join(''); try { // Split by lines and process each SSE event const lines = rawData.split('\n').filter(line => line.trim()); for (const line of lines) { // Look for data lines in SSE format if (line.startsWith('data: ')) { const jsonStr = line.substring(6).trim(); // Skip non-JSON lines (like "data: [DONE]") if (!jsonStr.startsWith('{')) continue; try { const eventData = JSON.parse(jsonStr); // Extract text from content_block_delta events if (eventData.type === 'content_block_delta' && eventData.delta && eventData.delta.type === 'text_delta' && typeof eventData.delta.text === 'string') { assembledText += eventData.delta.text; } } catch (parseError) { // Skip malformed JSON continue; } } } // If we successfully extracted text, return it if (assembledText.trim().length > 0) { return { finalText: assembledText, isFormatted: true, rawData: rawData }; } // Fallback: try to find any text content in the raw data const textMatches = rawData.match(/"text":"([^"]+)"/g); if (textMatches) { let fallbackText = ''; for (const match of textMatches) { const text = match.match(/"text":"([^"]+)"/)?.[1]; if (text) { // Unescape common JSON escape sequences fallbackText += text.replace(/\\n/g, '\n').replace(/\\"/g, '"').replace(/\\\\/g, '\\'); } } if (fallbackText.trim()) { return { finalText: fallbackText, isFormatted: true, rawData: rawData }; } } } catch (error) { console.warn('Error parsing streaming response:', error); } // Ultimate fallback to raw concatenation return { finalText: rawData, isFormatted: false, rawData: rawData }; }; const statusColors = getStatusColor(response.statusCode); const completedAt = response.completedAt ? new Date(response.completedAt).toLocaleString() : 'Unknown'; return (
toggleSection('overview')} >

API Response {response.statusCode}

{expandedSections.overview && (
{/* Response Overview */}
Status
{response.statusCode}
{response.statusCode >= 200 && response.statusCode < 300 ? 'Success' : response.statusCode >= 400 && response.statusCode < 500 ? 'Client Error' : response.statusCode >= 500 ? 'Server Error' : 'Unknown'}
Response Time
{response.responseTime}ms
{response.responseTime < 1000 ? 'Fast' : response.responseTime < 3000 ? 'Normal' : 'Slow'}
Type
{response.isStreaming ? 'Stream' : 'Single'}
{response.isStreaming ? 'Streaming' : 'Complete'}
Completed
{completedAt.split(' ')[1] || 'N/A'}
{completedAt.split(' ')[0] || ''}
{/* Response Headers */} {response.headers && (
toggleSection('responseHeaders')} >
Response Headers {Object.keys(response.headers).length}
{expandedSections.responseHeaders && (
Headers
                      {formatJSON(response.headers)}
                    
)}
)} {/* Response Body */} {(response.body || response.bodyText) && (
toggleSection('responseBody')} >
Response Body {response.body ? 'JSON' : 'Text'}
{expandedSections.responseBody && (
Response
                      {response.body ? formatJSON(response.body) : response.bodyText}
                    
)}
)} {/* Streaming Response */} {response.isStreaming && response.streamingChunks && response.streamingChunks.length > 0 && (() => { const parsed = parseStreamingResponse(response.streamingChunks); return (
toggleSection('streamingResponse')} >
Streaming Response {response.streamingChunks.length} chunks {parsed.isFormatted && ( Parsed )}
{expandedSections.streamingResponse && (
{/* Clean Parsed Response */} {parsed.isFormatted && (
Final Response (Clean)
                            {parsed.finalText}
                          
Extracted clean text from streaming chunks
)} {/* Raw Data (Collapsible) */}
toggleSection('rawStreamingData')} > Raw Streaming Data
{expandedSections.rawStreamingData && (
SSE Events & Metadata
                            {parsed.rawData}
                          
)}
{parsed.isFormatted ? `Successfully parsed ${response.streamingChunks.length} streaming chunks` : `Raw display of ${response.streamingChunks.length} streaming chunks (parsing failed)` }
)}
); })()}
)}
); }