bcbd1bdb22
Integ / changes (push) Has been skipped
Pre-commit / pre-commit (push) Failing after 1s
CLI exit codes / changes (push) Has been skipped
Test (Install) / changes (push) Has been skipped
Test (Python) / changes (push) Has been skipped
Test (TypeScript) / changes (push) Has been skipped
CLI exit codes / cli-gate (push) Has been cancelled
Test (Install) / test-install-gate (push) Has been cancelled
Integ / integ-gate (push) Has been cancelled
Test (Python) / test-python-gate (push) Has been cancelled
Test (TypeScript) / test-typescript-gate (push) Has been cancelled
Test (Install) / python-minimal (3.12) (push) Has been cancelled
Test (Install) / python-minimal (3.11) (push) Has been cancelled
Test (Install) / python-extra (agno, mirage.agents.agno) (push) Has been cancelled
Test (Install) / python-extra (chroma, mirage.resource.chroma) (push) Has been cancelled
Test (Install) / python-extra (pdf, mirage.core.filetype.pdf) (push) Has been cancelled
Integ / integ (push) Has been cancelled
Integ / integ-database (push) Has been cancelled
Integ / integ-database-ts (push) Has been cancelled
Integ / integ-data (push) Has been cancelled
Integ / integ-ssh (push) Has been cancelled
Integ / integ-ssh-ts (push) Has been cancelled
Test (Python) / audit (push) Has been cancelled
Test (TypeScript) / test (push) Has been cancelled
Test (TypeScript) / python-fs-shim (push) Has been cancelled
CLI exit codes / Python CLI (push) Has been cancelled
CLI exit codes / TypeScript CLI (push) Has been cancelled
CLI exit codes / Cross-language snapshot interop (push) Has been cancelled
Test (Python) / test (push) Has been cancelled
Test (Python) / import-isolation (deepagents, openai, mirage.agents.openai_agents) (push) Has been cancelled
Test (Python) / import-isolation (deepagents, pydantic-ai, mirage.agents.pydantic_ai) (push) Has been cancelled
Integ / integ-ts (push) Has been cancelled
Integ / integ-fuse (push) Has been cancelled
Test (Install) / python-extra (databricks, mirage.resource.databricks_volume) (push) Has been cancelled
Test (Install) / python-extra (deepagents, mirage.agents.langchain) (push) Has been cancelled
Test (Install) / python-extra (email, mirage.resource.email) (push) Has been cancelled
Test (Install) / python-extra (fuse, mirage.fuse.mount) (push) Has been cancelled
Test (Install) / python-extra (hdf5, mirage.core.filetype.hdf5) (push) Has been cancelled
Test (Install) / python-extra (hf, mirage.resource.hf_buckets) (push) Has been cancelled
Test (Install) / python-extra (lancedb, mirage.resource.lancedb) (push) Has been cancelled
Test (Install) / python-extra (langfuse, mirage.resource.langfuse) (push) Has been cancelled
Test (Install) / python-extra (mongodb, mirage.resource.mongodb) (push) Has been cancelled
Test (Install) / python-extra (nextcloud, mirage.resource.nextcloud) (push) Has been cancelled
Test (Install) / python-extra (openai, mirage.agents.openai_agents) (push) Has been cancelled
Test (Install) / python-extra (openhands, mirage.agents.openhands, 3.12) (push) Has been cancelled
Test (Install) / python-extra (parquet, mirage.core.filetype.parquet) (push) Has been cancelled
Test (Install) / python-extra (postgres, mirage.resource.postgres) (push) Has been cancelled
Test (Install) / python-extra (pydantic-ai, mirage.agents.pydantic_ai) (push) Has been cancelled
Test (Install) / python-extra (qdrant, mirage.resource.qdrant) (push) Has been cancelled
Test (Install) / python-extra (redis, mirage.resource.redis) (push) Has been cancelled
Test (Install) / python-extra (s3, mirage.resource.s3) (push) Has been cancelled
Test (Install) / python-extra (ssh, mirage.resource.ssh) (push) Has been cancelled
Test (Install) / ts-minimal (push) Has been cancelled
122 lines
4.3 KiB
Python
122 lines
4.3 KiB
Python
from typing import Any
|
|
|
|
from mirage.cache.index import IndexCacheStore, IndexEntry
|
|
from mirage.core.chroma.path import resolve_path
|
|
from mirage.core.chroma.walk import walk
|
|
from mirage.types import PathSpec
|
|
from mirage.utils.key_prefix import mount_prefix_of, rekey
|
|
from mirage.utils.score import score_from_distance
|
|
|
|
|
|
async def search_segments(
|
|
accessor,
|
|
query: str,
|
|
paths: list[PathSpec],
|
|
index: IndexCacheStore,
|
|
top_k: int = 10,
|
|
mount_prefix: str = "",
|
|
) -> bytes:
|
|
validate_args(query, top_k)
|
|
if not mount_prefix and paths:
|
|
mount_prefix = mount_prefix_of(paths[0].virtual,
|
|
paths[0].resource_path)
|
|
kwargs: dict[str, Any] = {
|
|
"query_texts": [query],
|
|
"n_results": top_k,
|
|
"include": ["documents", "metadatas", "distances"],
|
|
}
|
|
scoped_slugs: set[str] | None = None
|
|
if paths:
|
|
scoped_slugs = set((await target_entries(accessor, paths,
|
|
index)).keys())
|
|
if not scoped_slugs:
|
|
return b""
|
|
kwargs["where"] = {
|
|
accessor.config.slug_field: {
|
|
"$in": sorted(scoped_slugs)
|
|
}
|
|
}
|
|
collection = await accessor.get_collection()
|
|
response = await collection.query(**kwargs)
|
|
return query_result_to_bytes(response, accessor.config.slug_field,
|
|
mount_prefix, scoped_slugs)
|
|
|
|
|
|
def validate_args(query: str, top_k: int) -> None:
|
|
if not query:
|
|
raise ValueError("search: query is required")
|
|
if len(query) > 250:
|
|
raise ValueError("search: query cannot exceed 250 characters")
|
|
if top_k <= 0:
|
|
raise ValueError("search: top-k must be positive")
|
|
|
|
|
|
async def target_entries(
|
|
accessor,
|
|
paths: list[PathSpec],
|
|
index: IndexCacheStore,
|
|
) -> dict[str, IndexEntry]:
|
|
targets: dict[str, IndexEntry] = {}
|
|
for path in paths:
|
|
resolved = await resolve_path(accessor, path, index)
|
|
if resolved.entry is not None and not resolved.is_dir:
|
|
targets[str(resolved.entry.extra["slug"])] = resolved.entry
|
|
continue
|
|
if resolved.is_dir:
|
|
children = await walk(accessor,
|
|
path,
|
|
index,
|
|
include_root=False,
|
|
strip_prefix=False)
|
|
for child in children:
|
|
child_spec = PathSpec.from_str_path(
|
|
child, rekey(path.virtual, path.resource_path, child))
|
|
child_resolved = await resolve_path(accessor, child_spec,
|
|
index)
|
|
if (child_resolved.entry is not None
|
|
and not child_resolved.is_dir):
|
|
targets[str(child_resolved.entry.extra["slug"]
|
|
)] = child_resolved.entry
|
|
return targets
|
|
|
|
|
|
def query_result_to_bytes(
|
|
response: dict[str, Any],
|
|
slug_field: str,
|
|
mount_prefix: str,
|
|
scoped_slugs: set[str] | None = None,
|
|
) -> bytes:
|
|
documents = first_result_list(response.get("documents"))
|
|
metadatas = first_result_list(response.get("metadatas"))
|
|
distances = first_result_list(response.get("distances"))
|
|
contents: list[str] = []
|
|
for index, document in enumerate(documents):
|
|
metadata = metadatas[index] if index < len(metadatas) else {}
|
|
if not isinstance(metadata, dict):
|
|
continue
|
|
slug = metadata.get(slug_field)
|
|
if slug is None:
|
|
continue
|
|
slug_value = str(slug).strip("/")
|
|
if scoped_slugs is not None and slug_value not in scoped_slugs:
|
|
continue
|
|
score = score_from_distance(distances[index] if index <
|
|
len(distances) else None)
|
|
path = "/" + slug_value
|
|
prefix = mount_prefix.rstrip("/")
|
|
if prefix:
|
|
path = prefix + path
|
|
content = "" if document is None else str(document)
|
|
contents.append(f"{path}:{score}\n{content}")
|
|
if not contents:
|
|
return b""
|
|
return ("\n".join(contents) + "\n").encode()
|
|
|
|
|
|
def first_result_list(value: object) -> list:
|
|
if not isinstance(value, list):
|
|
return []
|
|
if value and isinstance(value[0], list):
|
|
return value[0]
|
|
return value
|