agromatrix: add shared-memory review api and crawl4ai robustness
This commit is contained in:
@@ -1099,6 +1099,144 @@ class MemoryRetrieval:
|
||||
logger.warning(f"resolve_pending_question failed: {e}")
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def _to_qdrant_point_id(raw_id: Any) -> Any:
|
||||
if isinstance(raw_id, int):
|
||||
return raw_id
|
||||
if isinstance(raw_id, float) and raw_id.is_integer():
|
||||
return int(raw_id)
|
||||
if isinstance(raw_id, str):
|
||||
v = raw_id.strip()
|
||||
if not v:
|
||||
return raw_id
|
||||
if v.isdigit():
|
||||
try:
|
||||
return int(v)
|
||||
except Exception:
|
||||
return v
|
||||
return v
|
||||
return raw_id
|
||||
|
||||
async def list_shared_pending_cases(self, limit: int = 50) -> List[Dict[str, Any]]:
|
||||
if not self.qdrant_client or not SHARED_AGRO_LIBRARY_ENABLED:
|
||||
return []
|
||||
size = max(1, min(int(limit or 50), 200))
|
||||
try:
|
||||
points, _ = self.qdrant_client.scroll(
|
||||
collection_name="agromatrix_shared_pending",
|
||||
limit=size,
|
||||
with_payload=True,
|
||||
with_vectors=False,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.debug(f"list_shared_pending_cases failed: {e}")
|
||||
return []
|
||||
|
||||
items: List[Dict[str, Any]] = []
|
||||
for p in points or []:
|
||||
payload = getattr(p, "payload", {}) or {}
|
||||
text = str(payload.get("text") or "").strip()
|
||||
timestamp = payload.get("timestamp") or ""
|
||||
candidates = payload.get("candidates") if isinstance(payload.get("candidates"), list) else []
|
||||
items.append(
|
||||
{
|
||||
"point_id": str(getattr(p, "id", "")),
|
||||
"timestamp": timestamp,
|
||||
"decision": payload.get("decision"),
|
||||
"reviewed": bool(payload.get("reviewed")),
|
||||
"excerpt": text[:240],
|
||||
"candidates": candidates[:5],
|
||||
}
|
||||
)
|
||||
items.sort(key=lambda x: x.get("timestamp") or "", reverse=True)
|
||||
return items
|
||||
|
||||
async def review_shared_pending_case(
|
||||
self,
|
||||
point_id: str,
|
||||
approve: bool,
|
||||
reviewer: Optional[str] = None,
|
||||
note: Optional[str] = None,
|
||||
) -> Dict[str, Any]:
|
||||
if not self.qdrant_client:
|
||||
return {"ok": False, "error": "qdrant_unavailable"}
|
||||
|
||||
try:
|
||||
from qdrant_client.http import models as qmodels
|
||||
import uuid
|
||||
|
||||
pid = self._to_qdrant_point_id(point_id)
|
||||
records = self.qdrant_client.retrieve(
|
||||
collection_name="agromatrix_shared_pending",
|
||||
ids=[pid],
|
||||
with_payload=True,
|
||||
with_vectors=True,
|
||||
)
|
||||
if not records:
|
||||
return {"ok": False, "error": "not_found"}
|
||||
|
||||
point = records[0]
|
||||
payload = dict(getattr(point, "payload", {}) or {})
|
||||
now_iso = datetime.utcnow().isoformat()
|
||||
payload["reviewed"] = bool(approve)
|
||||
payload["review"] = {
|
||||
"reviewer": (reviewer or "system")[:120],
|
||||
"approved": bool(approve),
|
||||
"note": (note or "")[:500],
|
||||
"reviewed_at": now_iso,
|
||||
}
|
||||
|
||||
library_point_id: Optional[str] = None
|
||||
if approve:
|
||||
vector = getattr(point, "vector", None)
|
||||
if isinstance(vector, dict):
|
||||
# Named vectors mode: pick first vector value.
|
||||
vector = next(iter(vector.values()), None)
|
||||
if not vector and COHERE_API_KEY:
|
||||
basis = str(payload.get("text") or payload.get("assistant_response") or "")[:2000]
|
||||
vector = await self.get_embedding(basis)
|
||||
if not vector:
|
||||
return {"ok": False, "error": "missing_vector"}
|
||||
|
||||
try:
|
||||
self.qdrant_client.get_collection("agromatrix_shared_library")
|
||||
except Exception:
|
||||
self.qdrant_client.create_collection(
|
||||
collection_name="agromatrix_shared_library",
|
||||
vectors_config=qmodels.VectorParams(
|
||||
size=len(vector),
|
||||
distance=qmodels.Distance.COSINE,
|
||||
),
|
||||
)
|
||||
|
||||
library_point_id = str(uuid.uuid4())
|
||||
payload["approved_at"] = now_iso
|
||||
self.qdrant_client.upsert(
|
||||
collection_name="agromatrix_shared_library",
|
||||
points=[
|
||||
qmodels.PointStruct(
|
||||
id=library_point_id,
|
||||
vector=vector,
|
||||
payload=payload,
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
self.qdrant_client.delete(
|
||||
collection_name="agromatrix_shared_pending",
|
||||
points_selector=qmodels.PointIdsList(points=[pid]),
|
||||
)
|
||||
|
||||
return {
|
||||
"ok": True,
|
||||
"approved": bool(approve),
|
||||
"point_id": str(getattr(point, "id", point_id)),
|
||||
"library_point_id": library_point_id,
|
||||
}
|
||||
except Exception as e:
|
||||
logger.warning(f"review_shared_pending_case failed: {e}")
|
||||
return {"ok": False, "error": str(e)}
|
||||
|
||||
async def store_interaction(
|
||||
self,
|
||||
channel: str,
|
||||
|
||||
Reference in New Issue
Block a user