mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-10 22:41:41 +00:00
ui show request / response side by side
This commit is contained in:
parent
3ba518979e
commit
b34d8f4e01
2 changed files with 145 additions and 127 deletions
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue