import { useState, useEffect } from 'react' import { LLMRequestData } from '../utils/llmLogger' import type { TaskState } from '../hooks/useTaskState' import { formatDuration } from '../utils/formatters' import { NoContent } from './NoContent' import './LLMRequestLog.scss' // Support both old interface and new taskState interface interface LLMRequestLogProps { requests?: LLMRequestData[] taskState?: TaskState } export default function LLMRequestLog({ requests: requestsProp, taskState }: LLMRequestLogProps) { // Use taskState.llmRequests if available, otherwise fall back to requests prop const requests = taskState?.llmRequests || requestsProp || [] const [selectedRequest, setSelectedRequest] = useState(null) const [activeTab, setActiveTab] = useState<'stats' | 'request' | 'response' | 'error'>('stats') // Reset active tab when a new request is selected useEffect(() => { setActiveTab('stats') }, [selectedRequest]) const getRequestPreview = (request: any) => { if (request?.messages && Array.isArray(request.messages)) { const lastUserMsg = [...request.messages].reverse().find((m: any) => m.role === 'user') if (lastUserMsg) { const content = typeof lastUserMsg.content === 'string' ? lastUserMsg.content : JSON.stringify(lastUserMsg.content) return content.length > 150 ? content.substring(0, 150) + '...' : content } const lastMsg = request.messages[request.messages.length - 1] if (lastMsg) { const content = typeof lastMsg.content === 'string' ? lastMsg.content : JSON.stringify(lastMsg.content) return content.length > 150 ? content.substring(0, 150) + '...' : content } } if (typeof request === 'string') { return request.length > 150 ? request.substring(0, 150) + '...' : request } const requestStr = JSON.stringify(request) return requestStr.length > 150 ? requestStr.substring(0, 150) + '...' : requestStr } const selectedRequestData = selectedRequest ? requests.find(r => r.requestId === selectedRequest) : null const getStatusBadge = (status: string) => { const className = `status-badge ${status}` // Display ERROR in all caps for error status, otherwise use the status as-is but uppercase const displayText = status.toUpperCase() return {displayText} } if(requests.length === 0) { return } return (
{requests.map((request) => (
setSelectedRequest(request.requestId)} >
{request.model}
{getStatusBadge(request.status)} {new Date(request.timestamp).toLocaleTimeString()}
{getRequestPreview(request.requestData)}
{request.providerName} {request.duration !== undefined && ( {formatDuration(request.duration)} )} {request.tags && request.tags.length > 0 && (
{request.tags.map((tag: string) => ( {tag} ))}
)}
))}
{selectedRequestData && (

Request Details

{!!selectedRequestData.responseData && ( )} {!!selectedRequestData.errorData && ( )}
{activeTab === 'stats' && (
Request ID: {selectedRequestData.requestId}
Agent ID: {selectedRequestData.agentId}
Provider: {selectedRequestData.providerName}
Model: {selectedRequestData.model}
Status: {getStatusBadge(selectedRequestData.status)}
Timestamp: {selectedRequestData.timestamp.toLocaleString()}
{selectedRequestData.duration !== undefined && (
Duration: {formatDuration(selectedRequestData.duration)}
)} {selectedRequestData.tags && selectedRequestData.tags.length > 0 && (
Tags:
{selectedRequestData.tags.map((tag: string) => ( {tag} ))}
)}
)} {activeTab === 'request' && (
                  {JSON.stringify(selectedRequestData.requestData, null, 2)}
                
)} {activeTab === 'response' && !!selectedRequestData.responseData && (
                  {typeof selectedRequestData.responseData === 'string' 
                    ? selectedRequestData.responseData 
                    : JSON.stringify(selectedRequestData.responseData, null, 2)}
                
)} {activeTab === 'error' && !!selectedRequestData.errorData && (
                  {typeof selectedRequestData.errorData === 'string' 
                    ? selectedRequestData.errorData 
                    : JSON.stringify(selectedRequestData.errorData, null, 2)}
                
)}
)}
) }