diff --git a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/index.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/index.test.tsx
index 7137da201d8..3224496b13a 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/index.test.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/index.test.tsx
@@ -45,7 +45,7 @@ describe("VectorStoreManagement loading state", () => {
it("should resolve the loading state when accessToken is null instead of showing the skeleton forever", async () => {
const user = userEvent.setup();
- render();
+ render();
await openManageTab(user);
expect(await screen.findByText("table-loaded")).toBeInTheDocument();
expect(mockVectorStoreListCall).not.toHaveBeenCalled();
@@ -59,7 +59,7 @@ describe("VectorStoreManagement loading state", () => {
resolveFetch = resolve;
}),
);
- render();
+ render();
await openManageTab(user);
expect(screen.getByText("table-loading")).toBeInTheDocument();
@@ -69,6 +69,43 @@ describe("VectorStoreManagement loading state", () => {
});
});
+describe("VectorStoreManagement create flow visibility", () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ mockVectorStoreListCall.mockResolvedValue({ data: [] });
+ mockCredentialListCall.mockResolvedValue({ credentials: [] });
+ });
+
+ it.each([
+ { label: "Internal User", userRole: "Internal User", isViewOnly: false },
+ { label: "Internal Viewer", userRole: "Internal Viewer", isViewOnly: true },
+ { label: "proxy_admin_viewer session (userRole Admin, isViewOnly)", userRole: "Admin", isViewOnly: true },
+ { label: "Org Admin", userRole: "Org Admin", isViewOnly: false },
+ ])(
+ "should hide the Create Vector Store tab and button and skip /credentials for $label",
+ async ({ userRole, isViewOnly }) => {
+ render(
+ ,
+ );
+ await waitFor(() => expect(mockVectorStoreListCall).toHaveBeenCalledWith("sk-test"));
+ expect(screen.queryByRole("tab", { name: "Create Vector Store" })).not.toBeInTheDocument();
+ expect(screen.getByRole("tab", { name: "Manage Vector Stores" })).toHaveAttribute("aria-selected", "true");
+ expect(await screen.findByText("table-loaded")).toBeInTheDocument();
+ expect(screen.queryByRole("button", { name: "+ Add Vector Store" })).not.toBeInTheDocument();
+ expect(mockCredentialListCall).not.toHaveBeenCalled();
+ },
+ );
+
+ it("should keep the Create Vector Store tab and button and fetch /credentials for a proxy admin", async () => {
+ const user = userEvent.setup();
+ render();
+ await waitFor(() => expect(mockCredentialListCall).toHaveBeenCalledWith("sk-test"));
+ expect(screen.getByRole("tab", { name: "Create Vector Store" })).toHaveAttribute("aria-selected", "true");
+ await openManageTab(user);
+ expect(screen.getByRole("button", { name: "+ Add Vector Store" })).toBeInTheDocument();
+ });
+});
+
describe("VectorStoreManagement Indexes tab", () => {
beforeEach(() => {
vi.clearAllMocks();
@@ -88,7 +125,7 @@ describe("VectorStoreManagement Indexes tab", () => {
},
],
});
- render();
+ render();
await user.click(screen.getByRole("tab", { name: "Indexes" }));
expect(await screen.findByText("support-docs-index")).toBeInTheDocument();
expect(screen.getByText("support-docs-store")).toBeInTheDocument();
@@ -96,7 +133,7 @@ describe("VectorStoreManagement Indexes tab", () => {
});
it("should not render the Indexes tab for an Admin Viewer", async () => {
- render();
+ render();
await waitFor(() => expect(mockVectorStoreListCall).toHaveBeenCalledWith("sk-test"));
expect(screen.getByRole("tab", { name: "Manage Vector Stores" })).toBeInTheDocument();
expect(screen.queryByRole("tab", { name: "Indexes" })).not.toBeInTheDocument();
@@ -125,7 +162,7 @@ describe("VectorStoreManagement Indexes tab", () => {
},
],
});
- render();
+ render();
await user.click(screen.getByRole("tab", { name: "Indexes" }));
await user.click(await screen.findByRole("button", { name: "support-docs-store" }));
expect(await screen.findByTestId("vector-store-info-view")).toHaveTextContent("vs-1");
@@ -135,7 +172,7 @@ describe("VectorStoreManagement Indexes tab", () => {
it("should link to the feature docs and a GitHub issue for unsupported providers on the Indexes tab", async () => {
const user = userEvent.setup();
mockIndexesListCall.mockResolvedValue({ object: "list", data: [] });
- render();
+ render();
await user.click(screen.getByRole("tab", { name: "Indexes" }));
expect(screen.getByRole("link", { name: "vector store index docs" })).toHaveAttribute(
"href",
@@ -149,7 +186,7 @@ describe("VectorStoreManagement Indexes tab", () => {
});
it("should not call indexesListCall until the Indexes tab is clicked", async () => {
- render();
+ render();
await waitFor(() => expect(mockVectorStoreListCall).toHaveBeenCalledWith("sk-test"));
expect(screen.getByRole("tab", { name: "Indexes" })).toBeInTheDocument();
expect(mockIndexesListCall).not.toHaveBeenCalled();
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/index.tsx b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/index.tsx
index 1745c710c51..285a96520bd 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/index.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/index.tsx
@@ -24,9 +24,10 @@ interface VectorStoreProps {
accessToken: string | null;
userID: string | null;
userRole: string | null;
+ isViewOnly: boolean;
}
-const VectorStoreManagement: React.FC = ({ accessToken, userID, userRole }) => {
+const VectorStoreManagement: React.FC = ({ accessToken, userID, userRole, isViewOnly }) => {
const [vectorStores, setVectorStores] = useState([]);
const [isLoadingVectorStores, setIsLoadingVectorStores] = useState(true);
const [isCreateModalVisible, setIsCreateModalVisible] = useState(false);
@@ -37,7 +38,9 @@ const VectorStoreManagement: React.FC = ({ accessToken, userID
const [selectedVectorStoreId, setSelectedVectorStoreId] = useState(null);
const [editVectorStore, setEditVectorStore] = useState(false);
const [isDeleting, setIsDeleting] = useState(false);
- const { onTabChange, hasVisited } = useVisitedTabs("create");
+ const canCreateVectorStores = isProxyAdminRole(userRole || "") && !isViewOnly;
+ const defaultTab = canCreateVectorStores ? "create" : "manage";
+ const { onTabChange, hasVisited } = useVisitedTabs(defaultTab);
const fetchVectorStores = async () => {
if (!accessToken) {
@@ -56,7 +59,7 @@ const VectorStoreManagement: React.FC = ({ accessToken, userID
};
const fetchCredentials = async () => {
- if (!accessToken) return;
+ if (!accessToken || !canCreateVectorStores) return;
try {
const response = await credentialListCall(accessToken);
setCredentials(response.credentials || []);
@@ -153,11 +156,13 @@ const VectorStoreManagement: React.FC = ({ accessToken, userID
You can use vector stores to store and retrieve LLM embeddings.
-
+
-
- Create Vector Store
-
+ {canCreateVectorStores && (
+
+ Create Vector Store
+
+ )}
Manage Vector Stores
@@ -171,14 +176,18 @@ const VectorStoreManagement: React.FC = ({ accessToken, userID
)}
-
-
-
+ {canCreateVectorStores && (
+
+
+
+ )}
-
+ {canCreateVectorStores && (
+
+ )}
;
+ const { accessToken, userRole, userId, isViewOnly } = useAuthorized();
+ return (
+
+ );
}