address feedback

This commit is contained in:
Ishaan Jaffer 2026-01-30 18:33:01 -08:00
parent 7aeecb999b
commit 2f82ea9997
2 changed files with 57 additions and 123 deletions

View file

@ -1,5 +1,5 @@
import { Button, Tag, Tooltip, Typography } from "antd";
import { CloseOutlined, CopyOutlined, UpOutlined, DownOutlined } from "@ant-design/icons";
import { Button, Space, Tag, Tooltip, Typography } from "antd";
import { CloseOutlined, UpOutlined, DownOutlined } from "@ant-design/icons";
import moment from "moment";
import { LogEntry } from "../columns";
import { getProviderLogoAndName } from "../../provider_info_helpers";
@ -20,7 +20,6 @@ const { Text } = Typography;
interface DrawerHeaderProps {
log: LogEntry;
onClose: () => void;
onCopyRequestId: () => void;
onPrevious: () => void;
onNext: () => void;
statusLabel: string;
@ -35,7 +34,6 @@ interface DrawerHeaderProps {
export function DrawerHeader({
log,
onClose,
onCopyRequestId,
onPrevious,
onNext,
statusLabel,
@ -61,7 +59,7 @@ export function DrawerHeader({
{/* Row 1: Request ID + Actions */}
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: SPACING_MEDIUM }}>
<RequestIdSection requestId={log.request_id} onCopy={onCopyRequestId} />
<RequestIdSection requestId={log.request_id} />
<NavigationSection onPrevious={onPrevious} onNext={onNext} onClose={onClose} />
</div>
@ -84,7 +82,7 @@ function ModelProviderSection({
providerName?: string;
}) {
return (
<div style={{ display: "flex", alignItems: "center", gap: SPACING_MEDIUM, marginBottom: SPACING_MEDIUM }}>
<Space size={SPACING_MEDIUM} style={{ marginBottom: SPACING_MEDIUM }}>
{providerLogo && (
<img
src={providerLogo}
@ -96,43 +94,42 @@ function ModelProviderSection({
}}
/>
)}
<div>
<Space size={SPACING_MEDIUM} direction="horizontal">
<Text strong style={{ fontSize: 14 }}>
{model}
</Text>
{providerName && (
<Text type="secondary" style={{ fontSize: 12, marginLeft: SPACING_MEDIUM }}>
<Text type="secondary" style={{ fontSize: 12 }}>
{providerName}
</Text>
)}
</div>
</div>
</Space>
</Space>
);
}
/**
* Request ID display with copy button
* Request ID display with copy functionality
*/
function RequestIdSection({ requestId, onCopy }: { requestId: string; onCopy: () => void }) {
function RequestIdSection({ requestId }: { requestId: string }) {
return (
<div style={{ display: "flex", alignItems: "center", gap: SPACING_MEDIUM, flex: 1, minWidth: 0 }}>
<div style={{ flex: 1, minWidth: 0 }}>
<Tooltip title={requestId}>
<Text
strong
copyable={{ text: requestId, tooltips: ["Copy Request ID", "Copied!"] }}
style={{
fontSize: FONT_SIZE_HEADER,
fontFamily: FONT_FAMILY_MONO,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
display: "block",
}}
>
{requestId}
</Text>
</Tooltip>
<Tooltip title="Copy Request ID">
<Button type="text" size="small" icon={<CopyOutlined />} onClick={onCopy} />
</Tooltip>
</div>
);
}
@ -161,7 +158,7 @@ function NavigationSection({
};
return (
<div style={{ display: "flex", alignItems: "center", gap: SPACING_SMALL }}>
<Space size={SPACING_SMALL} split={<div style={{ width: 1, height: 20, background: COLOR_BORDER }} />}>
<Button type="text" size="small" onClick={onPrevious}>
<UpOutlined />
<span style={keyboardShortcutStyle}>K</span>
@ -170,13 +167,10 @@ function NavigationSection({
<DownOutlined />
<span style={keyboardShortcutStyle}>J</span>
</Button>
<div style={{ width: 1, height: 20, background: COLOR_BORDER, margin: `0 ${SPACING_MEDIUM}px` }} />
<Tooltip title="ESC to close">
<Button type="text" icon={<CloseOutlined />} onClick={onClose} />
</Tooltip>
</div>
</Space>
);
}
@ -195,13 +189,17 @@ function StatusBar({
environment: string;
}) {
return (
<div style={{ display: "flex", alignItems: "center", gap: SPACING_LARGE }}>
<Space size={SPACING_LARGE}>
<Tag color={statusColor}>{statusLabel}</Tag>
<Tag>Env: {environment}</Tag>
<Text type="secondary" style={{ fontSize: FONT_SIZE_MEDIUM }}>
{moment(log.startTime).format("MMM D, YYYY h:mm:ss A")}
<span style={{ marginLeft: SPACING_MEDIUM }}>({moment(log.startTime).fromNow()})</span>
</Text>
</div>
<Space size={SPACING_MEDIUM}>
<Text type="secondary" style={{ fontSize: FONT_SIZE_MEDIUM }}>
{moment(log.startTime).format("MMM D, YYYY h:mm:ss A")}
</Text>
<Text type="secondary" style={{ fontSize: FONT_SIZE_MEDIUM }}>
({moment(log.startTime).fromNow()})
</Text>
</Space>
</Space>
);
}

View file

@ -1,6 +1,5 @@
import { useState } from "react";
import { Drawer, Typography, Button, Descriptions, Card, Tag, Tabs, Alert, message } from "antd";
import { CopyOutlined } from "@ant-design/icons";
import { Drawer, Typography, Space, Descriptions, Card, Tag, Tabs, Alert } from "antd";
import { Accordion, AccordionHeader, AccordionBody } from "@tremor/react";
import moment from "moment";
import { LogEntry } from "../columns";
@ -13,8 +12,16 @@ import { TruncatedValue } from "./TruncatedValue";
import { TokenFlow } from "./TokenFlow";
import { JsonViewer } from "./JsonViewer";
import { DrawerHeader } from "./DrawerHeader";
import { copyToClipboard } from "./clipboardUtils";
import { useKeyboardNavigation } from "./useKeyboardNavigation";
import {
formatData,
checkHasMessages,
checkHasResponse,
normalizeGuardrailEntries,
calculateTotalMaskedEntities,
getGuardrailLabel,
checkHasVectorStoreData,
} from "./utils";
import {
DRAWER_WIDTH,
DRAWER_CONTENT_PADDING,
@ -25,7 +32,7 @@ import {
FONT_SIZE_SMALL,
FONT_FAMILY_MONO,
SPACING_XLARGE,
MESSAGE_REQUEST_ID_COPIED,
SPACING_MEDIUM,
} from "./constants";
const { Text } = Typography;
@ -94,11 +101,6 @@ export function LogDetailsDrawer({
const statusColor = metadata.status === "failure" ? ("error" as const) : ("success" as const);
const environment = metadata?.user_api_key_team_alias || "default";
const handleCopyRequestId = () => {
navigator.clipboard.writeText(logEntry.request_id);
message.success(MESSAGE_REQUEST_ID_COPIED);
};
const getRawRequest = () => {
return formatData(logEntry.proxy_server_request || logEntry.messages);
};
@ -135,7 +137,6 @@ export function LogDetailsDrawer({
<DrawerHeader
log={logEntry}
onClose={onClose}
onCopyRequestId={handleCopyRequestId}
onPrevious={selectPreviousLog}
onNext={selectNextLog}
statusLabel={statusLabel}
@ -201,7 +202,6 @@ export function LogDetailsDrawer({
{/* Request/Response JSON - Collapsible */}
<RequestResponseSection
hasResponse={hasResponse}
onCopy={(data, label) => copyToClipboard(JSON.stringify(data, null, 2), label)}
getRawRequest={getRawRequest}
getFormattedResponse={getFormattedResponse}
/>
@ -214,7 +214,7 @@ export function LogDetailsDrawer({
{/* Metadata Card - Only show if there's metadata */}
{logEntry.metadata && Object.keys(logEntry.metadata).length > 0 && (
<MetadataSection metadata={logEntry.metadata} onCopy={(data) => copyToClipboard(data, "Metadata")} />
<MetadataSection metadata={logEntry.metadata} />
)}
{/* Bottom spacing for scroll area */}
@ -251,27 +251,27 @@ function TagsSection({ tags }: { tags: Record<string, any> }) {
<Text strong style={{ display: "block", marginBottom: 8, fontSize: 16 }}>
Tags
</Text>
<div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
<Space size={SPACING_MEDIUM} wrap>
{Object.entries(tags).map(([key, value]) => (
<Tag key={key}>
{key}: {String(value)}
</Tag>
))}
</div>
</Space>
</div>
);
}
function GuardrailLabel({ label, maskedCount }: { label: string; maskedCount: number }) {
return (
<>
<Space size={SPACING_MEDIUM}>
<span>{label}</span>
{maskedCount > 0 && (
<Tag color="blue" style={{ marginLeft: 8 }}>
<Tag color="blue">
{maskedCount} masked
</Tag>
)}
</>
</Space>
);
}
@ -334,23 +334,20 @@ function MetricsSection({ logEntry, metadata }: { logEntry: LogEntry; metadata:
interface RequestResponseSectionProps {
hasResponse: boolean;
onCopy: (data: any, label: string) => void;
getRawRequest: () => any;
getFormattedResponse: () => any;
}
function RequestResponseSection({
hasResponse,
onCopy,
getRawRequest,
getFormattedResponse,
}: RequestResponseSectionProps) {
const [activeTab, setActiveTab] = useState<typeof TAB_REQUEST | typeof TAB_RESPONSE>(TAB_REQUEST);
const handleCopy = () => {
const getCopyText = () => {
const data = activeTab === TAB_REQUEST ? getRawRequest() : getFormattedResponse();
const label = activeTab === TAB_REQUEST ? "Request" : "Response";
onCopy(data, label);
return JSON.stringify(data, null, 2);
};
return (
@ -365,15 +362,13 @@ function RequestResponseSection({
activeKey={activeTab}
onChange={(key) => setActiveTab(key as typeof TAB_REQUEST | typeof TAB_RESPONSE)}
tabBarExtraContent={
<Button
type="text"
size="small"
icon={<CopyOutlined />}
onClick={handleCopy}
<Text
copyable={{
text: getCopyText(),
tooltips: ["Copy JSON", "Copied!"]
}}
disabled={activeTab === TAB_RESPONSE && !hasResponse}
>
Copy
</Button>
/>
}
items={[
{
@ -409,7 +404,7 @@ function RequestResponseSection({
);
}
function MetadataSection({ metadata, onCopy }: { metadata: Record<string, any>; onCopy: (data: string) => void }) {
function MetadataSection({ metadata }: { metadata: Record<string, any> }) {
return (
<div className="bg-white rounded-lg shadow w-full max-w-full overflow-hidden mb-6">
<Card
@ -418,14 +413,12 @@ function MetadataSection({ metadata, onCopy }: { metadata: Record<string, any>;
bordered={false}
style={{ marginBottom: 0 }}
extra={
<Button
type="text"
size="small"
icon={<CopyOutlined />}
onClick={() => onCopy(JSON.stringify(metadata, null, 2))}
>
Copy
</Button>
<Text
copyable={{
text: JSON.stringify(metadata, null, 2),
tooltips: ["Copy Metadata", "Copied!"]
}}
/>
}
>
<pre
@ -446,60 +439,3 @@ function MetadataSection({ metadata, onCopy }: { metadata: Record<string, any>;
);
}
// ============================================================================
// Helper Functions
// ============================================================================
function formatData(input: any) {
if (typeof input === "string") {
try {
return JSON.parse(input);
} catch {
return input;
}
}
return input;
}
function checkHasMessages(messages: any): boolean {
if (!messages) return false;
if (Array.isArray(messages)) return messages.length > 0;
if (typeof messages === "object") return Object.keys(messages).length > 0;
return false;
}
function checkHasResponse(response: any): boolean {
if (!response) return false;
return Object.keys(formatData(response)).length > 0;
}
function normalizeGuardrailEntries(guardrailInfo: any): any[] {
if (Array.isArray(guardrailInfo)) return guardrailInfo;
if (guardrailInfo) return [guardrailInfo];
return [];
}
function calculateTotalMaskedEntities(entries: any[]): number {
return entries.reduce((sum, entry) => {
const maskedCounts = entry?.masked_entity_count;
if (!maskedCounts) return sum;
return (
sum +
Object.values(maskedCounts).reduce<number>((acc, count) => (typeof count === "number" ? acc + count : acc), 0)
);
}, 0);
}
function getGuardrailLabel(entries: any[]): string {
if (entries.length === 0) return "-";
if (entries.length === 1) return entries[0]?.guardrail_name ?? "-";
return `${entries.length} guardrails`;
}
function checkHasVectorStoreData(metadata: Record<string, any>): boolean {
return (
metadata.vector_store_request_metadata &&
Array.isArray(metadata.vector_store_request_metadata) &&
metadata.vector_store_request_metadata.length > 0
);
}