mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-07 02:59:05 +00:00
fix(vector_stores): keep config-defined vector stores when /vector_store/list reconciles with the DB
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
55e9e4ce2f
commit
f577c7d3a4
3 changed files with 53 additions and 2 deletions
|
|
@ -613,6 +613,7 @@ async def list_vector_stores(
|
|||
# Process in-memory vector stores
|
||||
if litellm.vector_store_registry is not None:
|
||||
in_memory_vector_stores: Final = copy.deepcopy(litellm.vector_store_registry.vector_stores)
|
||||
config_vector_store_ids: Final = litellm.vector_store_registry.config_vector_store_ids
|
||||
|
||||
vector_stores_to_delete_from_memory: Final[list[str]] = []
|
||||
|
||||
|
|
@ -621,8 +622,7 @@ async def list_vector_stores(
|
|||
if not vector_store_id:
|
||||
continue
|
||||
|
||||
# If vector store is in memory but NOT in database, it was deleted
|
||||
if vector_store_id not in db_vector_store_ids:
|
||||
if vector_store_id not in db_vector_store_ids and vector_store_id not in config_vector_store_ids:
|
||||
verbose_proxy_logger.info(
|
||||
"Vector store %s exists in memory but not in database - marking for deletion from cache",
|
||||
vector_store_id,
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ class VectorStoreRegistry:
|
|||
def __init__(self, vector_stores: list[LiteLLM_ManagedVectorStore] = []):
|
||||
self.vector_stores: list[LiteLLM_ManagedVectorStore] = vector_stores
|
||||
self.vector_store_ids_to_vector_store_map: dict[str, LiteLLM_ManagedVectorStore] = {}
|
||||
self.config_vector_store_ids: set[str] = set()
|
||||
|
||||
def _extract_tool_params(self, tool: dict) -> VectorStoreToolParams:
|
||||
"""
|
||||
|
|
@ -429,6 +430,7 @@ class VectorStoreRegistry:
|
|||
updated_at=datetime.now(timezone.utc),
|
||||
)
|
||||
self.vector_stores.append(litellm_managed_vector_store)
|
||||
self.config_vector_store_ids.add(vector_store_id)
|
||||
|
||||
verbose_logger.debug(
|
||||
"all loaded vector stores = %s",
|
||||
|
|
@ -474,6 +476,7 @@ class VectorStoreRegistry:
|
|||
for vector_store in self.vector_stores
|
||||
if vector_store.get("vector_store_id") != vector_store_id
|
||||
]
|
||||
self.config_vector_store_ids.discard(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"""
|
||||
|
|
|
|||
|
|
@ -2002,6 +2002,54 @@ async def test_vector_store_update_and_list_synchronization():
|
|||
), "List should return updated data from database"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_vector_stores_keeps_config_defined_store_missing_from_db():
|
||||
"""
|
||||
Stores from config.yaml `vector_store_registry` are only loaded in memory and never written
|
||||
to the DB. Listing must not treat that DB miss as a deletion and evict them, while a
|
||||
DB-sourced store that disappeared from the DB is still evicted.
|
||||
"""
|
||||
from litellm.proxy.vector_store_endpoints.management_endpoints import list_vector_stores
|
||||
from litellm.vector_stores.vector_store_registry import VectorStoreRegistry
|
||||
|
||||
registry = VectorStoreRegistry(vector_stores=[])
|
||||
registry.load_vector_stores_from_config(
|
||||
[
|
||||
{
|
||||
"vector_store_name": "config-search-store",
|
||||
"litellm_params": {
|
||||
"vector_store_id": "config-search-store",
|
||||
"custom_llm_provider": "azure_ai",
|
||||
"api_key": "fake",
|
||||
"api_base": "https://example.search.windows.net",
|
||||
},
|
||||
}
|
||||
]
|
||||
)
|
||||
stale_db_store: LiteLLM_ManagedVectorStore = {
|
||||
"vector_store_id": "deleted-elsewhere",
|
||||
"custom_llm_provider": "bedrock",
|
||||
"litellm_params": {"vector_store_id": "deleted-elsewhere", "custom_llm_provider": "bedrock"},
|
||||
}
|
||||
registry.add_vector_store_to_registry(vector_store=stale_db_store)
|
||||
|
||||
admin = UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN.value, user_id="admin")
|
||||
with (
|
||||
patch.dict("litellm.proxy.proxy_server.general_settings", {}, clear=True),
|
||||
patch("litellm.proxy.proxy_server.prisma_client", MagicMock()),
|
||||
patch.object(litellm, "vector_store_registry", registry),
|
||||
patch(
|
||||
"litellm.proxy.vector_store_endpoints.management_endpoints.VectorStoreRegistry._get_vector_stores_from_db",
|
||||
new=AsyncMock(return_value=[]),
|
||||
),
|
||||
):
|
||||
response = await list_vector_stores(user_api_key_dict=admin)
|
||||
|
||||
assert [vs.get("vector_store_id") for vs in response["data"]] == ["config-search-store"]
|
||||
assert registry.get_litellm_managed_vector_store_from_registry_by_name("config-search-store") is not None
|
||||
assert registry.get_litellm_managed_vector_store_from_registry("deleted-elsewhere") is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_resolve_embedding_config_from_db():
|
||||
"""Test that _resolve_embedding_config_from_db correctly resolves embedding config from database."""
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue