diff --git a/ui/litellm-dashboard/src/components/view_logs/ConfigInfoMessage.test.tsx b/ui/litellm-dashboard/src/components/view_logs/ConfigInfoMessage.test.tsx index ad9b724ba80..ede5ce1a8d8 100644 --- a/ui/litellm-dashboard/src/components/view_logs/ConfigInfoMessage.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/ConfigInfoMessage.test.tsx @@ -13,13 +13,22 @@ describe("ConfigInfoMessage", () => { expect(container.innerHTML).toBe(""); }); - it("should display the YAML config snippet", () => { - render(); + it("should display the YAML config snippet when prompt storage is disabled", () => { + render(); expect(screen.getByText(/store_prompts_in_spend_logs: true/)).toBeInTheDocument(); }); - it("should reference Admin Settings \u2192 Logging Settings", () => { - render(); + it("should reference Admin Settings \u2192 Logging Settings when prompt storage is disabled", () => { + render(); expect(screen.getByText(/Admin Settings → Logging Settings/)).toBeInTheDocument(); }); + + it("should not show the enable-storage config hint when prompt storage is already enabled", () => { + render(); + + expect(screen.getByText("Request/Response Data Not Available")).toBeInTheDocument(); + expect(screen.getByText(/Prompt storage is enabled/)).toBeInTheDocument(); + expect(screen.queryByText(/store_prompts_in_spend_logs: true/)).not.toBeInTheDocument(); + expect(screen.queryByText(/Admin Settings → Logging Settings/)).not.toBeInTheDocument(); + }); }); diff --git a/ui/litellm-dashboard/src/components/view_logs/ConfigInfoMessage.tsx b/ui/litellm-dashboard/src/components/view_logs/ConfigInfoMessage.tsx index 8515d510805..e6d81456582 100644 --- a/ui/litellm-dashboard/src/components/view_logs/ConfigInfoMessage.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/ConfigInfoMessage.tsx @@ -2,9 +2,10 @@ import React from "react"; interface ConfigInfoMessageProps { show: boolean; + promptStorageEnabled?: boolean; } -export const ConfigInfoMessage: React.FC = ({ show }) => { +export const ConfigInfoMessage: React.FC = ({ show, promptStorageEnabled = false }) => { if (!show) return null; return ( @@ -28,19 +29,31 @@ export const ConfigInfoMessage: React.FC = ({ show }) =>

Request/Response Data Not Available

-

- To view request and response details, enable prompt storage in your LiteLLM configuration by adding the - following to your proxy_config.yaml file, or - toggle the setting in Admin Settings → Logging Settings. -

-
-          {`general_settings:
+        {promptStorageEnabled ? (
+          

+ Prompt storage is enabled, but no request or response payload was recorded for this request. This usually + means the payload was never captured rather than that storage is misconfigured, for example when a request + failed before the payload was stored (such as a streaming request whose upstream call errored), or when the + payload was written through a different logging integration. Check the request status and any error details + above to see what happened. +

+ ) : ( + <> +

+ To view request and response details, enable prompt storage in your LiteLLM configuration by adding the + following to your proxy_config.yaml file, or + toggle the setting in Admin Settings → Logging Settings. +

+
+              {`general_settings:
   store_model_in_db: true
   store_prompts_in_spend_logs: true`}
-        
-

- Note: This will only affect new requests after the configuration change. -

+
+

+ Note: This will only affect new requests after the configuration change. +

+ + )}
); diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailContent.test.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailContent.test.tsx index 85a38e26977..ceeb3961d6d 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailContent.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailContent.test.tsx @@ -156,6 +156,40 @@ describe("LogDetailContent", () => { expect(screen.getByText("Request/Response Data Not Available")).toBeInTheDocument(); }); + it("should show the enable-storage config hint when payload is missing and prompt storage is disabled", () => { + render( + , + ); + + expect(screen.getByText("Request/Response Data Not Available")).toBeInTheDocument(); + expect(screen.getByText(/store_prompts_in_spend_logs: true/)).toBeInTheDocument(); + expect(screen.queryByText(/Prompt storage is enabled/)).not.toBeInTheDocument(); + }); + + it("should show a per-request explanation (not the config hint) when payload is missing but prompt storage is enabled", () => { + render( + , + ); + + expect(screen.getByText("Request/Response Data Not Available")).toBeInTheDocument(); + expect(screen.getByText(/Prompt storage is enabled/)).toBeInTheDocument(); + expect(screen.queryByText(/store_prompts_in_spend_logs: true/)).not.toBeInTheDocument(); + }); + it("should not display ConfigInfoMessage when isLoadingDetails is true even without data", () => { render( - + )} diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx index 5a49cccec70..b0dca52b90b 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.test.tsx @@ -13,6 +13,14 @@ vi.mock("@/app/(dashboard)/hooks/logDetails/useLogDetails", () => ({ useLogDetails: () => ({ data: null, isLoading: false }), })); +vi.mock("@/app/(dashboard)/hooks/proxyConfig/useProxyConfig", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + getProxyConfigCall: vi.fn().mockResolvedValue([]), + }; +}); + vi.mock("./LogDetailContent", () => ({ LogDetailContent: () => null, GuardrailJumpLink: () => null, diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx index cc087a611b0..0916f93828c 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/LogDetailsDrawer.tsx @@ -14,6 +14,7 @@ import { getSpendString } from "@/utils/dataUtils"; import { normalizeGuardrailEntries, sortSessionLogs, SessionLogSortMode } from "./utils"; import { DRAWER_WIDTH } from "./constants"; import { useLogDetails } from "@/app/(dashboard)/hooks/logDetails/useLogDetails"; +import { ConfigType, getProxyConfigCall, proxyConfigKeys } from "@/app/(dashboard)/hooks/proxyConfig/useProxyConfig"; export interface LogDetailsDrawerProps { open: boolean; @@ -121,6 +122,18 @@ export function LogDetailsDrawer({ const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false); const [copiedLeftPanelId, setCopiedLeftPanelId] = useState(false); + // Proxy-wide store_prompts_in_spend_logs. Shares the query key (and therefore + // the cache) with useProxyConfig so the Logging Settings page and this drawer + // stay consistent. Used to tell apart "storage disabled" (show config hint) + // from "storage on but this request has no payload" in the missing-data notice. + const { data: generalSettings } = useQuery({ + queryKey: proxyConfigKeys.list({ filters: { configType: ConfigType.GENERAL_SETTINGS } }), + queryFn: () => getProxyConfigCall(accessToken!, ConfigType.GENERAL_SETTINGS), + enabled: Boolean(open && accessToken), + }); + const promptStorageEnabled = + generalSettings?.find((field) => field.field_name === "store_prompts_in_spend_logs")?.field_value === true; + const { data: sessionData } = useQuery({ queryKey: ["sessionLogs", sessionId], queryFn: async () => { @@ -463,6 +476,7 @@ export function LogDetailsDrawer({ logEntry={enrichedLog} isLoadingDetails={isLoadingDetails} accessToken={accessToken ?? null} + promptStorageEnabled={promptStorageEnabled} />