ui show request / response side by side

This commit is contained in:
Ishaan Jaff 2025-02-28 09:38:39 -08:00
parent 3ba518979e
commit b34d8f4e01
2 changed files with 145 additions and 127 deletions

View file

@ -221,3 +221,51 @@ const formatMessage = (message: any): string => {
}
return String(message);
};
// Add this new component for displaying request/response with copy buttons
export const RequestResponsePanel = ({ request, response }: { request: any; response: any }) => {
const requestStr = typeof request === 'object' ? JSON.stringify(request, null, 2) : String(request || '{}');
const responseStr = typeof response === 'object' ? JSON.stringify(response, null, 2) : String(response || '{}');
const copyToClipboard = (text: string) => {
navigator.clipboard.writeText(text);
};
return (
<div className="grid grid-cols-2 gap-4 mt-4">
<div className="rounded-lg border border-gray-200 bg-gray-50">
<div className="flex justify-between items-center p-3 border-b border-gray-200">
<h3 className="text-sm font-medium">Request</h3>
<button
onClick={() => copyToClipboard(requestStr)}
className="p-1 hover:bg-gray-200 rounded"
title="Copy request"
>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>
</button>
</div>
<pre className="p-4 overflow-auto text-xs font-mono h-64">{requestStr}</pre>
</div>
<div className="rounded-lg border border-gray-200 bg-gray-50">
<div className="flex justify-between items-center p-3 border-b border-gray-200">
<h3 className="text-sm font-medium">Response</h3>
<button
onClick={() => copyToClipboard(responseStr)}
className="p-1 hover:bg-gray-200 rounded"
title="Copy response"
>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>
</button>
</div>
<pre className="p-4 overflow-auto text-xs font-mono h-64">{responseStr}</pre>
</div>
</div>
);
};

View file

