bcbd1bdb22
Test (Python) / test-python-gate (push) Blocked by required conditions
Test (TypeScript) / test-typescript-gate (push) Blocked by required conditions
CLI exit codes / cli-gate (push) Blocked by required conditions
Test (Install) / test-install-gate (push) Blocked by required conditions
Integ / integ-gate (push) Blocked by required conditions
CLI exit codes / Python CLI (push) Waiting to run
Integ / changes (push) Has been skipped
Test (Install) / python-minimal (3.11) (push) Waiting to run
Test (Install) / python-minimal (3.12) (push) Waiting to run
Test (Install) / python-extra (chroma, mirage.resource.chroma) (push) Waiting to run
Integ / integ-ts (push) Waiting to run
Integ / integ (push) Waiting to run
Test (Install) / python-extra (deepagents, mirage.agents.langchain) (push) Waiting to run
Integ / integ-database (push) Waiting to run
Test (Install) / python-extra (email, mirage.resource.email) (push) Waiting to run
Test (Install) / python-extra (hdf5, mirage.core.filetype.hdf5) (push) Waiting to run
Integ / integ-ssh (push) Waiting to run
Pre-commit / pre-commit (push) Failing after 1s
CLI exit codes / changes (push) Has been skipped
Test (Install) / changes (push) Has been skipped
CLI exit codes / TypeScript CLI (push) Waiting to run
CLI exit codes / Cross-language snapshot interop (push) Waiting to run
Test (Install) / python-extra (agno, mirage.agents.agno) (push) Waiting to run
Test (Install) / python-extra (databricks, mirage.resource.databricks_volume) (push) Waiting to run
Test (Python) / changes (push) Has been skipped
Integ / integ-database-ts (push) Waiting to run
Test (Install) / python-extra (fuse, mirage.fuse.mount) (push) Waiting to run
Integ / integ-data (push) Waiting to run
Test (Install) / python-extra (lancedb, mirage.resource.lancedb) (push) Waiting to run
Test (Python) / test (push) Waiting to run
Integ / integ-fuse (push) Waiting to run
Test (Install) / python-extra (langfuse, mirage.resource.langfuse) (push) Waiting to run
Test (Python) / import-isolation (deepagents, openai, mirage.agents.openai_agents) (push) Waiting to run
Test (Python) / audit (push) Waiting to run
Test (Install) / python-extra (hf, mirage.resource.hf_buckets) (push) Waiting to run
Integ / integ-ssh-ts (push) Waiting to run
Test (Install) / python-extra (mongodb, mirage.resource.mongodb) (push) Waiting to run
Test (Python) / import-isolation (deepagents, pydantic-ai, mirage.agents.pydantic_ai) (push) Waiting to run
Test (TypeScript) / changes (push) Has been skipped
Test (Install) / python-extra (nextcloud, mirage.resource.nextcloud) (push) Waiting to run
Test (Install) / python-extra (openai, mirage.agents.openai_agents) (push) Waiting to run
Test (Install) / python-extra (openhands, mirage.agents.openhands, 3.12) (push) Waiting to run
Test (Install) / python-extra (parquet, mirage.core.filetype.parquet) (push) Waiting to run
Test (TypeScript) / test (push) Waiting to run
Test (Install) / python-extra (pdf, mirage.core.filetype.pdf) (push) Waiting to run
Test (TypeScript) / python-fs-shim (push) Waiting to run
Test (Install) / python-extra (postgres, mirage.resource.postgres) (push) Waiting to run
Test (Install) / python-extra (pydantic-ai, mirage.agents.pydantic_ai) (push) Waiting to run
Test (Install) / python-extra (qdrant, mirage.resource.qdrant) (push) Waiting to run
Test (Install) / python-extra (redis, mirage.resource.redis) (push) Waiting to run
Test (Install) / python-extra (s3, mirage.resource.s3) (push) Waiting to run
Test (Install) / python-extra (ssh, mirage.resource.ssh) (push) Waiting to run
Test (Install) / ts-minimal (push) Waiting to run
157 lines
4.2 KiB
Python
157 lines
4.2 KiB
Python
import base64
|
|
from difflib import SequenceMatcher
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
|
|
from mirage.resource.qdrant.config import QdrantConfig
|
|
|
|
COLLECTION = "animals"
|
|
|
|
_ROWS = [
|
|
{
|
|
"id": 1,
|
|
"label": "cat",
|
|
"kind": "big",
|
|
"name": "a big orange cat"
|
|
},
|
|
{
|
|
"id": 2,
|
|
"label": "cat",
|
|
"kind": "small",
|
|
"name": "a small grey cat"
|
|
},
|
|
{
|
|
"id": 3,
|
|
"label": "dog",
|
|
"kind": "big",
|
|
"name": "a big brown dog"
|
|
},
|
|
{
|
|
"id": 4,
|
|
"label": "dog",
|
|
"kind": "small",
|
|
"name": "a small white dog"
|
|
},
|
|
]
|
|
|
|
|
|
def _points() -> list[SimpleNamespace]:
|
|
points = []
|
|
for row in _ROWS:
|
|
payload = {
|
|
"label": row["label"],
|
|
"kind": row["kind"],
|
|
"name": row["name"],
|
|
"image_bytes":
|
|
base64.b64encode(f"PNG-{row['id']}".encode()).decode(),
|
|
}
|
|
points.append(SimpleNamespace(id=row["id"], payload=payload))
|
|
return points
|
|
|
|
|
|
def _match(point: SimpleNamespace, scroll_filter) -> bool:
|
|
if scroll_filter is None:
|
|
return True
|
|
for condition in scroll_filter.must:
|
|
value = (point.payload or {}).get(condition.key)
|
|
if str(value) != str(condition.match.value):
|
|
return False
|
|
return True
|
|
|
|
|
|
class FakeQdrantClient:
|
|
|
|
def __init__(self) -> None:
|
|
self.points = _points()
|
|
|
|
async def get_collections(self):
|
|
return SimpleNamespace(collections=[SimpleNamespace(name=COLLECTION)])
|
|
|
|
async def collection_exists(self, name: str) -> bool:
|
|
return name == COLLECTION
|
|
|
|
async def scroll(self,
|
|
collection_name,
|
|
scroll_filter=None,
|
|
limit=10,
|
|
offset=None,
|
|
with_payload=True,
|
|
with_vectors=False):
|
|
matched = [p for p in self.points if _match(p, scroll_filter)]
|
|
start = offset or 0
|
|
window = matched[start:start + limit]
|
|
nxt = start + limit if start + limit < len(matched) else None
|
|
return window, nxt
|
|
|
|
async def retrieve(self,
|
|
collection_name,
|
|
ids,
|
|
with_payload=True,
|
|
with_vectors=False):
|
|
return [p for p in self.points if p.id in ids]
|
|
|
|
async def create_payload_index(self,
|
|
collection_name,
|
|
field_name,
|
|
field_schema=None):
|
|
pass
|
|
|
|
async def query_points(self,
|
|
collection_name,
|
|
query=None,
|
|
limit=10,
|
|
with_payload=True):
|
|
text = query.text if query is not None else ""
|
|
ranked = sorted(
|
|
self.points,
|
|
key=lambda p: SequenceMatcher(
|
|
None, text, str((p.payload or {}).get("name", ""))).ratio(),
|
|
reverse=True,
|
|
)
|
|
scored = []
|
|
for point in ranked[:limit]:
|
|
ratio = SequenceMatcher(None, text,
|
|
str((point.payload
|
|
or {}).get("name", ""))).ratio()
|
|
scored.append(
|
|
SimpleNamespace(id=point.id,
|
|
payload=point.payload,
|
|
score=ratio))
|
|
return SimpleNamespace(points=scored)
|
|
|
|
|
|
class FakeAccessor:
|
|
|
|
def __init__(self, config: QdrantConfig, client: FakeQdrantClient) -> None:
|
|
self.config = config
|
|
self._client = client
|
|
self._search_cache: dict = {}
|
|
self._indexes_ensured: set[str] = set()
|
|
|
|
async def client(self):
|
|
return self._client
|
|
|
|
def cached_search(self, key):
|
|
return self._search_cache.get(key)
|
|
|
|
def store_search(self, key, rows):
|
|
self._search_cache[key] = rows
|
|
|
|
|
|
@pytest.fixture
|
|
def qdrant_config() -> QdrantConfig:
|
|
return QdrantConfig(
|
|
group_by=["label", "kind"],
|
|
id_field="id",
|
|
text_field="name",
|
|
blob_field="image_bytes",
|
|
blob_ext="png",
|
|
vector_field="vector",
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def accessor(qdrant_config) -> FakeAccessor:
|
|
return FakeAccessor(qdrant_config, FakeQdrantClient())
|