[Feat] UI - Allow clicking into Vector Stores (#12741)

* Add View Vector Store

* add /info for vector store

* fix updated_at
This commit is contained in:
Ishaan Jaff 2025-07-18 14:28:57 -07:00 committed by GitHub
parent 474ac2dd6a
commit 99e2ea081d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 190 additions and 16 deletions

View File

@ -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))

View File

@ -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 ###########
#########################################################

View File

@ -81,9 +81,9 @@ const Sidebar: React.FC<SidebarProps> = ({
children: [
{ key: "9", page: "caching", label: "Caching", icon: <DatabaseOutlined />, roles: all_admin_roles },
{ key: "10", page: "budgets", label: "Budgets", icon: <BankOutlined />, roles: all_admin_roles },
{ key: "21", page: "vector-stores", label: "Vector Stores", icon: <DatabaseOutlined />, roles: all_admin_roles },
{ key: "20", page: "transform-request", label: "API Playground", icon: <ApiOutlined />, roles: [...all_admin_roles, ...internalUserRoles] },
{ key: "19", page: "tag-management", label: "Tag Management", icon: <TagsOutlined />, roles: all_admin_roles },
{ key: "21", page: "vector-stores", label: "Vector Stores", icon: <DatabaseOutlined />, roles: all_admin_roles },
{ key: "4", page: "usage", label: "Old Usage", icon: <BarChartOutlined /> },
]
},

View File

@ -5536,6 +5536,36 @@ export const vectorStoreInfoCall = async (
}
};
export const vectorStoreUpdateCall = async (
accessToken: string,
formValues: Record<string, any>
): Promise<any> => {
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<EmailEventSettingsResponse> => {

View File

@ -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<VectorStoreTableProps> = ({
data,
onView,
onEdit,
onDelete,
}) => {
const [sorting, setSorting] = React.useState<SortingState>([
@ -48,13 +53,15 @@ const VectorStoreTable: React.FC<VectorStoreTableProps> = ({
cell: ({ row }) => {
const vectorStore = row.original;
return (
<div className="overflow-hidden">
<Tooltip title={vectorStore.vector_store_id}>
<span className="font-mono text-blue-500 text-xs font-normal">
{vectorStore.vector_store_id}
</span>
</Tooltip>
</div>
<button
onClick={() => onView(vectorStore.vector_store_id)}
className="font-mono text-blue-500 bg-blue-50 hover:bg-blue-100 text-xs font-normal px-2 py-0.5 text-left w-full truncate whitespace-nowrap cursor-pointer max-w-[15ch]"
>
{vectorStore.vector_store_id.length > 15
? `${vectorStore.vector_store_id.slice(0, 15)}...`
: vectorStore.vector_store_id
}
</button>
);
},
},
@ -101,7 +108,7 @@ const VectorStoreTable: React.FC<VectorStoreTableProps> = ({
},
},
{
header: "Created",
header: "Created At",
accessorKey: "created_at",
sortingFn: "datetime",
cell: ({ row }) => {
@ -113,6 +120,19 @@ const VectorStoreTable: React.FC<VectorStoreTableProps> = ({
);
},
},
{
header: "Updated At",
accessorKey: "updated_at",
sortingFn: "datetime",
cell: ({ row }) => {
const vectorStore = row.original;
return (
<span className="text-xs">
{new Date(vectorStore.updated_at).toLocaleDateString()}
</span>
);
},
},
{
id: "actions",
header: "",
@ -120,6 +140,12 @@ const VectorStoreTable: React.FC<VectorStoreTableProps> = ({
const vectorStore = row.original;
return (
<div className="flex space-x-2">
<Icon
icon={PencilAltIcon}
size="sm"
onClick={() => onEdit(vectorStore.vector_store_id)}
className="cursor-pointer"
/>
<Icon
icon={TrashIcon}
size="sm"

View File

@ -17,6 +17,8 @@ import { VectorStore } from "./types";
import VectorStoreTable from "./VectorStoreTable";
import VectorStoreForm from "./VectorStoreForm";
import DeleteModal from "./DeleteModal";
import VectorStoreInfoView from "./vector_store_info";
import { isAdminRole } from "@/utils/roles";
interface VectorStoreProps {
accessToken: string | null;
@ -35,6 +37,8 @@ const VectorStoreManagement: React.FC<VectorStoreProps> = ({
const [vectorStoreToDelete, setVectorStoreToDelete] = useState<string | null>(null);
const [lastRefreshed, setLastRefreshed] = useState("");
const [credentials, setCredentials] = useState<CredentialItem[]>([]);
const [selectedVectorStoreId, setSelectedVectorStoreId] = useState<string | null>(null);
const [editVectorStore, setEditVectorStore] = useState(false);
const fetchVectorStores = async () => {
if (!accessToken) return;
@ -72,6 +76,22 @@ const VectorStoreManagement: React.FC<VectorStoreProps> = ({
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<VectorStoreProps> = ({
fetchCredentials();
}, [accessToken]);
return (
return selectedVectorStoreId ? (
<div className="w-full h-full">
<VectorStoreInfoView
vectorStoreId={selectedVectorStoreId}
onClose={handleCloseInfo}
accessToken={accessToken}
is_admin={isAdminRole(userRole || "")}
editVectorStore={editVectorStore}
/>
</div>
) : (
<div className="w-full mx-4 h-[75vh]">
<div className="gap-2 p-8 h-[75vh] w-full mt-2">
<div className="flex justify-between mt-2 w-full items-center mb-4">
@ -128,6 +158,8 @@ const VectorStoreManagement: React.FC<VectorStoreProps> = ({
<Col numColSpan={1}>
<VectorStoreTable
data={vectorStores}
onView={handleView}
onEdit={handleEdit}
onDelete={handleDelete}
/>
</Col>

View File

@ -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<VectorStoreInfoViewProps> = ({
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<VectorStoreInfoViewProps> = ({
}
return (
<div className="p-4">
<div className="p-4 max-w-full">
<div className="flex justify-between items-center mb-6">
<div>
<Button onClick={onClose} className="mb-4"> Back to Vector Stores</Button>
<Button icon={ArrowLeftIcon} variant="light" className="mb-4" onClick={onClose}>
Back to Vector Stores
</Button>
<Title>Vector Store ID: {vectorStoreDetails.vector_store_id}</Title>
<Text className="text-gray-500">{vectorStoreDetails.vector_store_description || "No description"}</Text>
</div>