fix(ui): don't show enable-prompt-storage hint when storage is already on

This commit is contained in:
Devin AI 2026-07-09 02:09:30 +00:00
parent 9d745486d0
commit ac65bc658b
6 changed files with 107 additions and 18 deletions

View file

@ -13,13 +13,22 @@ describe("ConfigInfoMessage", () => {
expect(container.innerHTML).toBe("");
});
it("should display the YAML config snippet", () => {
render(<ConfigInfoMessage show={true} />);
it("should display the YAML config snippet when prompt storage is disabled", () => {
render(<ConfigInfoMessage show={true} promptStorageEnabled={false} />);
expect(screen.getByText(/store_prompts_in_spend_logs: true/)).toBeInTheDocument();
});
it("should reference Admin Settings \u2192 Logging Settings", () => {
render(<ConfigInfoMessage show={true} />);
it("should reference Admin Settings \u2192 Logging Settings when prompt storage is disabled", () => {
render(<ConfigInfoMessage show={true} promptStorageEnabled={false} />);
expect(screen.getByText(/Admin Settings → Logging Settings/)).toBeInTheDocument();
});
it("should not show the enable-storage config hint when prompt storage is already enabled", () => {
render(<ConfigInfoMessage show={true} promptStorageEnabled={true} />);
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();
});
});

View file

@ -2,9 +2,10 @@ import React from "react";
interface ConfigInfoMessageProps {
show: boolean;
promptStorageEnabled?: boolean;
}
export const ConfigInfoMessage: React.FC<ConfigInfoMessageProps> = ({ show }) => {
export const ConfigInfoMessage: React.FC<ConfigInfoMessageProps> = ({ show, promptStorageEnabled = false }) => {
if (!show) return null;
return (
@ -28,19 +29,31 @@ export const ConfigInfoMessage: React.FC<ConfigInfoMessageProps> = ({ show }) =>
</div>
<div>
<h4 className="text-sm font-medium text-blue-800">Request/Response Data Not Available</h4>
<p className="text-sm text-blue-700 mt-1">
To view request and response details, enable prompt storage in your LiteLLM configuration by adding the
following to your <code className="bg-blue-100 px-1 py-0.5 rounded-sm">proxy_config.yaml</code> file, or
toggle the setting in <strong>Admin Settings → Logging Settings</strong>.
</p>
<pre className="mt-2 bg-white p-3 rounded-sm border border-blue-200 text-xs font-mono overflow-auto">
{`general_settings:
{promptStorageEnabled ? (
<p className="text-sm text-blue-700 mt-1">
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.
</p>
) : (
<>
<p className="text-sm text-blue-700 mt-1">
To view request and response details, enable prompt storage in your LiteLLM configuration by adding the
following to your <code className="bg-blue-100 px-1 py-0.5 rounded-sm">proxy_config.yaml</code> file, or
toggle the setting in <strong>Admin Settings → Logging Settings</strong>.
</p>
<pre className="mt-2 bg-white p-3 rounded-sm border border-blue-200 text-xs font-mono overflow-auto">
{`general_settings:
store_model_in_db: true
store_prompts_in_spend_logs: true`}
</pre>
<p className="text-xs text-blue-700 mt-2">
Note: This will only affect new requests after the configuration change.
</p>
</pre>
<p className="text-xs text-blue-700 mt-2">
Note: This will only affect new requests after the configuration change.
</p>
</>
)}
</div>
</div>
);

View file

@ -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(
<LogDetailContent
logEntry={createLogEntry({
messages: [],
response: {},
metadata: {},
})}
promptStorageEnabled={false}
/>,
);
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(
<LogDetailContent
logEntry={createLogEntry({
messages: [],
response: {},
metadata: {},
})}
promptStorageEnabled={true}
/>,
);
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(
<LogDetailContent

View file

@ -41,6 +41,12 @@ export interface LogDetailContentProps {
/** When true, log details (messages/response) are still being lazy-loaded. */
isLoadingDetails?: boolean;
accessToken?: string | null;
/**
* Whether store_prompts_in_spend_logs is enabled proxy-wide. Controls which
* "data not available" hint is shown: the config instructions when storage is
* off, or a per-request explanation when storage is on but this row has no payload.
*/
promptStorageEnabled?: boolean;
}
/**
@ -51,7 +57,12 @@ export interface LogDetailContentProps {
* Designed to be placed inside LogDetailsDrawer's right panel so it can
* be reused for both single-log and session-mode views.
*/
export function LogDetailContent({ logEntry, isLoadingDetails = false, accessToken }: LogDetailContentProps) {
export function LogDetailContent({
logEntry,
isLoadingDetails = false,
accessToken,
promptStorageEnabled = false,
}: LogDetailContentProps) {
const metadata = logEntry.metadata || {};
const hasError = metadata.status === "failure";
const errorInfo = hasError ? metadata.error_information : null;
@ -157,7 +168,7 @@ export function LogDetailContent({ logEntry, isLoadingDetails = false, accessTok
{/* Configuration Info Message */}
{missingData && (
<div className="mb-6">
<ConfigInfoMessage show={missingData} />
<ConfigInfoMessage show={missingData} promptStorageEnabled={promptStorageEnabled} />
</div>
)}

View file

@ -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<typeof import("@/app/(dashboard)/hooks/proxyConfig/useProxyConfig")>();
return {
...actual,
getProxyConfigCall: vi.fn().mockResolvedValue([]),
};
});
vi.mock("./LogDetailContent", () => ({
LogDetailContent: () => null,
GuardrailJumpLink: () => null,

View file

@ -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}
/>
</div>
</div>