aethergraph 0.1.0a2__py3-none-any.whl → 0.1.0a4__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.
Files changed (114) hide show
  1. aethergraph/__main__.py +3 -0
  2. aethergraph/api/v1/artifacts.py +23 -4
  3. aethergraph/api/v1/schemas.py +7 -0
  4. aethergraph/api/v1/session.py +123 -4
  5. aethergraph/config/config.py +2 -0
  6. aethergraph/config/search.py +49 -0
  7. aethergraph/contracts/services/channel.py +18 -1
  8. aethergraph/contracts/services/execution.py +58 -0
  9. aethergraph/contracts/services/llm.py +26 -0
  10. aethergraph/contracts/services/memory.py +10 -4
  11. aethergraph/contracts/services/planning.py +53 -0
  12. aethergraph/contracts/storage/event_log.py +8 -0
  13. aethergraph/contracts/storage/search_backend.py +47 -0
  14. aethergraph/contracts/storage/vector_index.py +73 -0
  15. aethergraph/core/graph/action_spec.py +76 -0
  16. aethergraph/core/graph/graph_fn.py +75 -2
  17. aethergraph/core/graph/graphify.py +74 -2
  18. aethergraph/core/runtime/graph_runner.py +2 -1
  19. aethergraph/core/runtime/node_context.py +66 -3
  20. aethergraph/core/runtime/node_services.py +8 -0
  21. aethergraph/core/runtime/run_manager.py +263 -271
  22. aethergraph/core/runtime/run_types.py +54 -1
  23. aethergraph/core/runtime/runtime_env.py +35 -14
  24. aethergraph/core/runtime/runtime_services.py +308 -18
  25. aethergraph/plugins/agents/default_chat_agent.py +266 -74
  26. aethergraph/plugins/agents/default_chat_agent_v2.py +487 -0
  27. aethergraph/plugins/channel/adapters/webui.py +69 -21
  28. aethergraph/plugins/channel/routes/webui_routes.py +8 -48
  29. aethergraph/runtime/__init__.py +12 -0
  30. aethergraph/server/app_factory.py +10 -1
  31. aethergraph/server/ui_static/assets/index-CFktGdbW.js +4913 -0
  32. aethergraph/server/ui_static/assets/index-DcfkFlTA.css +1 -0
  33. aethergraph/server/ui_static/index.html +2 -2
  34. aethergraph/services/artifacts/facade.py +157 -21
  35. aethergraph/services/artifacts/types.py +35 -0
  36. aethergraph/services/artifacts/utils.py +42 -0
  37. aethergraph/services/channel/channel_bus.py +3 -1
  38. aethergraph/services/channel/event_hub copy.py +55 -0
  39. aethergraph/services/channel/event_hub.py +81 -0
  40. aethergraph/services/channel/factory.py +3 -2
  41. aethergraph/services/channel/session.py +709 -74
  42. aethergraph/services/container/default_container.py +69 -7
  43. aethergraph/services/execution/__init__.py +0 -0
  44. aethergraph/services/execution/local_python.py +118 -0
  45. aethergraph/services/indices/__init__.py +0 -0
  46. aethergraph/services/indices/global_indices.py +21 -0
  47. aethergraph/services/indices/scoped_indices.py +292 -0
  48. aethergraph/services/llm/generic_client.py +342 -46
  49. aethergraph/services/llm/generic_embed_client.py +359 -0
  50. aethergraph/services/llm/types.py +3 -1
  51. aethergraph/services/memory/distillers/llm_long_term.py +60 -109
  52. aethergraph/services/memory/distillers/llm_long_term_v1.py +180 -0
  53. aethergraph/services/memory/distillers/llm_meta_summary.py +57 -266
  54. aethergraph/services/memory/distillers/llm_meta_summary_v1.py +342 -0
  55. aethergraph/services/memory/distillers/long_term.py +48 -131
  56. aethergraph/services/memory/distillers/long_term_v1.py +170 -0
  57. aethergraph/services/memory/facade/chat.py +18 -8
  58. aethergraph/services/memory/facade/core.py +159 -19
  59. aethergraph/services/memory/facade/distillation.py +86 -31
  60. aethergraph/services/memory/facade/retrieval.py +100 -1
  61. aethergraph/services/memory/factory.py +4 -1
  62. aethergraph/services/planning/__init__.py +0 -0
  63. aethergraph/services/planning/action_catalog.py +271 -0
  64. aethergraph/services/planning/bindings.py +56 -0
  65. aethergraph/services/planning/dependency_index.py +65 -0
  66. aethergraph/services/planning/flow_validator.py +263 -0
  67. aethergraph/services/planning/graph_io_adapter.py +150 -0
  68. aethergraph/services/planning/input_parser.py +312 -0
  69. aethergraph/services/planning/missing_inputs.py +28 -0
  70. aethergraph/services/planning/node_planner.py +613 -0
  71. aethergraph/services/planning/orchestrator.py +112 -0
  72. aethergraph/services/planning/plan_executor.py +506 -0
  73. aethergraph/services/planning/plan_types.py +321 -0
  74. aethergraph/services/planning/planner.py +617 -0
  75. aethergraph/services/planning/planner_service.py +369 -0
  76. aethergraph/services/planning/planning_context_builder.py +43 -0
  77. aethergraph/services/planning/quick_actions.py +29 -0
  78. aethergraph/services/planning/routers/__init__.py +0 -0
  79. aethergraph/services/planning/routers/simple_router.py +26 -0
  80. aethergraph/services/rag/facade.py +0 -3
  81. aethergraph/services/scope/scope.py +30 -30
  82. aethergraph/services/scope/scope_factory.py +15 -7
  83. aethergraph/services/skills/__init__.py +0 -0
  84. aethergraph/services/skills/skill_registry.py +465 -0
  85. aethergraph/services/skills/skills.py +220 -0
  86. aethergraph/services/skills/utils.py +194 -0
  87. aethergraph/storage/artifacts/artifact_index_jsonl.py +16 -10
  88. aethergraph/storage/artifacts/artifact_index_sqlite.py +12 -2
  89. aethergraph/storage/docstore/sqlite_doc_sync.py +1 -1
  90. aethergraph/storage/memory/event_persist.py +42 -2
  91. aethergraph/storage/memory/fs_persist.py +32 -2
  92. aethergraph/storage/search_backend/__init__.py +0 -0
  93. aethergraph/storage/search_backend/generic_vector_backend.py +230 -0
  94. aethergraph/storage/search_backend/null_backend.py +34 -0
  95. aethergraph/storage/search_backend/sqlite_lexical_backend.py +387 -0
  96. aethergraph/storage/search_backend/utils.py +31 -0
  97. aethergraph/storage/search_factory.py +75 -0
  98. aethergraph/storage/vector_index/faiss_index.py +72 -4
  99. aethergraph/storage/vector_index/sqlite_index.py +521 -52
  100. aethergraph/storage/vector_index/sqlite_index_vanila.py +311 -0
  101. aethergraph/storage/vector_index/utils.py +22 -0
  102. {aethergraph-0.1.0a2.dist-info → aethergraph-0.1.0a4.dist-info}/METADATA +1 -1
  103. {aethergraph-0.1.0a2.dist-info → aethergraph-0.1.0a4.dist-info}/RECORD +108 -64
  104. {aethergraph-0.1.0a2.dist-info → aethergraph-0.1.0a4.dist-info}/WHEEL +1 -1
  105. aethergraph/plugins/agents/default_chat_agent copy.py +0 -90
  106. aethergraph/server/ui_static/assets/index-BR5GtXcZ.css +0 -1
  107. aethergraph/server/ui_static/assets/index-CQ0HZZ83.js +0 -400
  108. aethergraph/services/eventhub/event_hub.py +0 -76
  109. aethergraph/services/llm/generic_client copy.py +0 -691
  110. aethergraph/services/prompts/file_store.py +0 -41
  111. {aethergraph-0.1.0a2.dist-info → aethergraph-0.1.0a4.dist-info}/entry_points.txt +0 -0
  112. {aethergraph-0.1.0a2.dist-info → aethergraph-0.1.0a4.dist-info}/licenses/LICENSE +0 -0
  113. {aethergraph-0.1.0a2.dist-info → aethergraph-0.1.0a4.dist-info}/licenses/NOTICE +0 -0
  114. {aethergraph-0.1.0a2.dist-info → aethergraph-0.1.0a4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,311 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import json
5
+ from pathlib import Path
6
+ import sqlite3
7
+ from typing import Any
8
+
9
+ import numpy as np
10
+
11
+ from aethergraph.contracts.storage.vector_index import VectorIndex
12
+
13
+ SCHEMA = """
14
+ CREATE TABLE IF NOT EXISTS chunks (
15
+ corpus_id TEXT,
16
+ chunk_id TEXT,
17
+ meta_json TEXT,
18
+ PRIMARY KEY (corpus_id, chunk_id)
19
+ );
20
+
21
+ CREATE TABLE IF NOT EXISTS embeddings (
22
+ corpus_id TEXT,
23
+ chunk_id TEXT,
24
+ vec BLOB, -- np.float32 array bytes
25
+ norm REAL,
26
+ -- promoted / hot fields
27
+ scope_id TEXT,
28
+ user_id TEXT,
29
+ org_id TEXT,
30
+ client_id TEXT,
31
+ session_id TEXT,
32
+ run_id TEXT,
33
+ graph_id TEXT,
34
+ node_id TEXT,
35
+ kind TEXT,
36
+ source TEXT,
37
+ created_at_ts REAL,
38
+ PRIMARY KEY (corpus_id, chunk_id)
39
+ );
40
+
41
+ CREATE INDEX IF NOT EXISTS idx_emb_corpus_scope_time
42
+ ON embeddings(corpus_id, scope_id, created_at_ts DESC);
43
+
44
+ CREATE INDEX IF NOT EXISTS idx_emb_corpus_user_time
45
+ ON embeddings(corpus_id, user_id, created_at_ts DESC);
46
+
47
+ CREATE INDEX IF NOT EXISTS idx_emb_corpus_org_time
48
+ ON embeddings(corpus_id, org_id, created_at_ts DESC);
49
+
50
+ CREATE INDEX IF NOT EXISTS idx_emb_corpus_kind_time
51
+ ON embeddings(corpus_id, kind, created_at_ts DESC);
52
+ """
53
+
54
+
55
+ def _ensure_db(path: str) -> None:
56
+ Path(path).parent.mkdir(parents=True, exist_ok=True)
57
+ conn = sqlite3.connect(path, check_same_thread=False)
58
+ try:
59
+ cur = conn.cursor()
60
+ for stmt in SCHEMA.strip().split(";\n\n"):
61
+ s = stmt.strip()
62
+ if s:
63
+ cur.execute(s)
64
+ conn.commit()
65
+ finally:
66
+ conn.close()
67
+
68
+
69
+ class SQLiteVectorIndex(VectorIndex):
70
+ """
71
+ Simple SQLite-backed vector index.
72
+
73
+ Uses brute-force cosine similarity per corpus.
74
+
75
+ Promoted fields you *may* pass in meta:
76
+ - scope_id, user_id, org_id, client_id, session_id
77
+ - run_id, graph_id, node_id
78
+ - kind, source
79
+ - created_at_ts (float UNIX timestamp)
80
+ """
81
+
82
+ def __init__(self, root: str):
83
+ self.root = Path(root)
84
+ self.root.mkdir(parents=True, exist_ok=True)
85
+ self.db_path = str(self.root / "index.sqlite")
86
+ _ensure_db(self.db_path)
87
+
88
+ def _connect(self) -> sqlite3.Connection:
89
+ return sqlite3.connect(self.db_path, check_same_thread=False)
90
+
91
+ async def add(
92
+ self,
93
+ corpus_id: str,
94
+ chunk_ids: list[str],
95
+ vectors: list[list[float]],
96
+ metas: list[dict[str, Any]],
97
+ ) -> None:
98
+ if not chunk_ids:
99
+ return
100
+
101
+ def _add_sync():
102
+ conn = self._connect()
103
+ try:
104
+ cur = conn.cursor()
105
+ for cid, vec, meta in zip(chunk_ids, vectors, metas, strict=True):
106
+ v = np.asarray(vec, dtype=np.float32)
107
+ norm = float(np.linalg.norm(v) + 1e-9)
108
+
109
+ meta_json = json.dumps(meta, ensure_ascii=False)
110
+
111
+ # promoted, optional
112
+ scope_id = meta.get("scope_id")
113
+ user_id = meta.get("user_id")
114
+ org_id = meta.get("org_id")
115
+ client_id = meta.get("client_id")
116
+ session_id = meta.get("session_id")
117
+ run_id = meta.get("run_id")
118
+ graph_id = meta.get("graph_id")
119
+ node_id = meta.get("node_id")
120
+ kind = meta.get("kind")
121
+ source = meta.get("source")
122
+ created_at_ts = meta.get("created_at_ts")
123
+
124
+ cur.execute(
125
+ "REPLACE INTO chunks(corpus_id,chunk_id,meta_json) VALUES(?,?,?)",
126
+ (corpus_id, cid, meta_json),
127
+ )
128
+ cur.execute(
129
+ """
130
+ REPLACE INTO embeddings(
131
+ corpus_id,
132
+ chunk_id,
133
+ vec,
134
+ norm,
135
+ scope_id,
136
+ user_id,
137
+ org_id,
138
+ client_id,
139
+ session_id,
140
+ run_id,
141
+ graph_id,
142
+ node_id,
143
+ kind,
144
+ source,
145
+ created_at_ts
146
+ )
147
+ VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
148
+ """,
149
+ (
150
+ corpus_id,
151
+ cid,
152
+ v.tobytes(),
153
+ norm,
154
+ scope_id,
155
+ user_id,
156
+ org_id,
157
+ client_id,
158
+ session_id,
159
+ run_id,
160
+ graph_id,
161
+ node_id,
162
+ kind,
163
+ source,
164
+ created_at_ts,
165
+ ),
166
+ )
167
+ conn.commit()
168
+ finally:
169
+ conn.close()
170
+
171
+ await asyncio.to_thread(_add_sync)
172
+
173
+ async def delete(self, corpus_id: str, chunk_ids: list[str] | None = None) -> None:
174
+ def _delete_sync():
175
+ conn = self._connect()
176
+ try:
177
+ cur = conn.cursor()
178
+ if chunk_ids:
179
+ placeholders = ",".join("?" for _ in chunk_ids)
180
+ cur.execute(
181
+ f"DELETE FROM chunks WHERE corpus_id=? AND chunk_id IN ({placeholders})",
182
+ [corpus_id, *chunk_ids],
183
+ )
184
+ cur.execute(
185
+ f"DELETE FROM embeddings WHERE corpus_id=? AND chunk_id IN ({placeholders})",
186
+ [corpus_id, *chunk_ids],
187
+ )
188
+ else:
189
+ cur.execute("DELETE FROM chunks WHERE corpus_id=?", (corpus_id,))
190
+ cur.execute("DELETE FROM embeddings WHERE corpus_id=?", (corpus_id,))
191
+ conn.commit()
192
+ finally:
193
+ conn.close()
194
+
195
+ await asyncio.to_thread(_delete_sync)
196
+
197
+ async def list_chunks(self, corpus_id: str) -> list[str]:
198
+ def _list_sync() -> list[str]:
199
+ conn = self._connect()
200
+ try:
201
+ cur = conn.cursor()
202
+ cur.execute("SELECT chunk_id FROM chunks WHERE corpus_id=?", (corpus_id,))
203
+ return [r[0] for r in cur.fetchall()]
204
+ finally:
205
+ conn.close()
206
+
207
+ return await asyncio.to_thread(_list_sync)
208
+
209
+ async def list_corpora(self) -> list[str]:
210
+ def _list_sync() -> list[str]:
211
+ conn = self._connect()
212
+ try:
213
+ cur = conn.cursor()
214
+ cur.execute("SELECT DISTINCT corpus_id FROM chunks")
215
+ return [r[0] for r in cur.fetchall()]
216
+ finally:
217
+ conn.close()
218
+
219
+ return await asyncio.to_thread(_list_sync)
220
+
221
+ async def search(
222
+ self,
223
+ corpus_id: str,
224
+ query_vec: list[float],
225
+ k: int,
226
+ where: dict[str, Any] | None = None,
227
+ max_candidates: int | None = None,
228
+ created_at_min: float | None = None,
229
+ created_at_max: float | None = None,
230
+ ) -> list[dict[str, Any]]:
231
+ q = np.asarray(query_vec, dtype=np.float32)
232
+ qn = float(np.linalg.norm(q) + 1e-9)
233
+
234
+ where = where or {}
235
+
236
+ def _search_sync() -> list[dict[str, Any]]:
237
+ conn = self._connect()
238
+ try:
239
+ cur = conn.cursor()
240
+
241
+ sql = """
242
+ SELECT e.chunk_id, e.vec, e.norm, c.meta_json
243
+ FROM embeddings e
244
+ JOIN chunks c
245
+ ON e.corpus_id = c.corpus_id AND e.chunk_id = c.chunk_id
246
+ WHERE e.corpus_id=?
247
+ """
248
+ params: list[Any] = [corpus_id]
249
+
250
+ promoted_cols = {
251
+ "scope_id",
252
+ "user_id",
253
+ "org_id",
254
+ "client_id",
255
+ "session_id",
256
+ "run_id",
257
+ "graph_id",
258
+ "node_id",
259
+ "kind",
260
+ "source",
261
+ }
262
+
263
+ # Equality predicates on promoted fields
264
+ for key, val in where.items():
265
+ if val is None:
266
+ continue
267
+ if key in promoted_cols:
268
+ sql += f" AND e.{key} = ?"
269
+ params.append(val)
270
+
271
+ # Time bounds (if any)
272
+ if created_at_min is not None:
273
+ sql += " AND e.created_at_ts >= ?"
274
+ params.append(created_at_min)
275
+ if created_at_max is not None:
276
+ sql += " AND e.created_at_ts <= ?"
277
+ params.append(created_at_max)
278
+
279
+ # Candidate bound + recency bias
280
+ candidate_limit = max_candidates or 5000
281
+ sql += " ORDER BY e.created_at_ts DESC"
282
+ sql += " LIMIT ?"
283
+ params.append(candidate_limit)
284
+
285
+ cur.execute(sql, params)
286
+ rows = cur.fetchall()
287
+ finally:
288
+ conn.close()
289
+
290
+ scored: list[tuple[float, str, dict[str, Any]]] = []
291
+ for chunk_id, vec_bytes, norm, meta_json in rows:
292
+ v = np.frombuffer(vec_bytes, dtype=np.float32)
293
+ score = float(np.dot(q, v) / (qn * (norm or 1e-9)))
294
+ meta = json.loads(meta_json)
295
+ scored.append((score, chunk_id, meta))
296
+
297
+ scored.sort(key=lambda x: x[0], reverse=True)
298
+ top = scored[:k]
299
+
300
+ out: list[dict[str, Any]] = []
301
+ for score, chunk_id, meta in top:
302
+ out.append(
303
+ {
304
+ "chunk_id": chunk_id,
305
+ "score": score,
306
+ "meta": meta,
307
+ }
308
+ )
309
+ return out
310
+
311
+ return await asyncio.to_thread(_search_sync)
@@ -0,0 +1,22 @@
1
+ from typing import Any
2
+
3
+ from aethergraph.contracts.storage.vector_index import IndexMeta
4
+
5
+
6
+ def build_index_meta_from_scope(
7
+ *,
8
+ kind: str | None,
9
+ source: str | None,
10
+ ts: str | None,
11
+ created_at_ts: float | None,
12
+ extra: dict[str, Any] | None = None,
13
+ ) -> dict[str, Any]:
14
+ m = IndexMeta(
15
+ kind=kind,
16
+ source=source,
17
+ ts=ts,
18
+ created_at_ts=created_at_ts,
19
+ extra=extra or {},
20
+ )
21
+
22
+ return m.to_dict()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aethergraph
3
- Version: 0.1.0a2
3
+ Version: 0.1.0a4
4
4
  Summary: Python-first agentic DAG execution framework
5
5
  Author-email: Zhaocheng Liu <zhaocheng@aiperture.io>
6
6
  License: Apache License