From cd8f8430f96bb324a09eec78f92b1844eb54200a Mon Sep 17 00:00:00 2001 From: drjones Date: Sun, 1 Mar 2026 10:30:56 -0800 Subject: [PATCH] Maximize Qdrant utilization: richer memory payloads, filtered retrieval, and qdrant health in metrics API --- app.py | 3 ++- bot.py | 4 ++++ services.py | 49 ++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/app.py b/app.py index fc0d5ab..3849531 100644 --- a/app.py +++ b/app.py @@ -6,7 +6,7 @@ from datetime import datetime, timedelta import threading from db import init_db, SessionLocal, BotDecision, TradeExecution, CuratedInsight from bot import start_scheduler, run_cycle, curate_cycle -from services import account_snapshot +from services import account_snapshot, qdrant_memory_health app = FastAPI(title="alpaca-llm-bot-v1") templates = Jinja2Templates(directory="templates") @@ -94,6 +94,7 @@ def api_metrics(hours: int = 72): "cumulativeNotional": t_values, }, "leaderboard": leaderboard, + "qdrant": qdrant_memory_health(), } finally: db.close() diff --git a/bot.py b/bot.py index 4ae111c..ebda107 100644 --- a/bot.py +++ b/bot.py @@ -125,9 +125,11 @@ def run_cycle(): # learning memory + notes + orchestration signal qdrant_add_memory(symbol, f"{symbol} {decision['action']} conf={decision['confidence']} reason={decision['reason']}", { + "memory_type": "execution", "status": drow.status, "action": decision["action"], "confidence": decision["confidence"], + "outcome_score": 1 if ok else -1, "ts": datetime.utcnow().isoformat(), }) trilium_log( @@ -145,9 +147,11 @@ def run_cycle(): # store non-trade decisions too for memory qdrant_add_memory(symbol, f"{symbol} decision={decision['action']} conf={decision['confidence']} status={drow.status}", { + "memory_type": "decision", "status": drow.status, "action": decision["action"], "confidence": decision["confidence"], + "outcome_score": 0 if drow.status in {"skipped", "risk_blocked"} else (-1 if drow.status=="failed" else 1), "ts": datetime.utcnow().isoformat(), }) diff --git a/services.py b/services.py index a77e490..a1675d6 100644 --- a/services.py +++ b/services.py @@ -34,8 +34,15 @@ def qdrant_add_memory(symbol: str, text: str, payload: dict): if not vec: return False qdrant_ensure_collection(len(vec)) + enriched = { + "symbol": symbol, + "text": text[:2000], + "memory_type": payload.get("memory_type", "decision"), + "created_at": int(time.time()), + **payload, + } body = { - "points": [{"id": str(uuid.uuid4()), "vector": vec, "payload": {"symbol": symbol, "text": text[:2000], **payload}}] + "points": [{"id": str(uuid.uuid4()), "vector": vec, "payload": enriched}] } try: r = requests.put(f"{settings.qdrant_url}/collections/{settings.qdrant_collection}/points", json=body, timeout=15) @@ -44,11 +51,21 @@ def qdrant_add_memory(symbol: str, text: str, payload: dict): return False -def qdrant_similar(symbol: str, query_text: str, limit: int = 5): +def qdrant_similar(symbol: str, query_text: str, limit: int = 8): vec = _embed(query_text) if not vec: return [] - body = {"vector": vec, "limit": limit, "with_payload": True} + body = { + "vector": vec, + "limit": limit, + "with_payload": True, + "filter": { + "should": [ + {"key": "symbol", "match": {"value": symbol}}, + {"key": "memory_type", "match": {"value": "macro"}} + ] + } + } try: r = requests.post(f"{settings.qdrant_url}/collections/{settings.qdrant_collection}/points/search", json=body, timeout=15) if not r.ok: @@ -56,8 +73,14 @@ def qdrant_similar(symbol: str, query_text: str, limit: int = 5): out = [] for p in r.json().get("result", []): pl = p.get("payload", {}) - if pl.get("symbol") in (symbol, None): - out.append({"score": p.get("score", 0), "text": pl.get("text", ""), "status": pl.get("status", "")}) + out.append({ + "score": p.get("score", 0), + "text": pl.get("text", ""), + "status": pl.get("status", ""), + "action": pl.get("action", ""), + "confidence": pl.get("confidence", None), + "outcome_score": pl.get("outcome_score", None), + }) return out except Exception: return [] @@ -233,6 +256,22 @@ def trilium_log(title: str, body: str): return False +def qdrant_memory_health(): + try: + r = requests.get(f"{settings.qdrant_url}/collections/{settings.qdrant_collection}", timeout=10) + if not r.ok: + return {"ok": False, "status": r.status_code} + j = r.json().get("result", {}) + return { + "ok": True, + "collection": settings.qdrant_collection, + "points_count": j.get("points_count"), + "indexed_vectors_count": j.get("indexed_vectors_count"), + } + except Exception as e: + return {"ok": False, "error": str(e)[:160]} + + def n8n_emit(event: dict): if not settings.n8n_webhook: return False