@ -8,6 +8,8 @@ import { DataTable } from "./table";
import { columns, LogEntry } from "./columns";
import { Row } from "@tanstack/react-table";
import { prefetchLogDetails } from "./prefetch";
import { RequestResponsePanel } from "./columns";
interface SpendLogsTableProps {
accessToken: string | null;
token: string | null;
@ -579,141 +581,109 @@ function RequestViewer({ row }: { row: Row<LogEntry> }) {
{/* Combined Info Card */}
<div className="bg-white rounded-lg shadow">
<div className="p-4 border-b">
<h3 className="text-lg font-medium ">Request Details</h3>
<h3 className="text-lg font-medium">Request Details</h3>
</div>
<div className="space-y-2 p-4 ">
<div className="flex">
<span className="font-medium w-1/3">Request ID:</span>
<span>{row.original.request_id}</span>
</div>
<div className="flex">
<span className="font-medium w-1/3">Api Key:</span>
<span>{row.original.api_key}</span>
</div>
<div className="flex">
<span className="font-medium w-1/3">Team ID:</span>
<span>{row.original.team_id}</span>
</div>
<div className="flex">
<span className="font-medium w-1/3">Model:</span>
<span>{row.original.model}</span>
</div>
<div className="flex">
<span className="font-medium w-1/3">Custom LLM Provider:</span>
<span>{row.original.custom_llm_provider}</span>
</div>
<div className="flex">
<span className="font-medium w-1/3">Api Base:</span>
<span>{row.original.api_base}</span>
</div>
<div className="flex">
<span className="font-medium w-1/3">Call Type:</span>
<span>{row.original.call_type}</span>
</div>
<div className="flex">
<span className="font-medium w-1/3">Spend:</span>
<span>{row.original.spend}</span>
</div>
<div className="flex">
<span className="font-medium w-1/3">Total Tokens:</span>
<span>{row.original.total_tokens}</span>
</div>
<div className="flex">
<span className="font-medium w-1/3">Prompt Tokens:</span>
<span>{row.original.prompt_tokens}</span>
</div>
<div className="flex">
<span className="font-medium w-1/3">Completion Tokens:</span>
<span>{row.original.completion_tokens}</span>
</div>
<div className="flex">
<span className="font-medium w-1/3">Start Time:</span>
<span>{row.original.startTime}</span>
</div>
<div className="flex">
<span className="font-medium w-1/3">End Time:</span>
<span>{row.original.endTime}</span>
</div>
<div className="flex">
<span className="font-medium w-1/3">Cache Hit:</span>
<span>{row.original.cache_hit}</span>
</div>
<div className="flex">
<span className="font-medium w-1/3">Cache Key:</span>
<span>{row.original.cache_key}</span>
</div>
{row?.original?.requester_ip_address && (
<div className="grid grid-cols-2 gap-4 p-4">
<div className="space-y-2">
<div className="flex">
<span className="font-medium w-1/3">Request IP Address:</span>
<span>{row?.original?.requester_ip_address}</span>
<span className="font-medium w-1/3">Request ID:</span>
<span className="font-mono text-sm">{row.original.request_id}</span>
</div>
<div className="flex">
<span className="font-medium w-1/3">Model:</span>
<span>{row.original.model}</span>
</div>
<div className="flex">
<span className="font-medium w-1/3">Provider:</span>
<span>{row.original.custom_llm_provider || "-"}</span>
</div>
<div className="flex">
<span className="font-medium w-1/3">Start Time:</span>
<span>{row.original.startTime}</span>
</div>
<div className="flex">
<span className="font-medium w-1/3">End Time:</span>
<span>{row.original.endTime}</span>
</div>
</div>
<div className="space-y-2">
<div className="flex">
<span className="font-medium w-1/3">Tokens:</span>
<span>{row.original.total_tokens} ({row.original.prompt_tokens}+{row.original.completion_tokens})</span>
</div>
<div className="flex">
<span className="font-medium w-1/3">Cost:</span>
<span>${Number(row.original.spend || 0).toFixed(6)}</span>
</div>
<div className="flex">
<span className="font-medium w-1/3">Cache Hit:</span>
<span>{row.original.cache_hit}</span>
</div>
{row?.original?.requester_ip_address && (
<div className="flex">
<span className="font-medium w-1/3">IP Address:</span>
<span>{row?.original?.requester_ip_address}</span>
</div>
)}
<div className="flex">
<span className="font-medium w-1/3">Status:</span>
<span className={`px-2 py-1 rounded-md text-xs font-medium inline-block text-center w-16 ${
(row.original.metadata?.status || "Success").toLowerCase() !== "failure"
? 'bg-green-100 text-green-800'
: 'bg-red-100 text-red-800'
}`}>
{(row.original.metadata?.status || "Success").toLowerCase() !== "failure" ? "Success" : "Failure"}
</span>
</div>
)}
</div>
</div>
{/* Request Card */}
<div className="bg-white rounded-lg shadow">
<div className="flex justify-between items-center p-4 border-b">
<h3 className="text-lg font-medium">Request Tags</h3>
</div>
<pre className="p-4 text-wrap overflow-auto text-sm">
{JSON.stringify(formatData(row.original.request_tags), null, 2)}
</pre>
</div>
{/* Request Card */}
<div className="bg-white rounded-lg shadow">
<div className="flex justify-between items-center p-4 border-b">
<h3 className="text-lg font-medium">Request</h3>
{/* <div>
<button className="mr-2 px-3 py-1 text-sm border rounded hover:bg-gray-50">
Expand
</button>
<button className="px-3 py-1 text-sm border rounded hover:bg-gray-50">
JSON
</button>
</div> */}
</div>
<pre className="p-4 text-wrap overflow-auto text-sm">
{JSON.stringify(formatData(row.original.messages), null, 2)}
</pre>
</div>
{/* Response Card */}
<div className="bg-white rounded-lg shadow">
<div className="flex justify-between items-center p-4 border-b">
<h3 className="text-lg font-medium">Response</h3>
<div>
{/* <button className="mr-2 px-3 py-1 text-sm border rounded hover:bg-gray-50">
Expand
</button>
<button className="px-3 py-1 text-sm border rounded hover:bg-gray-50">
JSON
</button> */}
</div>
</div>
<pre className="p-4 text-wrap overflow-auto text-sm">
{JSON.stringify(formatData(row.original.response), null, 2)}
</pre>
</div>
{/* Metadata Card */}
{row.original.metadata &&
Object.keys(row.original.metadata).length > 0 && (
<div className="bg-white rounded-lg shadow">
<div className="flex justify-between items-center p-4 border-b">
<h3 className="text-lg font-medium">Metadata</h3>
{/* <div>
<button className="px-3 py-1 text-sm border rounded hover:bg-gray-50">
JSON
</button>
</div> */}
</div>
<pre className="p-4 text-wrap overflow-auto text-sm ">
{JSON.stringify(row.original.metadata, null, 2)}
</pre>
{/* Request/Response Panel */}
<RequestResponsePanel
request={formatData(row.original.messages)}
response={formatData(row.original.response)}
/>
{/* Tags Card - Only show if there are tags */}
{row.original.request_tags && Object.keys(row.original.request_tags).length > 0 && (
<div className="bg-white rounded-lg shadow">
<div className="flex justify-between items-center p-4 border-b">
<h3 className="text-lg font-medium">Request Tags</h3>
</div>
)}
<div className="p-4">
<div className="flex flex-wrap gap-2">
{Object.entries(row.original.request_tags).map(([key, value]) => (
<span key={key} className="px-2 py-1 bg-gray-100 rounded-full text-xs">
{key}: {String(value)}
</span>
))}
</div>
</div>
</div>
)}
{/* Metadata Card - Only show if there's metadata */}
{row.original.metadata && Object.keys(row.original.metadata).length > 0 && (
<div className="bg-white rounded-lg shadow">
<div className="flex justify-between items-center p-4 border-b">
<h3 className="text-lg font-medium">Metadata</h3>
<button
onClick={() => navigator.clipboard.writeText(JSON.stringify(row.original.metadata, null, 2))}
className="p-1 hover:bg-gray-200 rounded"
title="Copy metadata"
>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>
</button>
</div>
<pre className="p-4 overflow-auto text-xs font-mono max-h-64">
{JSON.stringify(row.original.metadata, null, 2)}
</pre>
</div>
)}
</div>
);
}