diff --git a/ui/litellm-dashboard/src/components/view_logs/RequestResponsePanel.test.tsx b/ui/litellm-dashboard/src/components/view_logs/RequestResponsePanel.test.tsx
new file mode 100644
index 0000000000..d163be057b
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/view_logs/RequestResponsePanel.test.tsx
@@ -0,0 +1,164 @@
+import { render, screen, act } from "@testing-library/react";
+import { describe, expect, it, vi, beforeEach } from "vitest";
+import userEvent from "@testing-library/user-event";
+import { RequestResponsePanel } from "./RequestResponsePanel";
+import type { LogEntry } from "./columns";
+import NotificationsManager from "../molecules/notifications_manager";
+
+const mockNotificationsManager = vi.mocked(NotificationsManager);
+
+const baseLogEntry: LogEntry = {
+ request_id: "chatcmpl-test-id",
+ api_key: "api-key",
+ team_id: "team-id",
+ model: "gpt-4",
+ model_id: "gpt-4",
+ call_type: "chat",
+ spend: 0,
+ total_tokens: 0,
+ prompt_tokens: 0,
+ completion_tokens: 0,
+ startTime: "2025-11-14T00:00:00Z",
+ endTime: "2025-11-14T00:00:00Z",
+ cache_hit: "miss",
+ duration: 1,
+ messages: [{ role: "user", content: "hello" }],
+ response: { status: "ok" },
+ metadata: {
+ status: "success",
+ additional_usage_values: {
+ cache_read_input_tokens: 0,
+ cache_creation_input_tokens: 0,
+ },
+ },
+ request_tags: {},
+ custom_llm_provider: "openai",
+ api_base: "https://api.example.com",
+};
+
+describe("RequestResponsePanel", () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ Object.defineProperty(navigator, "clipboard", {
+ value: {
+ writeText: vi.fn().mockResolvedValue(undefined),
+ },
+ writable: true,
+ configurable: true,
+ });
+ Object.defineProperty(window, "isSecureContext", {
+ value: true,
+ writable: true,
+ configurable: true,
+ });
+ });
+
+ it("should render the component with request and response panels", () => {
+ const mockGetRawRequest = vi.fn().mockReturnValue({ test: "request" });
+ const mockFormattedResponse = vi.fn().mockReturnValue({ test: "response" });
+
+ render(
+ ,
+ );
+
+ expect(screen.getByText("Request")).toBeInTheDocument();
+ expect(screen.getByText("Response")).toBeInTheDocument();
+ });
+
+ it("should copy request to clipboard when copy button is clicked", async () => {
+ const user = userEvent.setup();
+ const mockGetRawRequest = vi.fn().mockReturnValue({ test: "request data" });
+ const mockFormattedResponse = vi.fn().mockReturnValue({ test: "response" });
+ const mockWriteText = vi.fn().mockResolvedValue(undefined);
+
+ if (navigator.clipboard) {
+ vi.spyOn(navigator.clipboard, "writeText").mockImplementation(mockWriteText);
+ } else {
+ Object.defineProperty(navigator, "clipboard", {
+ value: {
+ writeText: mockWriteText,
+ },
+ writable: true,
+ configurable: true,
+ });
+ }
+
+ render(
+ ,
+ );
+
+ const copyButtons = screen.getAllByRole("button");
+ const copyRequestButton = copyButtons.find((button) => button.getAttribute("title") === "Copy request");
+
+ expect(copyRequestButton).toBeInTheDocument();
+
+ await act(async () => {
+ await user.click(copyRequestButton!);
+ });
+
+ expect(mockGetRawRequest).toHaveBeenCalled();
+ expect(mockWriteText).toHaveBeenCalledWith(JSON.stringify({ test: "request data" }, null, 2));
+ expect(mockNotificationsManager.success).toHaveBeenCalledWith("Request copied to clipboard");
+ });
+
+ it("should copy response to clipboard when copy button is clicked", async () => {
+ const user = userEvent.setup();
+ const mockGetRawRequest = vi.fn().mockReturnValue({ test: "request" });
+ const mockFormattedResponse = vi.fn().mockReturnValue({ test: "response data" });
+ const mockWriteText = vi.fn().mockResolvedValue(undefined);
+
+ if (navigator.clipboard) {
+ vi.spyOn(navigator.clipboard, "writeText").mockImplementation(mockWriteText);
+ } else {
+ Object.defineProperty(navigator, "clipboard", {
+ value: {
+ writeText: mockWriteText,
+ },
+ writable: true,
+ configurable: true,
+ });
+ }
+
+ render(
+ ,
+ );
+
+ const copyButtons = screen.getAllByRole("button");
+ const copyResponseButton = copyButtons.find((button) => button.getAttribute("title") === "Copy response");
+
+ expect(copyResponseButton).toBeInTheDocument();
+ expect(copyResponseButton).not.toBeDisabled();
+
+ await act(async () => {
+ await user.click(copyResponseButton!);
+ });
+
+ expect(mockFormattedResponse).toHaveBeenCalled();
+ expect(mockWriteText).toHaveBeenCalledWith(JSON.stringify({ test: "response data" }, null, 2));
+ expect(mockNotificationsManager.success).toHaveBeenCalledWith("Response copied to clipboard");
+ });
+});
diff --git a/ui/litellm-dashboard/src/components/view_logs/RequestResponsePanel.tsx b/ui/litellm-dashboard/src/components/view_logs/RequestResponsePanel.tsx
index 407d99fdd7..97a9272f1e 100644
--- a/ui/litellm-dashboard/src/components/view_logs/RequestResponsePanel.tsx
+++ b/ui/litellm-dashboard/src/components/view_logs/RequestResponsePanel.tsx
@@ -1,6 +1,7 @@
import { LogEntry } from "./columns";
import NotificationsManager from "../molecules/notifications_manager";
-import { JsonView, allExpanded, defaultStyles } from 'react-json-view-lite';
+import { JsonView, defaultStyles } from "react-json-view-lite";
+import "react-json-view-lite/dist/index.css";
interface RequestResponsePanelProps {
row: {
@@ -95,15 +96,7 @@ export function RequestResponsePanel({
-
- {/* {JSON.stringify(getRawRequest(), null, 2)} */}
-
-
+
@@ -136,17 +129,9 @@ export function RequestResponsePanel({
-
+
{hasResponse ? (
-
- {/* {JSON.stringify(formattedResponse(), null, 2)} */}
-
-
+
) : (
Response data not available
)}