review(#25947): close the request-path gap Greptile flagged on PR #26118

pop_vector_stores_to_run_with_db_fallback applied the same
"in memory but not in DB -> evict" reconcile as list_vector_stores.
The listing fix surfaced config-declared stores in the UI, but the
request path still evicted them on the first inference call, so they
appeared visible but silently unusable.

Extend the same from_litellm_config guard to the request path: skip
the DB round-trip and the eviction branch for config-declared
entries — they are authoritative from proxy_config.yaml and will
never be in the DB table. Runtime entries keep the old semantics
unchanged.

Two new tests:
- test_pop_vector_stores_preserves_config_loaded_on_db_miss —
  direct coverage for the gap: config store is returned, stays in
  the registry, and no DB lookup is issued.
- test_pop_vector_stores_still_evicts_runtime_on_db_miss —
  regression guard: runtime entries without the flag are still
  evicted, as before.

55 tests passing across the registry and endpoint suites
(7 new, 48 pre-existing, no regressions).
This commit is contained in:
sakenuGOD 2026-04-20 19:29:51 +03:00
parent b2c5b3267c
commit 07a8077f97
2 changed files with 105 additions and 3 deletions

View file

@ -370,8 +370,19 @@ class VectorStoreRegistry:
break
# Verify vector store still exists in database (if we have DB access)
# This ensures deleted vector stores are removed from cache
if vector_store is not None and prisma_client is not None:
# This ensures deleted vector stores are removed from cache.
#
# Vector stores declared in proxy_config.yaml are intentionally
# never written to the DB table — verifying them against the DB
# would always miss and they would be evicted on first use,
# making them visible in /vector_store/list (see #25947) but
# silently unreachable during inference. Skip the DB check for
# those entries; they are authoritative from the config file.
if (
vector_store is not None
and prisma_client is not None
and vector_store.get("from_litellm_config") is not True
):
try:
# Check if it still exists in database
db_vector_store = await prisma_client.db.litellm_managedvectorstorestable.find_unique(

View file

@ -13,7 +13,7 @@ sys.path.insert(
) # Adds the parent directory to the system path
from datetime import datetime, timezone
from unittest.mock import MagicMock, patch
from unittest.mock import AsyncMock, MagicMock, patch
import litellm
from litellm.types.vector_stores import LiteLLM_ManagedVectorStore
@ -229,3 +229,94 @@ def test_add_vector_store_to_registry_does_not_mark_from_litellm_config():
assert len(registry.vector_stores) == 1
assert registry.vector_stores[0].get("from_litellm_config") is not True
@pytest.mark.asyncio
async def test_pop_vector_stores_preserves_config_loaded_on_db_miss():
"""
Request-path regression for #25947 (Greptile follow-up): a
vector store loaded from proxy_config.yaml must still be usable
during inference when it is missing from the DB the DB-reconcile
eviction that applies to runtime entries must not fire for
config-declared ones, or they get listed in the UI but silently
evicted on first use.
"""
registry = VectorStoreRegistry(vector_stores=[])
registry.load_vector_stores_from_config(
[
{
"vector_store_name": "config-demo",
"litellm_params": {
"custom_llm_provider": "pg_vector",
"vector_store_id": "vs-config-path",
},
}
]
)
prisma = MagicMock()
# Any DB lookup returns "not found" — mimics a proxy running without
# the managed vector_store table populated.
prisma.db.litellm_managedvectorstorestable.find_unique = AsyncMock(
return_value=None
)
with patch.object(
registry,
"get_litellm_managed_vector_store_from_registry_or_db",
new=AsyncMock(return_value=None),
):
result = await registry.pop_vector_stores_to_run_with_db_fallback(
non_default_params={"vector_store_ids": ["vs-config-path"]},
prisma_client=prisma,
)
assert len(result) == 1
assert result[0]["vector_store_id"] == "vs-config-path"
# Still in the registry — not evicted by the DB-miss reconcile.
ids_after = [vs.get("vector_store_id") for vs in registry.vector_stores]
assert "vs-config-path" in ids_after
# DB path should not have been consulted for a config-declared store —
# we know it will never be there, so asking wastes a round-trip.
prisma.db.litellm_managedvectorstorestable.find_unique.assert_not_called()
@pytest.mark.asyncio
async def test_pop_vector_stores_still_evicts_runtime_on_db_miss():
"""
Regression guard: runtime-inserted entries (no from_litellm_config
flag) must keep the old eviction semantics, otherwise stale cache
after a runtime deletion would never be cleaned up.
"""
from unittest.mock import AsyncMock as _AsyncMock
registry = VectorStoreRegistry(vector_stores=[])
registry.add_vector_store_to_registry(
LiteLLM_ManagedVectorStore(
vector_store_id="vs-runtime-gone",
custom_llm_provider="openai",
vector_store_name="runtime-gone",
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc),
)
)
prisma = MagicMock()
prisma.db.litellm_managedvectorstorestable.find_unique = _AsyncMock(
return_value=None
)
with patch.object(
registry,
"get_litellm_managed_vector_store_from_registry_or_db",
new=_AsyncMock(return_value=None),
):
await registry.pop_vector_stores_to_run_with_db_fallback(
non_default_params={"vector_store_ids": ["vs-runtime-gone"]},
prisma_client=prisma,
)
# Evicted from the in-memory registry, as before the fix.
ids_after = [vs.get("vector_store_id") for vs in registry.vector_stores]
assert "vs-runtime-gone" not in ids_after
prisma.db.litellm_managedvectorstorestable.find_unique.assert_called_once()