diff --git a/enterprise/litellm_enterprise/proxy/vector_stores/endpoints.py b/enterprise/litellm_enterprise/proxy/vector_stores/endpoints.py index 8e3343c1a9..43bdfa3844 100644 --- a/enterprise/litellm_enterprise/proxy/vector_stores/endpoints.py +++ b/enterprise/litellm_enterprise/proxy/vector_stores/endpoints.py @@ -22,6 +22,8 @@ from litellm.types.vector_stores import ( LiteLLM_ManagedVectorStore, LiteLLM_ManagedVectorStoreListResponse, VectorStoreDeleteRequest, + VectorStoreInfoRequest, + VectorStoreUpdateRequest, ) from litellm.vector_stores.vector_store_registry import VectorStoreRegistry @@ -219,3 +221,75 @@ async def delete_vector_store( return {"message": f"Vector store {data.vector_store_id} deleted successfully"} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) + + +@router.post( + "/vector_store/info", + tags=["vector store management"], + dependencies=[Depends(user_api_key_auth)], +) +async def get_vector_store_info( + data: VectorStoreInfoRequest, + user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), +): + """Return a single vector store's details""" + from litellm.proxy.proxy_server import prisma_client + + if prisma_client is None: + raise HTTPException(status_code=500, detail="Database not connected") + + try: + vector_store = await prisma_client.db.litellm_managedvectorstorestable.find_unique( + where={"vector_store_id": data.vector_store_id} + ) + if vector_store is None: + raise HTTPException( + status_code=404, + detail=f"Vector store with ID {data.vector_store_id} not found", + ) + + vector_store_dict = vector_store.model_dump() + return {"vector_store": vector_store_dict} + except Exception as e: + verbose_proxy_logger.exception(f"Error getting vector store info: {str(e)}") + raise HTTPException(status_code=500, detail=str(e)) + + +@router.post( + "/vector_store/update", + tags=["vector store management"], + dependencies=[Depends(user_api_key_auth)], +) +async def update_vector_store( + data: VectorStoreUpdateRequest, + user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), +): + """Update vector store details""" + from litellm.proxy.proxy_server import prisma_client + + if prisma_client is None: + raise HTTPException(status_code=500, detail="Database not connected") + + try: + update_data = data.model_dump(exclude_unset=True) + vector_store_id = update_data.pop("vector_store_id") + if update_data.get("vector_store_metadata") is not None: + update_data["vector_store_metadata"] = safe_dumps(update_data["vector_store_metadata"]) + + updated = await prisma_client.db.litellm_managedvectorstorestable.update( + where={"vector_store_id": vector_store_id}, + data=update_data, + ) + + updated_vs = LiteLLM_ManagedVectorStore(**updated.model_dump()) + + if litellm.vector_store_registry is not None: + litellm.vector_store_registry.update_vector_store_in_registry( + vector_store_id=vector_store_id, + updated_data=updated_vs, + ) + + return {"vector_store": updated_vs} + except Exception as e: + verbose_proxy_logger.exception(f"Error updating vector store: {str(e)}") + raise HTTPException(status_code=500, detail=str(e)) diff --git a/litellm/vector_stores/vector_store_registry.py b/litellm/vector_stores/vector_store_registry.py index a88bb59f84..c5bb809d26 100644 --- a/litellm/vector_stores/vector_store_registry.py +++ b/litellm/vector_stores/vector_store_registry.py @@ -243,6 +243,16 @@ class VectorStoreRegistry: if vector_store.get("vector_store_id") != vector_store_id ] + def update_vector_store_in_registry( + self, vector_store_id: str, updated_data: LiteLLM_ManagedVectorStore + ): + """Update or add a vector store in the registry""" + for i, vector_store in enumerate(self.vector_stores): + if vector_store.get("vector_store_id") == vector_store_id: + self.vector_stores[i] = updated_data + return + self.vector_stores.append(updated_data) + ######################################################### ########### DB management helpers for vector stores ########### ######################################################### diff --git a/ui/litellm-dashboard/src/components/leftnav.tsx b/ui/litellm-dashboard/src/components/leftnav.tsx index 701ac0c929..e50424fb1a 100644 --- a/ui/litellm-dashboard/src/components/leftnav.tsx +++ b/ui/litellm-dashboard/src/components/leftnav.tsx @@ -81,9 +81,9 @@ const Sidebar: React.FC = ({ children: [ { key: "9", page: "caching", label: "Caching", icon: , roles: all_admin_roles }, { key: "10", page: "budgets", label: "Budgets", icon: , roles: all_admin_roles }, + { key: "21", page: "vector-stores", label: "Vector Stores", icon: , roles: all_admin_roles }, { key: "20", page: "transform-request", label: "API Playground", icon: , roles: [...all_admin_roles, ...internalUserRoles] }, { key: "19", page: "tag-management", label: "Tag Management", icon: , roles: all_admin_roles }, - { key: "21", page: "vector-stores", label: "Vector Stores", icon: , roles: all_admin_roles }, { key: "4", page: "usage", label: "Old Usage", icon: }, ] }, diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index 421f87e4e5..98010ee9a8 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -5536,6 +5536,36 @@ export const vectorStoreInfoCall = async ( } }; +export const vectorStoreUpdateCall = async ( + accessToken: string, + formValues: Record +): Promise => { + try { + let url = proxyBaseUrl + ? `${proxyBaseUrl}/vector_store/update` + : `/vector_store/update`; + + const response = await fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${accessToken}`, + }, + body: JSON.stringify(formValues), + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.detail || "Failed to update vector store"); + } + + return await response.json(); + } catch (error) { + console.error("Error updating vector store:", error); + throw error; + } +}; + export const getEmailEventSettings = async ( accessToken: string ): Promise => { 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 436e7c87bc..a7262deff5 100644 --- a/ui/litellm-dashboard/src/components/vector_store_management/VectorStoreTable.tsx +++ b/ui/litellm-dashboard/src/components/vector_store_management/VectorStoreTable.tsx @@ -12,6 +12,7 @@ import { } from "@tremor/react"; import { TrashIcon, + PencilAltIcon, SwitchVerticalIcon, ChevronUpIcon, ChevronDownIcon, @@ -30,11 +31,15 @@ import { getProviderLogoAndName } from "../provider_info_helpers"; interface VectorStoreTableProps { data: VectorStore[]; + onView: (vectorStoreId: string) => void; + onEdit: (vectorStoreId: string) => void; onDelete: (vectorStoreId: string) => void; } const VectorStoreTable: React.FC = ({ data, + onView, + onEdit, onDelete, }) => { const [sorting, setSorting] = React.useState([ @@ -48,13 +53,15 @@ const VectorStoreTable: React.FC = ({ cell: ({ row }) => { const vectorStore = row.original; return ( -
- - - {vectorStore.vector_store_id} - - -
+ ); }, }, @@ -101,7 +108,7 @@ const VectorStoreTable: React.FC = ({ }, }, { - header: "Created", + header: "Created At", accessorKey: "created_at", sortingFn: "datetime", cell: ({ row }) => { @@ -113,6 +120,19 @@ const VectorStoreTable: React.FC = ({ ); }, }, + { + header: "Updated At", + accessorKey: "updated_at", + sortingFn: "datetime", + cell: ({ row }) => { + const vectorStore = row.original; + return ( + + {new Date(vectorStore.updated_at).toLocaleDateString()} + + ); + }, + }, { id: "actions", header: "", @@ -120,6 +140,12 @@ const VectorStoreTable: React.FC = ({ const vectorStore = row.original; return (
+ onEdit(vectorStore.vector_store_id)} + className="cursor-pointer" + /> = ({ const [vectorStoreToDelete, setVectorStoreToDelete] = useState(null); const [lastRefreshed, setLastRefreshed] = useState(""); const [credentials, setCredentials] = useState([]); + const [selectedVectorStoreId, setSelectedVectorStoreId] = useState(null); + const [editVectorStore, setEditVectorStore] = useState(false); const fetchVectorStores = async () => { if (!accessToken) return; @@ -72,6 +76,22 @@ const VectorStoreManagement: React.FC = ({ setIsDeleteModalOpen(true); }; + const handleView = (vectorStoreId: string) => { + setSelectedVectorStoreId(vectorStoreId); + setEditVectorStore(false); + }; + + const handleEdit = (vectorStoreId: string) => { + setSelectedVectorStoreId(vectorStoreId); + setEditVectorStore(true); + }; + + const handleCloseInfo = () => { + setSelectedVectorStoreId(null); + setEditVectorStore(false); + fetchVectorStores(); + }; + const confirmDelete = async () => { if (!accessToken || !vectorStoreToDelete) return; try { @@ -96,7 +116,17 @@ const VectorStoreManagement: React.FC = ({ fetchCredentials(); }, [accessToken]); - return ( + return selectedVectorStoreId ? ( +
+ +
+ ) : (
@@ -128,6 +158,8 @@ const VectorStoreManagement: React.FC = ({ diff --git a/ui/litellm-dashboard/src/components/vector_store_management/vector_store_info.tsx b/ui/litellm-dashboard/src/components/vector_store_management/vector_store_info.tsx index c10d8bbb44..efe708774e 100644 --- a/ui/litellm-dashboard/src/components/vector_store_management/vector_store_info.tsx +++ b/ui/litellm-dashboard/src/components/vector_store_management/vector_store_info.tsx @@ -15,7 +15,8 @@ import { Button as AntButton, } from "antd"; import { InfoCircleOutlined } from '@ant-design/icons'; -import { vectorStoreInfoCall, credentialListCall, CredentialItem } from "../networking"; +import { ArrowLeftIcon } from "@heroicons/react/outline"; +import { vectorStoreInfoCall, vectorStoreUpdateCall, credentialListCall, CredentialItem } from "../networking"; import { VectorStore } from "./types"; import { Providers, providerLogoMap, provider_map } from "../provider_info_helpers"; @@ -105,9 +106,8 @@ const VectorStoreInfoView: React.FC = ({ vector_store_description: values.vector_store_description, vector_store_metadata: metadata, }; - - // Use the updated data to call an update endpoint - // await vectorStoreUpdateCall(accessToken, updateData); + + await vectorStoreUpdateCall(accessToken, updateData); message.success("Vector store updated successfully"); setIsEditing(false); fetchVectorStoreDetails(); @@ -122,10 +122,12 @@ const VectorStoreInfoView: React.FC = ({ } return ( -
+
- + Vector Store ID: {vectorStoreDetails.vector_store_id} {vectorStoreDetails.vector_store_description || "No description"}