From 1f0d54d2fc4d3f331cbb81083f30a91917e145a6 Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Mon, 26 Jan 2026 15:58:00 -0800 Subject: [PATCH] ragIngestCall --- .../src/components/networking.tsx | 17 ++- .../CreateVectorStore.test.tsx | 26 ++-- .../CreateVectorStore.tsx | 117 ++++++++++++++---- .../TestVectorStoreTab.test.tsx | 89 +++++++++++++ .../TestVectorStoreTab.tsx | 75 +++++++++++ .../VectorStoreTable.tsx | 26 ++++ .../vector_store_management/index.tsx | 7 ++ .../vector_store_management/types.tsx | 16 ++- 8 files changed, 337 insertions(+), 36 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/vector_store_management/TestVectorStoreTab.test.tsx create mode 100644 ui/litellm-dashboard/src/components/vector_store_management/TestVectorStoreTab.tsx diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index 2fef2abfad3..a1625b6ffbe 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -6952,7 +6952,9 @@ export const ragIngestCall = async ( accessToken: string, file: File, customLlmProvider: string, - vectorStoreId?: string + vectorStoreId?: string, + vectorStoreName?: string, + vectorStoreDescription?: string ): Promise => { try { let url = proxyBaseUrl ? `${proxyBaseUrl}/rag/ingest` : `/rag/ingest`; @@ -6960,7 +6962,7 @@ export const ragIngestCall = async ( const formData = new FormData(); formData.append("file", file); - const ingestOptions = { + const ingestOptions: any = { ingest_options: { vector_store: { custom_llm_provider: customLlmProvider, @@ -6969,6 +6971,17 @@ export const ragIngestCall = async ( }, }; + // Add litellm_vector_store_params if name or description provided + if (vectorStoreName || vectorStoreDescription) { + ingestOptions.ingest_options.litellm_vector_store_params = {}; + if (vectorStoreName) { + ingestOptions.ingest_options.litellm_vector_store_params.vector_store_name = vectorStoreName; + } + if (vectorStoreDescription) { + ingestOptions.ingest_options.litellm_vector_store_params.vector_store_description = vectorStoreDescription; + } + } + formData.append("request", JSON.stringify(ingestOptions)); const response = await fetch(url, { diff --git a/ui/litellm-dashboard/src/components/vector_store_management/CreateVectorStore.test.tsx b/ui/litellm-dashboard/src/components/vector_store_management/CreateVectorStore.test.tsx index d850afc1720..db2975781ba 100644 --- a/ui/litellm-dashboard/src/components/vector_store_management/CreateVectorStore.test.tsx +++ b/ui/litellm-dashboard/src/components/vector_store_management/CreateVectorStore.test.tsx @@ -16,13 +16,23 @@ vi.mock("../molecules/notifications_manager", () => ({ }, })); -// Mock ProviderLogo -vi.mock("../molecules/models/ProviderLogo", () => ({ - ProviderLogo: ({ provider, className }: { provider: string; className?: string }) => ( -
- {provider} -
- ), +// Mock vector_store_providers +vi.mock("../vector_store_providers", () => ({ + VectorStoreProviders: { + BEDROCK: "Amazon Bedrock", + OPENAI: "OpenAI", + AZURE_OPENAI: "Azure OpenAI", + }, + vectorStoreProviderMap: { + BEDROCK: "bedrock", + OPENAI: "openai", + AZURE_OPENAI: "azure_openai", + }, + vectorStoreProviderLogoMap: { + "Amazon Bedrock": "https://example.com/bedrock.png", + "OpenAI": "https://example.com/openai.png", + "Azure OpenAI": "https://example.com/azure.png", + }, })); describe("CreateVectorStore", () => { @@ -113,7 +123,7 @@ describe("CreateVectorStore", () => { }); await waitFor(() => { - expect(mockRagIngestCall).toHaveBeenCalledWith("test-token", expect.any(File), "openai", undefined); + expect(mockRagIngestCall).toHaveBeenCalledWith("test-token", expect.any(File), "bedrock", undefined); }); }); diff --git a/ui/litellm-dashboard/src/components/vector_store_management/CreateVectorStore.tsx b/ui/litellm-dashboard/src/components/vector_store_management/CreateVectorStore.tsx index 7c61255f40b..685e37ac739 100644 --- a/ui/litellm-dashboard/src/components/vector_store_management/CreateVectorStore.tsx +++ b/ui/litellm-dashboard/src/components/vector_store_management/CreateVectorStore.tsx @@ -1,13 +1,16 @@ import React, { useState } from "react"; import { Card, Title, Text } from "@tremor/react"; -import { Upload, Button, Select, Form, message, Alert } from "antd"; -import { InboxOutlined } from "@ant-design/icons"; +import { Upload, Button, Select, Form, message, Alert, Tooltip, Input } from "antd"; +import { InboxOutlined, InfoCircleOutlined } from "@ant-design/icons"; import type { UploadProps } from "antd"; import { ragIngestCall } from "../networking"; import { DocumentUpload, RAGIngestResponse } from "./types"; import DocumentsTable from "./DocumentsTable"; -import { Providers, provider_map } from "../provider_info_helpers"; -import { ProviderLogo } from "../molecules/models/ProviderLogo"; +import { + VectorStoreProviders, + vectorStoreProviderLogoMap, + vectorStoreProviderMap, +} from "../vector_store_providers"; import NotificationsManager from "../molecules/notifications_manager"; const { Dragger } = Upload; @@ -21,7 +24,9 @@ const CreateVectorStore: React.FC = ({ accessToken, onSu const [form] = Form.useForm(); const [documents, setDocuments] = useState([]); const [isCreating, setIsCreating] = useState(false); - const [selectedProvider, setSelectedProvider] = useState("openai"); + const [selectedProvider, setSelectedProvider] = useState("bedrock"); + const [vectorStoreName, setVectorStoreName] = useState(""); + const [vectorStoreDescription, setVectorStoreDescription] = useState(""); const [ingestResults, setIngestResults] = useState([]); const uploadProps: UploadProps = { @@ -111,7 +116,9 @@ const CreateVectorStore: React.FC = ({ accessToken, onSu accessToken, doc.originFileObj, selectedProvider, - vectorStoreId // Use the same vector store ID for subsequent uploads + vectorStoreId, // Use the same vector store ID for subsequent uploads + vectorStoreName || undefined, + vectorStoreDescription || undefined ); // Store the vector store ID from the first successful ingest @@ -157,8 +164,6 @@ const CreateVectorStore: React.FC = ({ accessToken, onSu } }; - const supportedProviders = ["openai", "bedrock"]; // Add more as needed - return (
@@ -197,18 +202,67 @@ const CreateVectorStore: React.FC = ({ accessToken, onSu )} - {/* Provider Selection and Create Button */} + {/* Provider Selection and Vector Store Details */}
- Step 2: Select Provider + Step 2: Configure Vector Store - Choose the LLM provider for embedding and vector store operations. + Choose the provider and optionally provide a name and description for your vector store.
- + + Vector Store Name{" "} + + + + + } + > + setVectorStoreName(e.target.value)} + placeholder="e.g., Product Documentation, Customer Support KB" + size="large" + className="rounded-md" + /> + + + + Description{" "} + + + + + } + > + setVectorStoreDescription(e.target.value)} + placeholder="e.g., Contains all product documentation and user guides" + rows={2} + size="large" + className="rounded-md" + /> + + + + Provider{" "} + + + + + } + required + > diff --git a/ui/litellm-dashboard/src/components/vector_store_management/TestVectorStoreTab.test.tsx b/ui/litellm-dashboard/src/components/vector_store_management/TestVectorStoreTab.test.tsx new file mode 100644 index 00000000000..541754fe2ad --- /dev/null +++ b/ui/litellm-dashboard/src/components/vector_store_management/TestVectorStoreTab.test.tsx @@ -0,0 +1,89 @@ +import { render, screen, fireEvent } from "@testing-library/react"; +import { describe, it, expect, vi } from "vitest"; +import TestVectorStoreTab from "./TestVectorStoreTab"; +import { VectorStore } from "./types"; + +// Mock VectorStoreTester component +vi.mock("./VectorStoreTester", () => ({ + VectorStoreTester: ({ vectorStoreId, accessToken }: { vectorStoreId: string; accessToken: string }) => ( +
+
{vectorStoreId}
+
{accessToken}
+
+ ), +})); + +const mockVectorStores: VectorStore[] = [ + { + vector_store_id: "vs_123", + custom_llm_provider: "openai", + vector_store_name: "Test Store 1", + vector_store_description: "Description 1", + created_at: "2024-01-01T00:00:00Z", + updated_at: "2024-01-01T00:00:00Z", + }, + { + vector_store_id: "vs_456", + custom_llm_provider: "bedrock", + vector_store_name: "Test Store 2", + vector_store_description: "Description 2", + created_at: "2024-01-02T00:00:00Z", + updated_at: "2024-01-02T00:00:00Z", + }, +]; + +describe("TestVectorStoreTab", () => { + it("should render the component successfully", () => { + render(); + + expect(screen.getByText("Select Vector Store")).toBeInTheDocument(); + expect(screen.getByText("Choose a vector store to test search queries against")).toBeInTheDocument(); + }); + + it("should show message when no access token", () => { + render(); + + expect(screen.getByText("Access token is required to test vector stores.")).toBeInTheDocument(); + }); + + it("should show message when no vector stores available", () => { + render(); + + expect(screen.getByText("No vector stores available. Create one first to test it.")).toBeInTheDocument(); + }); + + it("should render VectorStoreTester with first vector store by default", () => { + render(); + + expect(screen.getByTestId("vector-store-tester")).toBeInTheDocument(); + expect(screen.getByTestId("tester-vector-store-id")).toHaveTextContent("vs_123"); + expect(screen.getByTestId("tester-access-token")).toHaveTextContent("test-token"); + }); + + it("should update VectorStoreTester when selecting different vector store", () => { + render(); + + // Find the select component + const selectElement = screen.getByRole("combobox"); + + // Change selection + fireEvent.mouseDown(selectElement); + + // Wait for options to appear and click the second one + const option2 = screen.getByText("Test Store 2"); + fireEvent.click(option2); + + // Verify the tester component updated + expect(screen.getByTestId("tester-vector-store-id")).toHaveTextContent("vs_456"); + }); + + it("should display vector store names in select options", () => { + render(); + + const selectElement = screen.getByRole("combobox"); + fireEvent.mouseDown(selectElement); + + expect(screen.getByText("Test Store 1")).toBeInTheDocument(); + expect(screen.getByText("Test Store 2")).toBeInTheDocument(); + }); +}); diff --git a/ui/litellm-dashboard/src/components/vector_store_management/TestVectorStoreTab.tsx b/ui/litellm-dashboard/src/components/vector_store_management/TestVectorStoreTab.tsx new file mode 100644 index 00000000000..da005491ca6 --- /dev/null +++ b/ui/litellm-dashboard/src/components/vector_store_management/TestVectorStoreTab.tsx @@ -0,0 +1,75 @@ +import React, { useState } from "react"; +import { Card, Select, Typography } from "antd"; +import { VectorStoreTester } from "./VectorStoreTester"; +import { VectorStore } from "./types"; + +const { Text, Title } = Typography; + +interface TestVectorStoreTabProps { + accessToken: string | null; + vectorStores: VectorStore[]; +} + +const TestVectorStoreTab: React.FC = ({ accessToken, vectorStores }) => { + const [selectedVectorStoreId, setSelectedVectorStoreId] = useState( + vectorStores.length > 0 ? vectorStores[0].vector_store_id : undefined + ); + + if (!accessToken) { + return ( + + Access token is required to test vector stores. + + ); + } + + if (vectorStores.length === 0) { + return ( + +
+ No vector stores available. Create one first to test it. +
+
+ ); + } + + return ( +
+ +
+
+ Select Vector Store + Choose a vector store to test search queries against +
+ + +
+
+ + {selectedVectorStoreId && ( + + )} +
+ ); +}; + +export default TestVectorStoreTab; diff --git a/ui/litellm-dashboard/src/components/vector_store_management/VectorStoreTable.tsx b/ui/litellm-dashboard/src/components/vector_store_management/VectorStoreTable.tsx index 52462c02e98..41e2b63112e 100644 --- a/ui/litellm-dashboard/src/components/vector_store_management/VectorStoreTable.tsx +++ b/ui/litellm-dashboard/src/components/vector_store_management/VectorStoreTable.tsx @@ -66,6 +66,32 @@ const VectorStoreTable: React.FC = ({ data, onView, onEdi ); }, }, + { + header: "Files", + accessorKey: "vector_store_metadata", + cell: ({ row }) => { + const vectorStore = row.original; + const ingestedFiles = vectorStore.vector_store_metadata?.ingested_files || []; + + if (ingestedFiles.length === 0) { + return -; + } + + const filenames = ingestedFiles + .map((file) => file.filename || file.file_url || "Unknown") + .join(", "); + + const displayText = ingestedFiles.length === 1 + ? ingestedFiles[0].filename || ingestedFiles[0].file_url || "1 file" + : `${ingestedFiles.length} files`; + + return ( + + {displayText} + + ); + }, + }, { header: "Provider", accessorKey: "custom_llm_provider", diff --git a/ui/litellm-dashboard/src/components/vector_store_management/index.tsx b/ui/litellm-dashboard/src/components/vector_store_management/index.tsx index bcc5cd8059b..9cb57b8b9f6 100644 --- a/ui/litellm-dashboard/src/components/vector_store_management/index.tsx +++ b/ui/litellm-dashboard/src/components/vector_store_management/index.tsx @@ -8,6 +8,7 @@ import VectorStoreForm from "./VectorStoreForm"; import DeleteResourceModal from "../common_components/DeleteResourceModal"; import VectorStoreInfoView from "./vector_store_info"; import CreateVectorStore from "./CreateVectorStore"; +import TestVectorStoreTab from "./TestVectorStoreTab"; import { isAdminRole } from "@/utils/roles"; import NotificationsManager from "../molecules/notifications_manager"; @@ -148,6 +149,7 @@ const VectorStoreManagement: React.FC = ({ accessToken, userID Create Vector Store Manage Vector Stores + Test Vector Store @@ -173,6 +175,11 @@ const VectorStoreManagement: React.FC = ({ accessToken, userID + + {/* Tab 3: Test Vector Store */} + + + diff --git a/ui/litellm-dashboard/src/components/vector_store_management/types.tsx b/ui/litellm-dashboard/src/components/vector_store_management/types.tsx index bb041fb3683..5be7825e9a1 100644 --- a/ui/litellm-dashboard/src/components/vector_store_management/types.tsx +++ b/ui/litellm-dashboard/src/components/vector_store_management/types.tsx @@ -1,9 +1,23 @@ +export interface IngestedFile { + file_id?: string; + filename?: string; + file_url?: string; + ingested_at: string; + file_size?: number; + content_type?: string; +} + +export interface VectorStoreMetadata { + ingested_files?: IngestedFile[]; + [key: string]: any; +} + export interface VectorStore { vector_store_id: string; custom_llm_provider: string; vector_store_name?: string; vector_store_description?: string; - vector_store_metadata?: Record; + vector_store_metadata?: VectorStoreMetadata; created_at: string; updated_at: string; created_by?: string;