Skip to content

Commit 111983b

Browse files
Teagan42Copilot
andauthored
fix: derive qdrant model params without private api (#60)
* fix: derive qdrant model params without private api * Update mcp_plex/loader.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 8e39cbe commit 111983b

File tree

6 files changed

+59
-3
lines changed

6 files changed

+59
-3
lines changed

AGENTS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
split into batches of five.
2828
- Qdrant upserts are batched and network errors are logged so large loads can
2929
proceed even when individual batches fail.
30+
- Qdrant model metadata is tracked locally in a supported mapping so the loader
31+
no longer depends on the private `_get_model_params` helper removed in newer
32+
clients.
3033

3134
## User Queries
3235
The project should handle natural-language searches and recommendations such as:

mcp_plex/loader.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,17 @@
4747
_imdb_batch_limit: int = 5
4848
_qdrant_batch_size: int = 1000
4949

50+
# Known Qdrant-managed dense embedding models with their dimensionality and
51+
# similarity metric. To support a new server-side embedding model, add an entry
52+
# here with the appropriate vector size and `models.Distance` value.
53+
_DENSE_MODEL_PARAMS: dict[str, tuple[int, models.Distance]] = {
54+
"BAAI/bge-small-en-v1.5": (384, models.Distance.COSINE),
55+
"BAAI/bge-base-en-v1.5": (768, models.Distance.COSINE),
56+
"BAAI/bge-large-en-v1.5": (1024, models.Distance.COSINE),
57+
"text-embedding-3-small": (1536, models.Distance.COSINE),
58+
"text-embedding-3-large": (3072, models.Distance.COSINE),
59+
}
60+
5061

5162
async def _gather_in_batches(
5263
tasks: Sequence[Awaitable[T]], batch_size: int
@@ -62,6 +73,17 @@ async def _gather_in_batches(
6273
return results
6374

6475

76+
def _resolve_dense_model_params(model_name: str) -> tuple[int, models.Distance]:
77+
"""Look up Qdrant vector parameters for a known dense embedding model."""
78+
79+
try:
80+
return _DENSE_MODEL_PARAMS[model_name]
81+
except KeyError as exc:
82+
raise ValueError(
83+
f"Unknown dense embedding model '{model_name}'. Update _DENSE_MODEL_PARAMS with the model's size and distance."
84+
) from exc
85+
86+
6587
async def _fetch_imdb(client: httpx.AsyncClient, imdb_id: str) -> Optional[IMDbTitle]:
6688
"""Fetch metadata for an IMDb ID with caching and retry logic."""
6789

@@ -636,6 +658,7 @@ async def run(
636658
)
637659
)
638660

661+
dense_size, dense_distance = _resolve_dense_model_params(dense_model_name)
639662
if qdrant_url is None and qdrant_host is None:
640663
qdrant_url = ":memory:"
641664
client = AsyncQdrantClient(
@@ -647,7 +670,6 @@ async def run(
647670
https=qdrant_https,
648671
prefer_grpc=qdrant_prefer_grpc,
649672
)
650-
dense_size, dense_distance = client._get_model_params(dense_model_name)
651673
collection_name = "media-items"
652674
created_collection = False
653675
if not await client.collection_exists(collection_name):

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "mcp-plex"
7-
version = "0.26.27"
7+
version = "0.26.28"
88

99
description = "Plex-Oriented Model Context Protocol Server"
1010
requires-python = ">=3.11,<3.13"

tests/test_loader_integration.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from qdrant_client.async_qdrant_client import AsyncQdrantClient
88
from qdrant_client import models
9+
import pytest
910

1011
from mcp_plex import loader
1112

@@ -97,3 +98,20 @@ def test_run_upserts_in_batches(monkeypatch):
9798
asyncio.run(_run_loader(sample_dir))
9899
assert CaptureClient.upsert_calls == 2
99100
assert len(CaptureClient.captured_points) == 2
101+
102+
103+
def test_run_raises_for_unknown_dense_model():
104+
sample_dir = Path(__file__).resolve().parents[1] / "sample-data"
105+
106+
with pytest.raises(ValueError, match="Unknown dense embedding model"):
107+
asyncio.run(
108+
loader.run(
109+
None,
110+
None,
111+
None,
112+
sample_dir,
113+
None,
114+
None,
115+
dense_model_name="not-a-real/model",
116+
)
117+
)

tests/test_loader_unit.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import httpx
99
from qdrant_client import models
10+
import pytest
1011

1112
from mcp_plex import loader
1213
from mcp_plex.imdb_cache import IMDbCache
@@ -22,6 +23,7 @@
2223
_load_imdb_retry_queue,
2324
_persist_imdb_retry_queue,
2425
_process_imdb_retry_queue,
26+
_resolve_dense_model_params,
2527
resolve_tmdb_season_number,
2628
)
2729
from mcp_plex.types import TMDBSeason, TMDBShow
@@ -482,3 +484,14 @@ async def upsert(self, collection_name: str, points, **kwargs):
482484
monkeypatch.setattr(loader, "_qdrant_batch_size", 1)
483485
asyncio.run(loader._upsert_in_batches(client, "c", points))
484486
assert client.calls == 3
487+
488+
489+
def test_resolve_dense_model_params_known_model():
490+
size, distance = _resolve_dense_model_params("BAAI/bge-small-en-v1.5")
491+
assert size == 384
492+
assert distance is models.Distance.COSINE
493+
494+
495+
def test_resolve_dense_model_params_unknown_model():
496+
with pytest.raises(ValueError, match="Unknown dense embedding model"):
497+
_resolve_dense_model_params("not-a-real/model")

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)