[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:
parent
474ac2dd6a
commit
99e2ea081d
@ -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))
|
||||
|
||||
@ -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 ###########
|
||||
#########################################################
|
||||
|
||||
@ -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 /> },
|
||||
]
|
||||
},
|
||||
|
||||
@ -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> => {
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user