refactor: split gateway and pi-qmem extension into modules
- gateway: separate config, models, state, audit, guardrail, embeddings, store, metrics, cleanup and routes; keep main.py as FastAPI bootstrap - extension: split client/config, six tools, config command and rules; preserve jiti entrypoint and registrations - Dockerfile copies the complete gateway module set - tests: update monkeypatch boundaries for modular config/state
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
"""Operazioni Qdrant condivise dalle route."""
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
from typing import Any
|
||||
|
||||
from qdrant_client.http import models as qm
|
||||
|
||||
from config import SPARSE_VECTOR_NAME
|
||||
from models import SearchIn
|
||||
|
||||
|
||||
def search_filter(body: SearchIn) -> qm.Filter | None:
|
||||
must: list[Any] = []
|
||||
for key in ("kind", "project_id", "scope", "parent_id", "level", "topic"):
|
||||
value = getattr(body, key)
|
||||
if value:
|
||||
must.append(qm.FieldCondition(key=key, match=qm.MatchValue(value=value)))
|
||||
if not body.include_superseded:
|
||||
must.append(qm.IsEmptyCondition(is_empty=qm.PayloadField(key="superseded_by")))
|
||||
return qm.Filter(must=must) if must else None
|
||||
|
||||
|
||||
def search(qdrant: Any, collection: str, body: SearchIn, vector: list[float], sparse: Any) -> list[Any]:
|
||||
qfilter = search_filter(body)
|
||||
if body.hybrid and sparse is not None:
|
||||
return qdrant.query_points(
|
||||
collection_name=collection,
|
||||
prefetch=[
|
||||
qm.Prefetch(query=vector, using="", limit=body.top_k * 4, score_threshold=body.min_score),
|
||||
qm.Prefetch(query=sparse, using=SPARSE_VECTOR_NAME, limit=body.top_k * 4),
|
||||
],
|
||||
query=qm.FusionQuery(fusion=qm.Fusion.RRF),
|
||||
query_filter=qfilter,
|
||||
limit=body.top_k,
|
||||
with_payload=True,
|
||||
).points
|
||||
return qdrant.query_points(
|
||||
collection_name=collection,
|
||||
query=vector,
|
||||
query_filter=qfilter,
|
||||
limit=body.top_k,
|
||||
score_threshold=body.min_score,
|
||||
with_payload=True,
|
||||
).points
|
||||
|
||||
|
||||
def format_results(hits: list[Any]) -> list[dict]:
|
||||
return [
|
||||
{
|
||||
"memory_id": h.id,
|
||||
"score": round(h.score, 4),
|
||||
"text": h.payload.get("text"),
|
||||
"kind": h.payload.get("kind"),
|
||||
"agent_id": h.payload.get("agent_id"),
|
||||
"scope": h.payload.get("scope"),
|
||||
"project_id": h.payload.get("project_id"),
|
||||
"confidence": h.payload.get("confidence"),
|
||||
"created_at": h.payload.get("created_at"),
|
||||
"source": h.payload.get("source"),
|
||||
"supersedes_id": h.payload.get("supersedes_id"),
|
||||
"superseded_by": h.payload.get("superseded_by"),
|
||||
"supersede_reason": h.payload.get("supersede_reason"),
|
||||
"parent_id": h.payload.get("parent_id"),
|
||||
"level": h.payload.get("level"),
|
||||
"topic": h.payload.get("topic"),
|
||||
"links": h.payload.get("links"),
|
||||
}
|
||||
for h in hits
|
||||
]
|
||||
|
||||
|
||||
def reparent_active_children(qdrant: Any, collection: str, old_id: str, new_id: str) -> int:
|
||||
children, _ = qdrant.scroll(
|
||||
collection_name=collection,
|
||||
scroll_filter=qm.Filter(must=[
|
||||
qm.FieldCondition(key="parent_id", match=qm.MatchValue(value=old_id)),
|
||||
qm.IsEmptyCondition(is_empty=qm.PayloadField(key="superseded_by")),
|
||||
]),
|
||||
limit=1000,
|
||||
with_payload=False,
|
||||
)
|
||||
if not children:
|
||||
return 0
|
||||
qdrant.set_payload(collection_name=collection, payload={"parent_id": new_id}, points=[p.id for p in children])
|
||||
return len(children)
|
||||
Reference in New Issue
Block a user