diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/DrawerHeader.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/DrawerHeader.tsx index 6aa6410b302..ee1dedba49f 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/DrawerHeader.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/DrawerHeader.tsx @@ -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 */}
;
);
}
-// ============================================================================
-// 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((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): boolean {
- return (
- metadata.vector_store_request_metadata &&
- Array.isArray(metadata.vector_store_request_metadata) &&
- metadata.vector_store_request_metadata.length > 0
- );
-}
diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/clipboardUtils.ts b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/clipboardUtils.ts
deleted file mode 100644
index 6aae95bb72c..00000000000
--- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/clipboardUtils.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import { message } from "antd";
-import { MESSAGE_COPY_SUCCESS } from "./constants";
-
-/**
- * Copies text to clipboard with fallback for non-secure contexts.
- * Shows success/error message to user.
- *
- * @param text - Text to copy to clipboard
- * @param label - Label for the copied content (e.g., "Request", "Metadata")
- * @returns Promise - true if copy succeeded, false otherwise
- */
-export async function copyToClipboard(text: string, label: string): Promise {
- try {
- // Try modern clipboard API first
- if (navigator.clipboard && window.isSecureContext) {
- await navigator.clipboard.writeText(text);
- message.success(`${label} ${MESSAGE_COPY_SUCCESS}`);
- return true;
- } else {
- // Fallback for non-secure contexts (like 0.0.0.0)
- const textArea = document.createElement("textarea");
- textArea.value = text;
- textArea.style.position = "fixed";
- textArea.style.opacity = "0";
- document.body.appendChild(textArea);
- textArea.focus();
- textArea.select();
-
- const successful = document.execCommand("copy");
- document.body.removeChild(textArea);
-
- if (!successful) {
- throw new Error("execCommand failed");
- }
- message.success(`${label} ${MESSAGE_COPY_SUCCESS}`);
- return true;
- }
- } catch (error) {
- console.error("Copy failed:", error);
- message.error(`Failed to copy ${label}`);
- return false;
- }
-}
diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/constants.ts b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/constants.ts
index 91f5ff8f118..3186cc27b42 100644
--- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/constants.ts
+++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/constants.ts
@@ -39,6 +39,4 @@ export const SPACING_LARGE = 12;
export const SPACING_XLARGE = 16;
export const SPACING_XXLARGE = 24;
-// Messages
-export const MESSAGE_COPY_SUCCESS = "copied to clipboard";
-export const MESSAGE_REQUEST_ID_COPIED = "Request ID copied to clipboard";
+// Messages (kept for backwards compatibility if needed elsewhere)
diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.ts b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.ts
new file mode 100644
index 00000000000..61301cf5b54
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/utils.ts
@@ -0,0 +1,93 @@
+/**
+ * Utility functions for LogDetailsDrawer component.
+ * These functions handle data formatting, validation, and guardrail calculations.
+ */
+
+/**
+ * Formats data for display. If input is a string, attempts to parse as JSON.
+ * @param input - Data to format (string or object)
+ * @returns Parsed JSON object or original input
+ */
+export function formatData(input: any) {
+ if (typeof input === "string") {
+ try {
+ return JSON.parse(input);
+ } catch {
+ return input;
+ }
+ }
+ return input;
+}
+
+/**
+ * Checks if messages array/object contains data.
+ * @param messages - Messages to check
+ * @returns True if messages exist and have content
+ */
+export 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;
+}
+
+/**
+ * Checks if response object contains data.
+ * @param response - Response to check
+ * @returns True if response exists and has content
+ */
+export function checkHasResponse(response: any): boolean {
+ if (!response) return false;
+ return Object.keys(formatData(response)).length > 0;
+}
+
+/**
+ * Normalizes guardrail information into an array.
+ * @param guardrailInfo - Guardrail data (may be array, object, or null)
+ * @returns Array of guardrail entries
+ */
+export function normalizeGuardrailEntries(guardrailInfo: any): any[] {
+ if (Array.isArray(guardrailInfo)) return guardrailInfo;
+ if (guardrailInfo) return [guardrailInfo];
+ return [];
+}
+
+/**
+ * Calculates total number of masked entities across all guardrail entries.
+ * @param entries - Array of guardrail entries
+ * @returns Total count of masked entities
+ */
+export 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((acc, count) => (typeof count === "number" ? acc + count : acc), 0)
+ );
+ }, 0);
+}
+
+/**
+ * Gets a display label for guardrail(s).
+ * @param entries - Array of guardrail entries
+ * @returns Display string for guardrail label
+ */
+export function getGuardrailLabel(entries: any[]): string {
+ if (entries.length === 0) return "-";
+ if (entries.length === 1) return entries[0]?.guardrail_name ?? "-";
+ return `${entries.length} guardrails`;
+}
+
+/**
+ * Checks if vector store data exists in metadata.
+ * @param metadata - Metadata object to check
+ * @returns True if vector store data exists and is non-empty
+ */
+export function checkHasVectorStoreData(metadata: Record): boolean {
+ return (
+ metadata.vector_store_request_metadata &&
+ Array.isArray(metadata.vector_store_request_metadata) &&
+ metadata.vector_store_request_metadata.length > 0
+ );
+}