agentgraph-server 0.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agentgraph/__init__.py +1 -0
- agentgraph/auth/__init__.py +0 -0
- agentgraph/auth/credentials.py +224 -0
- agentgraph/backends/__init__.py +50 -0
- agentgraph/backends/sqlite/__init__.py +1 -0
- agentgraph/backends/sqlite/backend.py +1471 -0
- agentgraph/backends/sqlite/vector.py +142 -0
- agentgraph/cli.py +721 -0
- agentgraph/cli_query.py +519 -0
- agentgraph/config.py +90 -0
- agentgraph/connectors/__init__.py +0 -0
- agentgraph/connectors/base.py +455 -0
- agentgraph/connectors/registry.py +78 -0
- agentgraph/connectors/status.py +244 -0
- agentgraph/core/__init__.py +0 -0
- agentgraph/core/context.py +26 -0
- agentgraph/core/runtime.py +36 -0
- agentgraph/core/storage.py +240 -0
- agentgraph/graph/__init__.py +1 -0
- agentgraph/graph/bookmark.py +87 -0
- agentgraph/graph/delete.py +17 -0
- agentgraph/graph/download.py +35 -0
- agentgraph/graph/embeddings.py +58 -0
- agentgraph/graph/fetch.py +53 -0
- agentgraph/graph/gc.py +26 -0
- agentgraph/graph/link.py +63 -0
- agentgraph/graph/person.py +40 -0
- agentgraph/graph/query.py +244 -0
- agentgraph/graph/upsert.py +49 -0
- agentgraph/logging.py +78 -0
- agentgraph/mcp/__init__.py +0 -0
- agentgraph/mcp/server.py +811 -0
- agentgraph/perf.py +43 -0
- agentgraph/server/__init__.py +0 -0
- agentgraph/server/app.py +133 -0
- agentgraph/server/cli_api.py +708 -0
- agentgraph/server/dwell.py +79 -0
- agentgraph/server/graph_api.py +46 -0
- agentgraph/server/router.py +47 -0
- agentgraph/server/sync.py +247 -0
- agentgraph/skills.py +93 -0
- agentgraph_server-0.5.0.data/data/.agents/skills/graph/SKILL.md +159 -0
- agentgraph_server-0.5.0.data/data/.agents/skills/slack-auth/SKILL.md +92 -0
- agentgraph_server-0.5.0.dist-info/METADATA +286 -0
- agentgraph_server-0.5.0.dist-info/RECORD +49 -0
- agentgraph_server-0.5.0.dist-info/WHEEL +5 -0
- agentgraph_server-0.5.0.dist-info/entry_points.txt +2 -0
- agentgraph_server-0.5.0.dist-info/licenses/LICENSE +21 -0
- agentgraph_server-0.5.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""Vector search helpers for the SQLite backend.
|
|
2
|
+
|
|
3
|
+
Three modes (set at backend construction time):
|
|
4
|
+
- "sqlite-vec": Uses the sqlite-vec extension's vec_distance_cosine() scalar
|
|
5
|
+
function (O(n) scan, SIMD-accelerated). Falls through to numpy on failure.
|
|
6
|
+
- "numpy": Loads all embeddings from the DB into Python, computes cosine
|
|
7
|
+
similarity with numpy. Falls through to BM25-only on ImportError.
|
|
8
|
+
- "bm25-only": Skips vector search; BM25 (FTS5) ranking only.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import struct
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from agentgraph.perf import timed
|
|
17
|
+
|
|
18
|
+
# ---------------------------------------------------------------------------
|
|
19
|
+
# Blob encoding
|
|
20
|
+
# ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
def pack_embedding(vec: list[float]) -> bytes:
|
|
23
|
+
return struct.pack(f"{len(vec)}f", *vec)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def unpack_embedding(blob: bytes) -> list[float]:
|
|
27
|
+
n = len(blob) // 4
|
|
28
|
+
return list(struct.unpack(f"{n}f", blob))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# ---------------------------------------------------------------------------
|
|
32
|
+
# Extension loading
|
|
33
|
+
# ---------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
async def load_sqlite_vec(conn: Any) -> bool:
|
|
36
|
+
"""Load the sqlite-vec extension into *conn*. Returns True on success."""
|
|
37
|
+
try:
|
|
38
|
+
import sqlite_vec # type: ignore[import-untyped]
|
|
39
|
+
except ImportError:
|
|
40
|
+
return False
|
|
41
|
+
try:
|
|
42
|
+
# Must run on the aiosqlite background thread via _execute,
|
|
43
|
+
# passing the underlying sqlite3 connection.
|
|
44
|
+
def _do_load(db: Any) -> None:
|
|
45
|
+
db.enable_load_extension(True)
|
|
46
|
+
sqlite_vec.load(db)
|
|
47
|
+
db.enable_load_extension(False)
|
|
48
|
+
|
|
49
|
+
await conn._execute(_do_load, conn._connection)
|
|
50
|
+
return True
|
|
51
|
+
except Exception:
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# ---------------------------------------------------------------------------
|
|
56
|
+
# Vector search
|
|
57
|
+
# ---------------------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
async def vector_ranked(
|
|
60
|
+
conn: Any,
|
|
61
|
+
query_vec: list[float],
|
|
62
|
+
entity_types: list[str] | None,
|
|
63
|
+
limit: int,
|
|
64
|
+
mode: str,
|
|
65
|
+
vec_loaded: bool,
|
|
66
|
+
platform: str | None = None,
|
|
67
|
+
candidate_limit: int | None = None,
|
|
68
|
+
) -> list[tuple[str, int]]:
|
|
69
|
+
"""Return (entity_id, rank) pairs with rank starting at 1 (best).
|
|
70
|
+
|
|
71
|
+
Returns an empty list when mode is "bm25-only" or when no embeddings exist.
|
|
72
|
+
"""
|
|
73
|
+
if mode == "bm25-only":
|
|
74
|
+
return []
|
|
75
|
+
|
|
76
|
+
type_clause = ""
|
|
77
|
+
type_params: list[Any] = []
|
|
78
|
+
if entity_types:
|
|
79
|
+
placeholders = ",".join("?" * len(entity_types))
|
|
80
|
+
type_clause = f"AND entity_type IN ({placeholders})"
|
|
81
|
+
type_params = list(entity_types)
|
|
82
|
+
if platform:
|
|
83
|
+
type_clause += " AND platform = ?"
|
|
84
|
+
type_params.append(platform)
|
|
85
|
+
|
|
86
|
+
query_blob = pack_embedding(query_vec)
|
|
87
|
+
candidate_limit = candidate_limit if candidate_limit is not None else limit * 5
|
|
88
|
+
|
|
89
|
+
# ---- sqlite-vec path ----
|
|
90
|
+
if mode == "sqlite-vec" and vec_loaded:
|
|
91
|
+
try:
|
|
92
|
+
with timed("sqlite.vector_ranked.sqlite_vec", limit=limit, platform=platform):
|
|
93
|
+
cursor = await conn.execute(
|
|
94
|
+
f"""
|
|
95
|
+
SELECT id, vec_distance_cosine(content_embedding, ?) AS dist
|
|
96
|
+
FROM entities
|
|
97
|
+
WHERE content_embedding IS NOT NULL {type_clause}
|
|
98
|
+
ORDER BY dist ASC
|
|
99
|
+
LIMIT ?
|
|
100
|
+
""",
|
|
101
|
+
[query_blob, *type_params, candidate_limit],
|
|
102
|
+
)
|
|
103
|
+
rows = await cursor.fetchall()
|
|
104
|
+
return [(row[0], i + 1) for i, row in enumerate(rows)]
|
|
105
|
+
except Exception:
|
|
106
|
+
pass # fall through to numpy
|
|
107
|
+
|
|
108
|
+
# ---- numpy path ----
|
|
109
|
+
try:
|
|
110
|
+
import numpy as np
|
|
111
|
+
|
|
112
|
+
with timed("sqlite.vector_ranked.numpy", limit=limit, platform=platform):
|
|
113
|
+
cursor = await conn.execute(
|
|
114
|
+
f"SELECT id, content_embedding FROM entities WHERE content_embedding IS NOT NULL {type_clause}",
|
|
115
|
+
type_params,
|
|
116
|
+
)
|
|
117
|
+
rows = await cursor.fetchall()
|
|
118
|
+
if not rows:
|
|
119
|
+
return []
|
|
120
|
+
|
|
121
|
+
q = np.array(query_vec, dtype=np.float32)
|
|
122
|
+
q_norm = float(np.linalg.norm(q))
|
|
123
|
+
if q_norm == 0:
|
|
124
|
+
return []
|
|
125
|
+
q = q / q_norm
|
|
126
|
+
|
|
127
|
+
scored: list[tuple[str, float]] = []
|
|
128
|
+
for entity_id, blob in rows:
|
|
129
|
+
if not blob:
|
|
130
|
+
continue
|
|
131
|
+
vec = np.array(unpack_embedding(bytes(blob)), dtype=np.float32)
|
|
132
|
+
norm = float(np.linalg.norm(vec))
|
|
133
|
+
if norm == 0:
|
|
134
|
+
continue
|
|
135
|
+
sim = float(np.dot(q, vec / norm))
|
|
136
|
+
scored.append((entity_id, sim))
|
|
137
|
+
|
|
138
|
+
scored.sort(key=lambda x: x[1], reverse=True)
|
|
139
|
+
return [(eid, i + 1) for i, (eid, _) in enumerate(scored[:candidate_limit])]
|
|
140
|
+
|
|
141
|
+
except ImportError:
|
|
142
|
+
return []
|