mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-15 23:31:29 +00:00
fix(ui): stop the policy template queue timer chain on unmount
Applying multiple AI-suggested templates schedules each next template via setTimeout(handleUseTemplate, 500) with the timer id discarded, so navigating away mid-queue let the chain keep running: it continued creating guardrails through the API and setting state on the unmounted panel. Store the timer in a ref and clear it in an unmount cleanup. The regression test drives the two-template flow and asserts no further guardrail fetches happen after unmount
This commit is contained in:
parent
2b2ae4ca49
commit
64bd591ccf
2 changed files with 56 additions and 4 deletions
|
|
@ -104,7 +104,12 @@ vi.mock("./add_policy_form", () => ({
|
|||
|
||||
vi.mock("./guardrail_selection_modal", () => ({
|
||||
__esModule: true,
|
||||
default: () => null,
|
||||
default: ({ visible, onConfirm }: { visible: boolean; onConfirm: (defs: unknown[]) => void }) =>
|
||||
visible ? (
|
||||
<button type="button" data-testid="guardrail-selection-confirm" onClick={() => onConfirm([])}>
|
||||
confirm guardrails
|
||||
</button>
|
||||
) : null,
|
||||
}));
|
||||
|
||||
vi.mock("./template_parameter_modal", () => ({
|
||||
|
|
@ -114,7 +119,20 @@ vi.mock("./template_parameter_modal", () => ({
|
|||
|
||||
vi.mock("./ai_suggestion_modal", () => ({
|
||||
__esModule: true,
|
||||
default: () => null,
|
||||
default: ({ onSelectTemplates }: { onSelectTemplates: (templates: unknown[]) => void }) => (
|
||||
<button
|
||||
type="button"
|
||||
data-testid="ai-suggestion-select-two"
|
||||
onClick={() =>
|
||||
onSelectTemplates([
|
||||
{ id: "tmpl-1", guardrailDefinitions: [] },
|
||||
{ id: "tmpl-2", guardrailDefinitions: [] },
|
||||
])
|
||||
}
|
||||
>
|
||||
select two templates
|
||||
</button>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("./policy_test_panel", () => ({
|
||||
|
|
@ -196,3 +214,30 @@ describe("PoliciesPanel attachment delete", () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("PoliciesPanel template queue", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("stops processing queued templates after unmount", async () => {
|
||||
const user = userEvent.setup();
|
||||
const { unmount } = renderWithProviders(<PoliciesPanel accessToken="test-token" userRole="Admin" />);
|
||||
|
||||
await user.click(await screen.findByTestId("ai-suggestion-select-two"));
|
||||
|
||||
const callsAfterFirstTemplate = networkingMocks.getGuardrailsList.mock.calls.length;
|
||||
await user.click(await screen.findByTestId("guardrail-selection-confirm"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(networkingMocks.getGuardrailsList.mock.calls.length).toBe(callsAfterFirstTemplate + 1);
|
||||
});
|
||||
|
||||
const callsBeforeUnmount = networkingMocks.getGuardrailsList.mock.calls.length;
|
||||
unmount();
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 700));
|
||||
|
||||
expect(networkingMocks.getGuardrailsList.mock.calls.length).toBe(callsBeforeUnmount);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import React, { useState, useEffect, useCallback, useRef } from "react";
|
||||
import { Button, TabGroup, TabList, Tab, TabPanels, TabPanel } from "@tremor/react";
|
||||
import { Alert } from "antd";
|
||||
|
||||
|
|
@ -66,6 +66,13 @@ const PoliciesPanel: React.FC<PoliciesPanelProps> = ({ accessToken, userRole })
|
|||
const [loadedTemplates, setLoadedTemplates] = useState<any[]>([]);
|
||||
const [templateQueue, setTemplateQueue] = useState<any[]>([]);
|
||||
const [templateQueueProgress, setTemplateQueueProgress] = useState<{ current: number; total: number } | null>(null);
|
||||
const templateQueueTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (templateQueueTimerRef.current !== null) clearTimeout(templateQueueTimerRef.current);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const isAdmin = userRole ? isAdminRole(userRole) : false;
|
||||
|
||||
|
|
@ -338,7 +345,7 @@ const PoliciesPanel: React.FC<PoliciesPanelProps> = ({ accessToken, userRole })
|
|||
setTemplateQueue(remaining);
|
||||
setTemplateQueueProgress((prev) => (prev ? { ...prev, current: prev.current + 1 } : null));
|
||||
// Small delay so user can see the success message
|
||||
setTimeout(() => handleUseTemplate(nextTemplate), 500);
|
||||
templateQueueTimerRef.current = setTimeout(() => handleUseTemplate(nextTemplate), 500);
|
||||
} else {
|
||||
setTemplateQueueProgress(null);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue