Maximize Qdrant utilization: richer memory payloads, filtered retrieval, and qdrant health in metrics API

This commit is contained in:
2026-03-01 10:30:56 -08:00
parent fee413a852
commit cd8f8430f9
3 changed files with 50 additions and 6 deletions

3
app.py
View File

@@ -6,7 +6,7 @@ from datetime import datetime, timedelta
import threading import threading
from db import init_db, SessionLocal, BotDecision, TradeExecution, CuratedInsight from db import init_db, SessionLocal, BotDecision, TradeExecution, CuratedInsight
from bot import start_scheduler, run_cycle, curate_cycle 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") app = FastAPI(title="alpaca-llm-bot-v1")
templates = Jinja2Templates(directory="templates") templates = Jinja2Templates(directory="templates")
@@ -94,6 +94,7 @@ def api_metrics(hours: int = 72):
"cumulativeNotional": t_values, "cumulativeNotional": t_values,
}, },
"leaderboard": leaderboard, "leaderboard": leaderboard,
"qdrant": qdrant_memory_health(),
} }
finally: finally:
db.close() db.close()

4
bot.py
View File

@@ -125,9 +125,11 @@ def run_cycle():
# learning memory + notes + orchestration signal # learning memory + notes + orchestration signal
qdrant_add_memory(symbol, f"{symbol} {decision['action']} conf={decision['confidence']} reason={decision['reason']}", { qdrant_add_memory(symbol, f"{symbol} {decision['action']} conf={decision['confidence']} reason={decision['reason']}", {
"memory_type": "execution",
"status": drow.status, "status": drow.status,
"action": decision["action"], "action": decision["action"],
"confidence": decision["confidence"], "confidence": decision["confidence"],
"outcome_score": 1 if ok else -1,
"ts": datetime.utcnow().isoformat(), "ts": datetime.utcnow().isoformat(),
}) })
trilium_log( trilium_log(
@@ -145,9 +147,11 @@ def run_cycle():
# store non-trade decisions too for memory # store non-trade decisions too for memory
qdrant_add_memory(symbol, f"{symbol} decision={decision['action']} conf={decision['confidence']} status={drow.status}", { qdrant_add_memory(symbol, f"{symbol} decision={decision['action']} conf={decision['confidence']} status={drow.status}", {
"memory_type": "decision",
"status": drow.status, "status": drow.status,
"action": decision["action"], "action": decision["action"],
"confidence": decision["confidence"], "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(), "ts": datetime.utcnow().isoformat(),
}) })

View File

@@ -34,8 +34,15 @@ def qdrant_add_memory(symbol: str, text: str, payload: dict):
if not vec: if not vec:
return False return False
qdrant_ensure_collection(len(vec)) 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 = { 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: try:
r = requests.put(f"{settings.qdrant_url}/collections/{settings.qdrant_collection}/points", json=body, timeout=15) 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 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) vec = _embed(query_text)
if not vec: if not vec:
return [] 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: try:
r = requests.post(f"{settings.qdrant_url}/collections/{settings.qdrant_collection}/points/search", json=body, timeout=15) r = requests.post(f"{settings.qdrant_url}/collections/{settings.qdrant_collection}/points/search", json=body, timeout=15)
if not r.ok: if not r.ok:
@@ -56,8 +73,14 @@ def qdrant_similar(symbol: str, query_text: str, limit: int = 5):
out = [] out = []
for p in r.json().get("result", []): for p in r.json().get("result", []):
pl = p.get("payload", {}) pl = p.get("payload", {})
if pl.get("symbol") in (symbol, None): out.append({
out.append({"score": p.get("score", 0), "text": pl.get("text", ""), "status": pl.get("status", "")}) "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 return out
except Exception: except Exception:
return [] return []
@@ -233,6 +256,22 @@ def trilium_log(title: str, body: str):
return False 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): def n8n_emit(event: dict):
if not settings.n8n_webhook: if not settings.n8n_webhook:
return False return False