mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-10 22:41:41 +00:00
Fix fallbacks immediately deleting before api resolves
This commit is contained in:
parent
634ec91acc
commit
6bbc17771f
2 changed files with 53 additions and 67 deletions
|
|
@ -1,5 +1,5 @@
|
|||
import { render, waitFor } from "@testing-library/react";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { render, screen, waitFor } from "@testing-library/react";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import Fallbacks from "./fallbacks";
|
||||
import { getCallbacksCall, setCallbacksCall } from "./networking";
|
||||
|
||||
|
|
@ -8,17 +8,6 @@ vi.mock("./networking", () => ({
|
|||
setCallbacksCall: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./molecules/notifications_manager", () => ({
|
||||
__esModule: true,
|
||||
default: {
|
||||
success: vi.fn(),
|
||||
fromBackend: vi.fn(),
|
||||
info: vi.fn(),
|
||||
warning: vi.fn(),
|
||||
clear: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("./add_fallbacks", () => ({
|
||||
__esModule: true,
|
||||
default: () => <div>Mock Add Fallbacks</div>,
|
||||
|
|
@ -29,38 +18,15 @@ vi.mock("openai", () => ({
|
|||
OpenAI: vi.fn().mockImplementation(() => ({
|
||||
chat: {
|
||||
completions: {
|
||||
create: vi.fn(),
|
||||
create: vi.fn().mockResolvedValue({
|
||||
model: "test-model",
|
||||
}),
|
||||
},
|
||||
},
|
||||
})),
|
||||
},
|
||||
}));
|
||||
|
||||
// Polyfill ResizeObserver for components relying on it in tests
|
||||
if (typeof window !== "undefined" && !window.ResizeObserver) {
|
||||
window.ResizeObserver = class ResizeObserver {
|
||||
observe() {}
|
||||
unobserve() {}
|
||||
disconnect() {}
|
||||
};
|
||||
}
|
||||
|
||||
beforeAll(() => {
|
||||
Object.defineProperty(window, "matchMedia", {
|
||||
writable: true,
|
||||
value: vi.fn().mockImplementation((query: string) => ({
|
||||
matches: false,
|
||||
media: query,
|
||||
onchange: null,
|
||||
addListener: vi.fn(),
|
||||
removeListener: vi.fn(),
|
||||
addEventListener: vi.fn(),
|
||||
removeEventListener: vi.fn(),
|
||||
dispatchEvent: vi.fn(),
|
||||
})),
|
||||
});
|
||||
});
|
||||
|
||||
describe("Fallbacks", () => {
|
||||
const defaultProps = {
|
||||
accessToken: "token",
|
||||
|
|
@ -78,15 +44,14 @@ describe("Fallbacks", () => {
|
|||
fallbacks: [],
|
||||
},
|
||||
});
|
||||
mockSetCallbacksCall.mockResolvedValue({});
|
||||
});
|
||||
|
||||
it("should render an empty table with headers when access token is provided", async () => {
|
||||
const { getByText } = render(<Fallbacks {...defaultProps} />);
|
||||
it("should render", async () => {
|
||||
render(<Fallbacks {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getByText("Model Name")).toBeInTheDocument();
|
||||
expect(getByText("Fallbacks")).toBeInTheDocument();
|
||||
expect(getByText("Actions")).toBeInTheDocument();
|
||||
expect(screen.getByRole("columnheader", { name: "Model Name" })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -99,13 +64,13 @@ describe("Fallbacks", () => {
|
|||
|
||||
mockGetCallbacksCall.mockResolvedValue(mockFallbackData);
|
||||
|
||||
const { getByText } = render(<Fallbacks {...defaultProps} />);
|
||||
render(<Fallbacks {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getByText("xai/grok-2")).toBeInTheDocument();
|
||||
expect(getByText("xai/grok-4, gpt-4")).toBeInTheDocument();
|
||||
expect(getByText("gpt-3.5-turbo")).toBeInTheDocument();
|
||||
expect(getByText("gpt-4")).toBeInTheDocument();
|
||||
expect(screen.getByText("xai/grok-2")).toBeInTheDocument();
|
||||
expect(screen.getByText("xai/grok-4, gpt-4")).toBeInTheDocument();
|
||||
expect(screen.getByText("gpt-3.5-turbo")).toBeInTheDocument();
|
||||
expect(screen.getByText("gpt-4")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(mockGetCallbacksCall).toHaveBeenCalledWith(
|
||||
|
|
@ -114,4 +79,17 @@ describe("Fallbacks", () => {
|
|||
defaultProps.userRole,
|
||||
);
|
||||
});
|
||||
|
||||
it("should render AddFallbacks component", async () => {
|
||||
render(<Fallbacks {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Mock Add Fallbacks")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("should not render when access token is not provided", () => {
|
||||
const { container } = render(<Fallbacks {...defaultProps} accessToken={null} />);
|
||||
expect(container.firstChild).toBeNull();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import { PlayIcon, TrashIcon } from "@heroicons/react/outline";
|
||||
import { Icon, Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow } from "@tremor/react";
|
||||
import { Modal, Tooltip } from "antd";
|
||||
import { Tooltip } from "antd";
|
||||
import openai from "openai";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import AddFallbacks from "./add_fallbacks";
|
||||
import DeleteResourceModal from "./common_components/DeleteResourceModal";
|
||||
import NotificationsManager from "./molecules/notifications_manager";
|
||||
import { getCallbacksCall, setCallbacksCall } from "./networking";
|
||||
|
||||
|
|
@ -68,6 +69,7 @@ const Fallbacks: React.FC<FallbacksProps> = ({ accessToken, userRole, userID, mo
|
|||
const [routerSettings, setRouterSettings] = useState<{ [key: string]: any }>({});
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const [fallbackToDelete, setFallbackToDelete] = useState<FallbackEntry | null>(null);
|
||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!accessToken || !userRole || !userID) {
|
||||
|
|
@ -85,6 +87,7 @@ const Fallbacks: React.FC<FallbacksProps> = ({ accessToken, userRole, userID, mo
|
|||
|
||||
const handleDeleteClick = (fallbackEntry: FallbackEntry) => {
|
||||
setFallbackToDelete(fallbackEntry);
|
||||
setIsDeleteModalOpen(true);
|
||||
};
|
||||
|
||||
const handleDeleteConfirm = async () => {
|
||||
|
|
@ -100,10 +103,11 @@ const Fallbacks: React.FC<FallbacksProps> = ({ accessToken, userRole, userID, mo
|
|||
|
||||
const updatedFallbacks = routerSettings["fallbacks"]
|
||||
.map((dict: FallbackEntry) => {
|
||||
if (key in dict && Array.isArray(dict[key])) {
|
||||
delete dict[key];
|
||||
const newDict = { ...dict };
|
||||
if (key in newDict && Array.isArray(newDict[key])) {
|
||||
delete newDict[key];
|
||||
}
|
||||
return dict;
|
||||
return newDict;
|
||||
})
|
||||
.filter((dict: FallbackEntry) => Object.keys(dict).length > 0);
|
||||
|
||||
|
|
@ -124,11 +128,13 @@ const Fallbacks: React.FC<FallbacksProps> = ({ accessToken, userRole, userID, mo
|
|||
NotificationsManager.fromBackend("Failed to update router settings: " + error);
|
||||
} finally {
|
||||
setIsDeleting(false);
|
||||
setIsDeleteModalOpen(false);
|
||||
setFallbackToDelete(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteCancel = () => {
|
||||
setIsDeleteModalOpen(false);
|
||||
setFallbackToDelete(null);
|
||||
};
|
||||
|
||||
|
|
@ -183,20 +189,22 @@ const Fallbacks: React.FC<FallbacksProps> = ({ accessToken, userRole, userID, mo
|
|||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
{fallbackToDelete && (
|
||||
<Modal
|
||||
title="Delete Fallback"
|
||||
open={fallbackToDelete !== null}
|
||||
onOk={handleDeleteConfirm}
|
||||
onCancel={handleDeleteCancel}
|
||||
confirmLoading={isDeleting}
|
||||
okText="Delete"
|
||||
okButtonProps={{ danger: true }}
|
||||
>
|
||||
<p>Are you sure you want to delete fallback: {Object.keys(fallbackToDelete)[0]} ?</p>
|
||||
<p>This action cannot be undone.</p>
|
||||
</Modal>
|
||||
)}
|
||||
<DeleteResourceModal
|
||||
isOpen={isDeleteModalOpen}
|
||||
title="Delete Fallback?"
|
||||
message="Are you sure you want to delete this fallback? This action cannot be undone."
|
||||
resourceInformationTitle="Fallback Information"
|
||||
resourceInformation={[
|
||||
{
|
||||
label: "Model Name",
|
||||
value: fallbackToDelete ? Object.keys(fallbackToDelete)[0] : "",
|
||||
code: true,
|
||||
},
|
||||
]}
|
||||
onCancel={handleDeleteCancel}
|
||||
onOk={handleDeleteConfirm}
|
||||
confirmLoading={isDeleting}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue