This commit is contained in:
Ishaan Jaffer 2026-01-30 16:35:06 -08:00
parent 8890b9e406
commit 2d2b502b9d
5 changed files with 169 additions and 168 deletions

View file

@ -1,5 +1,5 @@
import React from "react";
import { Accordion, AccordionHeader, AccordionBody } from "@tremor/react";
import { Collapse } from "antd";
import { formatNumberWithCommas } from "@/utils/dataUtils";
export interface CostBreakdown {
@ -60,18 +60,22 @@ export const CostBreakdownViewer: React.FC<CostBreakdownViewerProps> = ({
return (
<div className="bg-white rounded-lg shadow w-full max-w-full overflow-hidden mb-6">
<Accordion>
<AccordionHeader className="p-4 border-b hover:bg-gray-50 transition-colors text-left">
<div className="flex items-center justify-between w-full">
<h3 className="text-lg font-medium text-gray-900">Cost Breakdown</h3>
<div className="flex items-center space-x-2 mr-4">
<span className="text-sm text-gray-500">Total:</span>
<span className="text-sm font-semibold text-gray-900">{formatCost(totalSpend)}</span>
</div>
</div>
</AccordionHeader>
<AccordionBody className="px-0">
<div className="p-6 space-y-4">
<Collapse
expandIconPosition="start"
items={[
{
key: "1",
label: (
<div className="flex items-center justify-between w-full">
<h3 className="text-lg font-medium text-gray-900">Cost Breakdown</h3>
<div className="flex items-center space-x-2 mr-4">
<span className="text-sm text-gray-500">Total:</span>
<span className="text-sm font-semibold text-gray-900">{formatCost(totalSpend)}</span>
</div>
</div>
),
children: (
<div className="p-6 space-y-4">
{/* Step 1: Base Token Costs */}
<div className="space-y-2 max-w-2xl">
<div className="flex text-sm">
@ -149,8 +153,10 @@ export const CostBreakdownViewer: React.FC<CostBreakdownViewerProps> = ({
</div>
</div>
</div>
</AccordionBody>
</Accordion>
),
},
]}
/>
</div>
);
};

View file

@ -1,5 +1,5 @@
import React, { useState } from "react";
import { Tooltip } from "antd";
import { Tooltip, Collapse } from "antd";
import PresidioDetectedEntities from "./PresidioDetectedEntities";
import BedrockGuardrailDetails, {
BedrockGuardrailResponse,
@ -207,8 +207,6 @@ const GuardrailViewer = ({ data }: GuardrailViewerProps) => {
? [data]
: [];
const [sectionExpanded, setSectionExpanded] = useState(true);
const primaryName =
guardrailEntries.length === 1 ? guardrailEntries[0].guardrail_name : `${guardrailEntries.length} guardrails`;
const statuses = Array.from(new Set(guardrailEntries.map((entry) => entry.guardrail_status)));
@ -231,55 +229,50 @@ const GuardrailViewer = ({ data }: GuardrailViewerProps) => {
}
return (
<div className="bg-white rounded-lg shadow mb-6">
<div
className="flex justify-between items-center p-4 border-b cursor-pointer hover:bg-gray-50"
onClick={() => setSectionExpanded(!sectionExpanded)}
>
<div className="flex items-center gap-2">
<svg
className={`w-5 h-5 text-gray-600 transition-transform ${sectionExpanded ? "transform rotate-90" : ""}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
<h3 className="text-lg font-medium">Guardrail Information</h3>
<div className="bg-white rounded-lg shadow w-full max-w-full overflow-hidden mb-6">
<Collapse
expandIconPosition="start"
items={[
{
key: "1",
label: (
<div className="flex items-center gap-2">
<h3 className="text-lg font-medium text-gray-900">Guardrail Information</h3>
<Tooltip title={tooltipTitle} placement="top" arrow destroyTooltipOnHide>
<span
className={`ml-2 px-2 py-1 rounded-md text-xs font-medium inline-block ${
allSucceeded ? "bg-green-100 text-green-800" : "bg-red-100 text-red-800 cursor-help"
}`}
>
{aggregatedStatus}
</span>
</Tooltip>
<Tooltip title={tooltipTitle} placement="top" arrow destroyTooltipOnHide>
<span
className={`px-2 py-1 rounded-md text-xs font-medium inline-block ${
allSucceeded ? "bg-green-100 text-green-800" : "bg-red-100 text-red-800 cursor-help"
}`}
>
{aggregatedStatus}
</span>
</Tooltip>
<span className="ml-2 font-mono text-sm text-gray-600">{primaryName}</span>
<span className="font-mono text-sm text-gray-600">{primaryName}</span>
{totalMaskedEntities > 0 && (
<span className="ml-2 px-2 py-1 bg-blue-50 text-blue-700 rounded-md text-xs font-medium">
{totalMaskedEntities} masked {totalMaskedEntities === 1 ? "entity" : "entities"}
</span>
)}
</div>
<span className="text-sm text-gray-500">{sectionExpanded ? "Click to collapse" : "Click to expand"}</span>
</div>
{sectionExpanded && (
<div className="p-4 space-y-6">
{guardrailEntries.map((entry, index) => (
<GuardrailDetails
key={`${entry.guardrail_name ?? "guardrail"}-${index}`}
entry={entry}
index={index}
total={guardrailEntries.length}
/>
))}
</div>
)}
{totalMaskedEntities > 0 && (
<span className="px-2 py-1 bg-blue-50 text-blue-700 rounded-md text-xs font-medium">
{totalMaskedEntities} masked {totalMaskedEntities === 1 ? "entity" : "entities"}
</span>
)}
</div>
),
children: (
<div className="p-4 space-y-6">
{guardrailEntries.map((entry, index) => (
<GuardrailDetails
key={`${entry.guardrail_name ?? "guardrail"}-${index}`}
entry={entry}
index={index}
total={guardrailEntries.length}
/>
))}
</div>
),
},
]}
/>
</div>
);
};

View file

@ -1,7 +1,6 @@
import { useState } from "react";
import { Drawer, Typography, Button, Descriptions, Card, Tag, Tabs, Alert, message } from "antd";
import { Drawer, Typography, Button, Descriptions, Card, Tag, Tabs, Alert, message, Collapse } from "antd";
import { CopyOutlined } from "@ant-design/icons";
import { Accordion, AccordionHeader, AccordionBody } from "@tremor/react";
import moment from "moment";
import { LogEntry } from "../columns";
import { formatNumberWithCommas } from "@/utils/dataUtils";
@ -359,56 +358,61 @@ function RequestResponseSection({
return (
<div className="bg-white rounded-lg shadow w-full max-w-full overflow-hidden mb-6">
<Accordion>
<AccordionHeader className="p-4 border-b hover:bg-gray-50 transition-colors text-left">
<h3 className="text-lg font-medium text-gray-900">Request & Response</h3>
</AccordionHeader>
<AccordionBody className="px-0">
<div style={{ padding: "0 24px" }}>
<Tabs
activeKey={activeTab}
onChange={(key) => setActiveTab(key as typeof TAB_REQUEST | typeof TAB_RESPONSE)}
tabBarExtraContent={
<Button
type="text"
size="small"
icon={<CopyOutlined />}
onClick={handleCopy}
disabled={activeTab === TAB_RESPONSE && !hasResponse}
>
Copy
</Button>
}
items={[
{
key: TAB_REQUEST,
label: "Request",
children: (
<div style={{ paddingTop: SPACING_XLARGE, paddingBottom: SPACING_XLARGE }}>
<JsonViewer data={getRawRequest()} mode="formatted" />
</div>
),
},
{
key: TAB_RESPONSE,
label: "Response",
children: (
<div style={{ paddingTop: SPACING_XLARGE, paddingBottom: SPACING_XLARGE }}>
{hasResponse ? (
<JsonViewer data={getFormattedResponse()} mode="formatted" />
) : (
<div style={{ textAlign: "center", padding: 20, color: "#999", fontStyle: "italic" }}>
Response data not available
<Collapse
defaultActiveKey={["1"]}
expandIconPosition="start"
items={[
{
key: "1",
label: <h3 className="text-lg font-medium text-gray-900">Request & Response</h3>,
children: (
<div style={{ padding: "0 24px" }}>
<Tabs
activeKey={activeTab}
onChange={(key) => setActiveTab(key as typeof TAB_REQUEST | typeof TAB_RESPONSE)}
tabBarExtraContent={
<Button
type="text"
size="small"
icon={<CopyOutlined />}
onClick={handleCopy}
disabled={activeTab === TAB_RESPONSE && !hasResponse}
>
Copy
</Button>
}
items={[
{
key: TAB_REQUEST,
label: "Request",
children: (
<div style={{ paddingTop: SPACING_XLARGE, paddingBottom: SPACING_XLARGE }}>
<JsonViewer data={getRawRequest()} mode="formatted" />
</div>
)}
</div>
),
},
]}
/>
</div>
</AccordionBody>
</Accordion>
),
},
{
key: TAB_RESPONSE,
label: "Response",
children: (
<div style={{ paddingTop: SPACING_XLARGE, paddingBottom: SPACING_XLARGE }}>
{hasResponse ? (
<JsonViewer data={getFormattedResponse()} mode="formatted" />
) : (
<div style={{ textAlign: "center", padding: 20, color: "#999", fontStyle: "italic" }}>
Response data not available
</div>
)}
</div>
),
},
]}
/>
</div>
),
},
]}
/>
</div>
);
}
@ -416,36 +420,42 @@ function RequestResponseSection({
function MetadataSection({ metadata, onCopy }: { metadata: Record<string, any>; onCopy: (data: string) => void }) {
return (
<div className="bg-white rounded-lg shadow w-full max-w-full overflow-hidden mb-6">
<Card
title="Metadata"
size="small"
bordered={false}
style={{ marginBottom: 0 }}
extra={
<Button
type="text"
size="small"
icon={<CopyOutlined />}
onClick={() => onCopy(JSON.stringify(metadata, null, 2))}
>
Copy
</Button>
}
>
<pre
style={{
maxHeight: METADATA_MAX_HEIGHT,
overflowY: "auto",
fontSize: FONT_SIZE_SMALL,
fontFamily: FONT_FAMILY_MONO,
whiteSpace: "pre-wrap",
wordBreak: "break-all",
margin: 0,
}}
>
{JSON.stringify(metadata, null, 2)}
</pre>
</Card>
<Collapse
expandIconPosition="start"
items={[
{
key: "1",
label: <h3 className="text-lg font-medium text-gray-900">Metadata</h3>,
children: (
<div>
<div style={{ display: "flex", justifyContent: "flex-end", marginBottom: 8 }}>
<Button
type="text"
size="small"
icon={<CopyOutlined />}
onClick={() => onCopy(JSON.stringify(metadata, null, 2))}
>
Copy
</Button>
</div>
<pre
style={{
maxHeight: METADATA_MAX_HEIGHT,
overflowY: "auto",
fontSize: FONT_SIZE_SMALL,
fontFamily: FONT_FAMILY_MONO,
whiteSpace: "pre-wrap",
wordBreak: "break-all",
margin: 0,
}}
>
{JSON.stringify(metadata, null, 2)}
</pre>
</div>
),
},
]}
/>
</div>
);
}

View file

@ -34,6 +34,7 @@ export function ToolsSection({ log }: ToolsSectionProps) {
return (
<div className="bg-white rounded-lg shadow w-full max-w-full overflow-hidden mb-6">
<Collapse
expandIconPosition="start"
items={[
{
key: "1",

View file

@ -1,4 +1,5 @@
import React, { useState } from "react";
import { Collapse } from "antd";
import { getProviderLogoAndName } from "../provider_info_helpers";
interface VectorStoreContent {
@ -30,7 +31,6 @@ interface VectorStoreViewerProps {
}
export function VectorStoreViewer({ data }: VectorStoreViewerProps) {
const [sectionExpanded, setSectionExpanded] = useState(true);
const [expandedResults, setExpandedResults] = useState<Record<string, boolean>>({});
if (!data || data.length === 0) {
@ -56,27 +56,15 @@ export function VectorStoreViewer({ data }: VectorStoreViewerProps) {
};
return (
<div className="bg-white rounded-lg shadow mb-6">
<div
className="flex justify-between items-center p-4 border-b cursor-pointer hover:bg-gray-50"
onClick={() => setSectionExpanded(!sectionExpanded)}
>
<div className="flex items-center">
<svg
className={`w-5 h-5 mr-2 text-gray-600 transition-transform ${sectionExpanded ? "transform rotate-90" : ""}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
<h3 className="text-lg font-medium">Vector Store Requests</h3>
</div>
<span className="text-sm text-gray-500">{sectionExpanded ? "Click to collapse" : "Click to expand"}</span>
</div>
{sectionExpanded && (
<div className="p-4">
<div className="bg-white rounded-lg shadow w-full max-w-full overflow-hidden mb-6">
<Collapse
expandIconPosition="start"
items={[
{
key: "1",
label: <h3 className="text-lg font-medium text-gray-900">Vector Store Requests</h3>,
children: (
<div className="p-4">
{data.map((request, index) => (
<div key={index} className="mb-6 last:mb-0">
<div className="bg-white rounded-lg border p-4 mb-4">
@ -168,7 +156,10 @@ export function VectorStoreViewer({ data }: VectorStoreViewerProps) {
</div>
))}
</div>
)}
),
},
]}
/>
</div>
);
}