import { formatCost, formatLatency, formatTokens, } from "@/app/workspace/dashboard/utils/chartUtils"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alertDialog"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { CodeEditor } from "@/components/ui/codeEditor"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdownMenu"; import { DottedSeparator } from "@/components/ui/separator"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/ui/tooltip"; import { useCopyToClipboard } from "@/hooks/useCopyToClipboard"; import { ProviderIconType, RenderProviderIcon, RoutingEngineUsedIcons, } from "@/lib/constants/icons"; import { RequestTypeColors, RequestTypeLabels, RoutingEngineUsedColors, RoutingEngineUsedLabels, Status } from "@/lib/constants/logs"; import { LogEntry, ResponsesMessage } from "@/lib/types/logs"; import { cn } from "@/lib/utils"; import { downloadAsJson } from "@/lib/utils/browser-download"; import { Link } from "@tanstack/react-router"; import { addMilliseconds, format } from "date-fns"; import { AlertCircle, ChevronDown, Clipboard, Download, Loader2, MoreVertical, Trash2, Wrench, } from "lucide-react"; import { useState, type ReactNode } from "react"; import { toast } from "sonner"; import BlockHeader from "../views/blockHeader"; import CollapsibleBox from "../views/collapsibleBox"; import ImageView from "../views/imageView"; import LogChatMessageView from "../views/logChatMessageView"; import LogEntryDetailsView from "../views/logEntryDetailsView"; import OCRView from "../views/ocrView"; import PluginLogsView from "../views/pluginLogsView"; import SpeechView from "../views/speechView"; import TranscriptionView from "../views/transcriptionView"; import VideoView from "../views/videoView"; const extractResponsesText = (msg: ResponsesMessage): string => { if (msg.type === "reasoning") { const summaryText = (msg.summary ?? []) .map((s) => s.text) .filter(Boolean) .join("\n") .trim(); if (summaryText) return summaryText; if (msg.encrypted_content) return msg.encrypted_content; } if (typeof msg.content === "string") return msg.content; if (Array.isArray(msg.content)) { return msg.content .filter( (b: any) => b && b.text && (b.type === "input_text" || b.type === "output_text" || b.type === "reasoning_text" || b.type === "refusal"), ) .map((b: any) => b.text as string) .join("\n"); } if (typeof (msg as any).arguments === "string") return (msg as any).arguments as string; return ""; }; const getResponsesRole = (msg: ResponsesMessage): MessageRole => { if (msg.type === "reasoning") return "reasoning"; if ( msg.type && (msg.type.endsWith("_call") || msg.type.endsWith("_call_output") || msg.type === "mcp_list_tools" || msg.type === "mcp_approval_request" || msg.type === "mcp_approval_responses") ) { return "tool"; } const r = msg.role; if (r === "user") return "user"; if (r === "assistant") return "assistant"; if (r === "system" || r === "developer") return "system"; return "assistant"; }; const extractMessageText = (message: any): string => { if (!message || message.content == null) return ""; if (typeof message.content === "string") return message.content; if (Array.isArray(message.content)) { return message.content .filter( (block: any) => block && (block.type === "text" || block.type === "input_text" || block.type === "output_text") && block.text, ) .map((block: any) => block.text) .join("\n"); } return ""; }; const formatJsonSafe = (str: string | undefined): string => { try { return JSON.stringify(JSON.parse(str || ""), null, 2); } catch { return str || ""; } }; const formatToolChoice = (value: unknown): string => { if (typeof value === "string") return value; try { return JSON.stringify(value); } catch { return String(value); } }; // Helper to detect passthrough operations const isPassthroughOperation = (object: string) => object === "passthrough" || object === "passthrough_stream"; // Helper to detect container operations (for hiding irrelevant fields like Model/Tokens) const isContainerOperation = (object: string) => { const containerTypes = [ "container_create", "container_list", "container_retrieve", "container_delete", "container_file_create", "container_file_list", "container_file_retrieve", "container_file_content", "container_file_delete", ]; return containerTypes.includes(object?.toLowerCase()); }; const statusPillStyles: Record = { success: "bg-green-50 text-green-700 border-green-200 dark:bg-green-950/40 dark:text-green-400 dark:border-green-900", error: "bg-red-50 text-red-700 border-red-200 dark:bg-red-950/40 dark:text-red-400 dark:border-red-900", processing: "bg-blue-50 text-blue-700 border-blue-200 dark:bg-blue-950/40 dark:text-blue-400 dark:border-blue-900", cancelled: "bg-gray-50 text-gray-700 border-gray-200 dark:bg-gray-900/40 dark:text-gray-400 dark:border-gray-800", }; const statusDotStyles: Record = { success: "bg-green-500", error: "bg-red-500", processing: "bg-blue-500", cancelled: "bg-gray-400", }; function StatusPill({ status }: { status: Status }) { return ( {status} ); } function HeroStat({ label, value, sub, mono = false, valueClass, hasRightBorder = false, }: { label: string; value: ReactNode; sub?: ReactNode; mono?: boolean; valueClass?: string; hasRightBorder?: boolean; }) { return (
{label}
{value}
{sub ? (
{sub}
) : null}
); } function CopyInlineButton({ text }: { text: string }) { const { copy } = useCopyToClipboard({ successMessage: "Copied" }); return ( ); } type MessageRole = "system" | "user" | "assistant" | "reasoning" | "tool"; const messageToneClass: Record = { system: "bg-zinc-50 border-zinc-200 dark:bg-zinc-900/40 dark:border-zinc-800", user: "bg-blue-50/60 border-blue-200 dark:bg-blue-950/30 dark:border-blue-900", assistant: "bg-white border-zinc-200 dark:bg-zinc-900 dark:border-zinc-800", reasoning: "bg-violet-50/70 border-violet-200 dark:bg-violet-950/30 dark:border-violet-900", tool: "bg-amber-50/70 border-amber-200 dark:bg-amber-950/30 dark:border-amber-900", }; const messageDotClass: Record = { system: "bg-zinc-400", user: "bg-blue-500", assistant: "bg-zinc-900 dark:bg-zinc-100", reasoning: "bg-violet-500", tool: "bg-amber-500", }; const messageRoleLabel: Record = { system: "System", user: "User", assistant: "Assistant", reasoning: "Reasoning", tool: "Tool", }; function CollapsibleCode({ text, preview = 3, lang, mono = true, }: { text: string; preview?: number; lang?: string; mono?: boolean; }) { const [open, setOpen] = useState(false); const lines = text.split("\n"); const shown = open ? lines : lines.slice(0, preview); const hasMore = lines.length > preview; const moreCount = lines.length - preview; return ( <> {mono ? (
          {shown.join("\n")}
        
) : (
{shown.join("\n")}
)} {hasMore && (
{lines.length} lines{lang ? ` · ${lang}` : ""}
)} ); } function MessageRow({ role, meta, children, last = false, }: { role: MessageRole; meta?: string; children: ReactNode; last?: boolean; }) { return (
{!last &&
}
{messageRoleLabel[role]} {meta ? ( {meta} ) : null}
{children}
); } interface LogDetailViewProps { log: LogEntry | null; resolvedSelectedPromptName?: string; // Current prompt name from prompt-repo when `selected_prompt_id` is set; falls back to stored log name loading?: boolean; handleDelete?: (log: LogEntry) => void; onClose?: () => void; headerAction?: ReactNode; onFilterByParentRequestId?: (parentRequestId: string) => void; } export function LogDetailView({ log, resolvedSelectedPromptName, loading = false, handleDelete, onClose, headerAction, onFilterByParentRequestId, }: LogDetailViewProps) { const { copy: copyRequestId } = useCopyToClipboard({ successMessage: "Request ID copied", }); const { copy: copyBody } = useCopyToClipboard({ successMessage: "Request body copied to clipboard", errorMessage: "Failed to copy request body", }); if (!log) return null; const selectedPromptDisplayName = resolvedSelectedPromptName ?? log.selected_prompt_name ?? ""; const isContainer = isContainerOperation(log.object); const isPassthrough = isPassthroughOperation(log.object); const passthroughParams = isPassthrough ? (log.params as { method?: string; path?: string; raw_query?: string; status_code?: number; }) : null; let toolsParameter = null; if (log.params?.tools) { try { toolsParameter = JSON.stringify(log.params.tools, null, 2); } catch {} } const audioFormat = (log.params as any)?.audio?.format || (log.params as any)?.extra_params?.audio?.format || undefined; const rawRequest = log.raw_request; const rawResponse = log.raw_response; const passthroughRequestBody = log.passthrough_request_body; const passthroughResponseBody = log.passthrough_response_body; const videoOutput = log.video_generation_output || log.video_retrieve_output || log.video_download_output; const videoListOutput = log.video_list_output; const pluginLogCount = (() => { if (!log.plugin_logs) return 0; try { const parsed = JSON.parse(log.plugin_logs); if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) { return Object.values(parsed).reduce((sum, v) => sum + (Array.isArray(v) ? v.length : 0), 0); } } catch {} return 0; })(); return loading ? (
) : ( <> {/* Breadcrumb header with actions */}
{headerAction} Request details
{handleDelete && onClose ? ( copyRequestBody(log, copyBody)} data-testid="logdetails-copy-request-body-button" > Copy request body downloadAsJson(log, `log-${log.id ?? "export"}.json`)} data-testid="logdetails-export-log-button" > Export as JSON Delete log Are you sure you want to delete this log? This action cannot be undone. This will permanently delete the log entry. Cancel { handleDelete(log); onClose(); }} > Delete ) : null}
{RequestTypeLabels[ log.object as keyof typeof RequestTypeLabels ] ?? log.object} {log.routing_rule && ( rule: {log.routing_rule.name} )} {log.metadata?.isAsyncRequest ? ( Async ) : null} {(log.is_large_payload_request || log.is_large_payload_response) && ( Large Payload )}
Request
{log.id || "—"} {log.id ? : null}
{(log.routing_rule || log.selected_key) && (
{log.routing_rule ? ( <> matched rule{" "} “{log.routing_rule.name}” ) : null} {log.routing_rule && log.selected_key ? " · " : ""} {log.selected_key ? ( <> key{" "} {log.selected_key.name} ) : null}
)}
{log.provider}
{ if (!log.timestamp) return ""; const start = new Date(log.timestamp); if (isNaN(start.getTime())) return ""; const startStr = format(start, "HH:mm:ss"); if (log.latency == null || isNaN(log.latency)) return startStr; return `${startStr} → ${format(addMilliseconds(start, log.latency), "HH:mm:ss")}`; })()} hasRightBorder />
More details timings, request meta, tokens, caching, metadata
{ const d = log.timestamp ? new Date(log.timestamp) : null; return d && !isNaN(d.getTime()) ? format(d, "yyyy-MM-dd hh:mm:ss aa") : "N/A"; })()} /> { const d = log.timestamp ? new Date(log.timestamp) : null; return d && !isNaN(d.getTime()) ? format( addMilliseconds(d, log.latency || 0), "yyyy-MM-dd hh:mm:ss aa", ) : "N/A"; })()} /> {log.latency.toFixed(2)}ms
) } />
{log.provider} } /> {!isContainer && ( )} {!isContainer && log.alias && ( )} {RequestTypeLabels[ log.object as keyof typeof RequestTypeLabels ] ?? log.object ?? "unknown"}
} /> {log.parent_request_id && ( onFilterByParentRequestId( log.parent_request_id as string, ) } > {log.parent_request_id} Filter this session ) : ( {log.parent_request_id} ) } /> )} {log.selected_key && ( )} {(log.selected_prompt_id || log.selected_prompt_name || log.selected_prompt_version) && ( {selectedPromptDisplayName} {selectedPromptDisplayName && log.selected_prompt_version ? " · " : ""} {log.selected_prompt_version ? ( <>v{log.selected_prompt_version} ) : null} } /> )} {log.number_of_retries > 0 && ( )} {log.team_id && ( {log.team_name || log.team_id} } /> )} {log.customer_id && ( {log.customer_name || log.customer_id} } /> )} {log.business_unit_id && ( {log.business_unit_name || log.business_unit_id} } /> )} {log.user_id && ( {log.user_name || log.user_id} {log.user_name ? log.user_id : "Filter by user"} } /> )} {log.fallback_index > 0 && ( )} {log.virtual_key && ( )} {log.routing_engines_used && log.routing_engines_used.length > 0 && ( {log.routing_engines_used.map((engine) => (
{RoutingEngineUsedIcons[ engine as keyof typeof RoutingEngineUsedIcons ]?.()} {RoutingEngineUsedLabels[ engine as keyof typeof RoutingEngineUsedLabels ] ?? engine}
))}
} /> )} {log.routing_rule && ( )} {(log.params as any)?.audio && ( <> {(log.params as any).audio.format && ( )} {(log.params as any).audio.voice && ( )} )} {passthroughParams && ( <> {passthroughParams.method && ( )} {passthroughParams.path && ( )} {passthroughParams.raw_query && ( )} {(passthroughParams.status_code ?? 0) !== 0 && ( )} )} {log.params && Object.keys(log.params).length > 0 && Object.entries(log.params) .filter(([key]) => { const passthroughKeys = [ "method", "path", "raw_query", "status_code", ]; return ( key !== "tools" && key !== "instructions" && key !== "audio" && !(isPassthrough && passthroughKeys.includes(key)) ); }) .filter( ([_, value]) => typeof value === "boolean" || typeof value === "number" || typeof value === "string", ) .map(([key, value]) => ( ))}
{log.status === "success" && !isContainer && !isPassthrough && ( <>
{log.token_usage?.prompt_tokens_details && ( <> {log.token_usage.prompt_tokens_details .cached_read_tokens && ( )} {log.token_usage.prompt_tokens_details .cached_write_tokens && ( )} {log.token_usage.prompt_tokens_details.audio_tokens && ( )} )} {log.token_usage?.completion_tokens_details && ( <> {log.token_usage.completion_tokens_details .reasoning_tokens && ( )} {log.token_usage.completion_tokens_details .audio_tokens && ( )} {log.token_usage.completion_tokens_details .accepted_prediction_tokens && ( )} {log.token_usage.completion_tokens_details .rejected_prediction_tokens && ( )} )}
{(() => { const params = log.params as any; const reasoning = params?.reasoning; if ( !reasoning || typeof reasoning !== "object" || Object.keys(reasoning).length === 0 ) { return null; } return ( <>
{reasoning.effort && ( {reasoning.effort} } /> )} {reasoning.summary && ( {reasoning.summary} } /> )} {reasoning.generate_summary && ( {reasoning.generate_summary} } /> )} {reasoning.max_tokens && ( )}
); })()} {log.cache_debug && ( <>
{log.cache_debug.cache_hit ? ( <> {log.cache_debug.hit_type} } /> {log.cache_debug.hit_type === "semantic" && ( <> {log.cache_debug.provider_used && ( {log.cache_debug.provider_used} } /> )} {log.cache_debug.model_used && ( )} {log.cache_debug.threshold && ( )} {log.cache_debug.similarity && ( )} {log.cache_debug.input_tokens && ( )} )} ) : ( <> {log.cache_debug.provider_used && ( {log.cache_debug.provider_used} } /> )} {log.cache_debug.model_used && ( )} {log.cache_debug.input_tokens && ( )} )}
)} {log.metadata && Object.keys(log.metadata).filter((k) => k !== "isAsyncRequest") .length > 0 && ( <>
{Object.entries(log.metadata) .filter(([key]) => key !== "isAsyncRequest") .map(([key, value]) => ( ))}
)} )} Messages {log.input_history?.length ? ( {log.input_history.length + (log.output_message ? 1 : 0)} ) : null} Tools {log.params?.tools?.length ? ( {log.params.tools.length} ) : null} Routing {log.routing_engine_logs ? ( {log.routing_engine_logs.split("\n").filter(Boolean).length} ) : null} Plugin Logs {pluginLogCount > 0 ? ( {pluginLogCount} ) : null} Raw JSON {(log.ocr_input || log.ocr_output) && ( )} {(log.speech_input || log.speech_output) && ( )} {(log.transcription_input || log.transcription_output) && ( )} {(log.image_generation_input || log.image_edit_input || log.image_variation_input || log.image_generation_output) && ( )} {(log.video_generation_input || videoOutput || videoListOutput) && ( )} {((log.input_history && log.input_history.length > 0) || (log.output_message && !log.error_details?.error.message)) && (
{log.input_history?.map((message, index) => { const role = ((message.role as string) || "user") as MessageRole; const text = extractMessageText(message); const hasToolCalls = Array.isArray(message.tool_calls) && message.tool_calls.length > 0; const isLast = index === (log.input_history?.length ?? 0) - 1 && !log.output_message && !log.error_details?.error.message; const lineCount = text ? text.split("\n").length : 0; const approxTokens = text ? Math.max(1, Math.round(text.length / 4)) : 0; const meta = text ? role === "system" || role === "tool" ? `${lineCount} line${lineCount === 1 ? "" : "s"} · ~${approxTokens} tokens` : `${lineCount} line${lineCount === 1 ? "" : "s"}` : hasToolCalls ? `${message.tool_calls!.length} tool call${message.tool_calls!.length === 1 ? "" : "s"}` : undefined; const usePlainText = role === "user" || role === "assistant"; return ( {text ? ( usePlainText ? ( ) : ( ) ) : ( )} {hasToolCalls && text ? (
{message.tool_calls! .map((tc) => tc.function?.name) .filter(Boolean) .join(", ") || `${message.tool_calls!.length} tool call${message.tool_calls!.length === 1 ? "" : "s"}`}
) : null}
); })} {log.output_message && !log.error_details?.error.message && (() => { const text = extractMessageText(log.output_message); const lineCount = text ? text.split("\n").length : 0; const tokenMeta = log.token_usage?.completion_tokens ? `${log.token_usage.completion_tokens} tokens` : undefined; const meta = text ? tokenMeta ? `${lineCount} line${lineCount === 1 ? "" : "s"} · ${tokenMeta}` : `${lineCount} line${lineCount === 1 ? "" : "s"}` : tokenMeta; return ( {text ? ( ) : ( )} ); })()}
)} {(() => { const inputMsgs = log.responses_input_history ?? []; const outputMsgs = log.status !== "processing" && !log.error_details?.error.message ? (log.responses_output ?? []) : []; const all: ResponsesMessage[] = [...inputMsgs, ...outputMsgs]; if (all.length === 0) return null; return (
{all.map((msg, index) => { const role = getResponsesRole(msg); const text = extractResponsesText(msg); const isLast = index === all.length - 1; const lineCount = text ? text.split("\n").length : 0; const approxTokens = text ? Math.max(1, Math.round(text.length / 4)) : 0; const isEncrypted = msg.type === "reasoning" && !!msg.encrypted_content; const meta = text ? role === "system" || role === "tool" ? msg.name ? `${msg.name} · ${lineCount} line${lineCount === 1 ? "" : "s"} · ~${approxTokens} tokens` : `${lineCount} line${lineCount === 1 ? "" : "s"} · ~${approxTokens} tokens` : role === "reasoning" ? `~${approxTokens} tokens${isEncrypted ? " · encrypted" : ""}` : `${lineCount} line${lineCount === 1 ? "" : "s"}` : msg.name ? msg.name : msg.type === "function_call_output" && msg.call_id ? msg.call_id : msg.type || undefined; const usePlainText = role === "user" || role === "assistant"; return ( {text ? ( usePlainText ? ( ) : ( ) ) : msg.output !== undefined ? ( ) : (
{msg.type || "—"}
)}
); })}
); })()} {log.is_large_payload_request && !log.input_history?.length && !log.responses_input_history?.length && (
Large payload request — input content was streamed directly to the provider and is not available for display. {log.raw_request && " A truncated preview is available in the Raw JSON tab."}
)} {log.is_large_payload_response && !log.output_message && !log.responses_output?.length && log.status !== "processing" && (
Large payload response — response content was streamed directly to the client and is not available for display. {log.raw_response && " A truncated preview is available in the Raw JSON tab."}
)} {log.status !== "processing" && log.embedding_output && log.embedding_output.length > 0 && !log.error_details?.error.message && (
Embedding
embedding.embedding, ), null, 2, ), }} />
)} {log.status !== "processing" && log.rerank_output && !log.error_details?.error.message && ( JSON.stringify(log.rerank_output, null, 2)} > )} {(log.error_details?.error.message || log.error_details?.error.error != null) && (
Error {log.error_details?.error.message ? ( ) : null}
{log.error_details?.error.message ? (
{log.error_details.error.message}
) : null} {log.error_details?.error.error != null ? (
Details
{typeof log.error_details.error.error === "string" ? log.error_details.error.error : JSON.stringify(log.error_details.error.error, null, 2)}
) : null}
)}
{toolsParameter ? (
{log.params?.tools?.length ?? 0} tools exposed to the model {(log.params as any)?.tool_choice != null ? ( <> {" "} · tool_choice ={" "} {formatToolChoice((log.params as any).tool_choice)} ) : null}
{(log.params?.tools as any[]).map((tool, i) => { const name = tool?.function?.name ?? tool?.name ?? `tool_${i}`; const description = tool?.function?.description ?? tool?.description ?? ""; const schema = tool?.function?.parameters ?? tool?.input_schema ?? tool?.parameters ?? null; const schemaJson = schema != null ? JSON.stringify(schema, null, 2) : ""; return (
{name}
{description ? (
{description}
) : null}
{schemaJson ? (
Parameters
                            {schemaJson}
                          
) : (
No parameter schema.
)}
); })}
) : null} {log.params?.instructions && ( log.params?.instructions || ""} >
{log.params.instructions}
)} {!toolsParameter && !log.params?.instructions && (
No tools or instructions on this request.
)}
{log.attempt_trail && log.attempt_trail.length > 1 && ( JSON.stringify(log.attempt_trail, null, 2)} >
{log.attempt_trail.map((record) => ( ))}
# Key Result
{record.attempt + 1} {record.key_name || record.key_id} {record.fail_reason ? ( {record.fail_reason} ) : ( success )}
)} {log.routing_engine_logs && ( log.routing_engine_logs || ""} >
{log.routing_engine_logs .split("\n") .filter((l) => l.trim()) .map((line, i) => { const m = line.match( /^\[(\d+)\]\s+\[([^\]]+)\]\s+-\s+(.*)$/, ); const ts = m ? Number(m[1]) : null; const scope = m ? m[2] : null; const message = m ? m[3] : line; return (
{ts != null ? ( {format(new Date(ts), "HH:mm:ss.SSS")} ) : null} {scope ? ( {scope} ) : null} {message}
); })}
)} {!log.attempt_trail?.length && !log.routing_engine_logs && (
No routing logs for this request.
)}
{log.plugin_logs ? ( ) : (
No plugin logs for this request.
)}
{isPassthrough && passthroughRequestBody && ( { try { return JSON.stringify( JSON.parse(passthroughRequestBody || ""), null, 2, ); } catch { return passthroughRequestBody || ""; } }} > { try { return JSON.stringify( JSON.parse(passthroughRequestBody || ""), null, 2, ); } catch { return passthroughRequestBody || ""; } })()} lang="json" readonly={true} options={{ scrollBeyondLastLine: false, lineNumbers: "off", alwaysConsumeMouseWheel: false, }} /> )} {isPassthrough && passthroughResponseBody && log.status !== "processing" && ( { try { return JSON.stringify( JSON.parse(passthroughResponseBody || ""), null, 2, ); } catch { return passthroughResponseBody || ""; } }} > { try { return JSON.stringify( JSON.parse(passthroughResponseBody || ""), null, 2, ); } catch { return passthroughResponseBody || ""; } })()} lang="json" readonly={true} options={{ scrollBeyondLastLine: false, lineNumbers: "off", alwaysConsumeMouseWheel: false, }} /> )} {rawRequest && ( <>
Raw Request sent to{" "} {log.provider} {log.is_large_payload_request && ( (truncated preview) )}
formatJsonSafe(rawRequest)} > )} {rawResponse && log.status !== "processing" && ( <>
Raw Response from{" "} {log.provider} {log.is_large_payload_response && ( (truncated preview) )}
formatJsonSafe(rawResponse)} > )} {log.list_models_output && ( JSON.stringify(log.list_models_output, null, 2)} > )} {!rawRequest && !rawResponse && !passthroughRequestBody && !passthroughResponseBody && !log.list_models_output && (
No raw JSON available.
)}
); } const copyRequestBody = async ( log: LogEntry, copy: (text: string) => Promise, ) => { try { const isChat = log.object === "chat.completion" || log.object === "chat.completion.chunk"; const isResponses = log.object === "response" || log.object === "response.completion.chunk"; const isRealtimeTurn = log.object === "realtime.turn"; const isSpeech = log.object === "audio.speech" || log.object === "audio.speech.chunk"; const isTextCompletion = log.object === "text.completion" || log.object === "text.completion.chunk"; const isEmbedding = log.object === "list"; const extractTextFromMessage = (message: any): string => { if (!message || !message.content) { return ""; } if (typeof message.content === "string") { return message.content; } if (Array.isArray(message.content)) { return message.content .filter((block: any) => block && block.type === "text" && block.text) .map((block: any) => block.text) .join("\n"); } return ""; }; const extractTextsFromMessage = (message: any): string[] => { if (!message || !message.content) { return []; } if (typeof message.content === "string") { return message.content ? [message.content] : []; } if (Array.isArray(message.content)) { return message.content .filter((block: any) => block && block.type === "text" && block.text) .map((block: any) => block.text); } return []; }; const isSupportedType = isChat || isResponses || isRealtimeTurn || isSpeech || isTextCompletion || isEmbedding; if (!isSupportedType) { if ( log.object === "audio.transcription" || log.object === "audio.transcription.chunk" ) { toast.error( "Copy request body is not available for transcription requests", ); } else { toast.error( "Copy request body is only available for chat, responses, speech, text completion, and embedding requests", ); } return; } const requestBody: any = { model: log.provider && log.model ? `${log.provider}/${log.model}` : log.model || "", }; if (isRealtimeTurn) { if (log.input_history && log.input_history.length > 0) { requestBody.messages = log.input_history; } if (log.output_message) { requestBody.output = log.output_message; } } else if (isChat && log.input_history && log.input_history.length > 0) { requestBody.messages = log.input_history; } else if ( isResponses && log.responses_input_history && log.responses_input_history.length > 0 ) { requestBody.input = log.responses_input_history; } else if (isSpeech && log.speech_input) { requestBody.input = log.speech_input.input; } else if ( isTextCompletion && log.input_history && log.input_history.length > 0 ) { const firstMessage = log.input_history[0]; const prompt = extractTextFromMessage(firstMessage); if (prompt) { requestBody.prompt = prompt; } } else if ( isEmbedding && log.input_history && log.input_history.length > 0 ) { const texts: string[] = []; for (const message of log.input_history) { const messageTexts = extractTextsFromMessage(message); texts.push(...messageTexts); } if (texts.length > 0) { requestBody.input = texts.length === 1 ? texts[0] : texts; } } if (log.params) { const paramsCopy = { ...log.params }; delete paramsCopy.tools; delete paramsCopy.instructions; Object.assign(requestBody, paramsCopy); } if ( (isChat || isResponses || isRealtimeTurn) && log.params?.tools && Array.isArray(log.params.tools) && log.params.tools.length > 0 ) { requestBody.tools = log.params.tools; } if ((isResponses || isRealtimeTurn) && log.params?.instructions) { requestBody.instructions = log.params.instructions; } const requestBodyJson = JSON.stringify(requestBody, null, 2); await copy(requestBodyJson); } catch { toast.error("Failed to copy request body"); } };