-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmemory_db.py
More file actions
138 lines (124 loc) · 3.25 KB
/
Copy pathmemory_db.py
File metadata and controls
138 lines (124 loc) · 3.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
from __future__ import annotations
"""
DB helpers for Azurro's Memory Vault (Postgres + pgvector).
"""
import json
import os
from contextlib import contextmanager
from typing import Any, Iterable
import psycopg2
import psycopg2.extras
PG_DSN = os.getenv("AZURRO_PG_DSN", "").strip()
def is_configured() -> bool:
return bool(PG_DSN)
@contextmanager
def get_conn():
if not PG_DSN:
raise RuntimeError("AZURRO_PG_DSN is not set in .env")
conn = psycopg2.connect(PG_DSN)
try:
yield conn
finally:
conn.close()
def insert_memory_item(
kind: str,
source: str,
text: str,
embedding: list[float] | None,
*,
fixture_id: int | None = None,
azuro_game_id: str | None = None,
league: str | None = None,
window_name: str | None = None,
features: dict[str, Any] | None = None,
importance: float = 0.5,
tags: Iterable[str] | None = None,
) -> None:
"""
Insert one row into memory_items.
embedding may be None (will store NULL) – useful for backfilling later.
"""
cols = [
"kind",
"source",
"text",
"embedding",
"fixture_id",
"azuro_game_id",
"league",
"window_name",
"features_json",
"importance",
"tags",
]
values = [
kind,
source,
text,
embedding,
fixture_id,
azuro_game_id,
league,
window_name,
json.dumps(features or {}, ensure_ascii=False),
importance,
list(tags) if tags is not None else None,
]
placeholders = ", ".join(["%s"] * len(cols))
sql = f"INSERT INTO memory_items ({', '.join(cols)}) VALUES ({placeholders})"
with get_conn() as conn:
with conn.cursor() as cur:
cur.execute(sql, values)
conn.commit()
def query_similar(
query_embedding: list[float],
*,
top_k: int = 10,
kind: str | None = None,
league: str | None = None,
window_name: str | None = None,
) -> list[dict[str, Any]]:
"""
Return top_k most similar memory_items rows by L2 distance.
"""
where = []
params: list[Any] = [query_embedding]
if kind:
where.append("kind = %s")
params.append(kind)
if league:
where.append("league = %s")
params.append(league)
if window_name:
where.append("window_name = %s")
params.append(window_name)
where_sql = " AND ".join(where)
if where_sql:
where_sql = "WHERE " + where_sql
sql = f"""
SELECT id,
created_at,
kind,
source,
fixture_id,
azuro_game_id,
league,
window_name,
features_json,
text,
importance,
tags,
(embedding <-> %s::vector) AS distance
FROM memory_items
{where_sql}
ORDER BY embedding <-> %s::vector
LIMIT %s
"""
params.append(query_embedding)
params.append(top_k)
with get_conn() as conn:
with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur:
cur.execute(sql, params)
rows = cur.fetchall()
# convert JSON / tags as-is
return [dict(r) for r in rows]