genxai-framework 0.1.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.
- cli/__init__.py +3 -0
- cli/commands/__init__.py +6 -0
- cli/commands/approval.py +85 -0
- cli/commands/audit.py +127 -0
- cli/commands/metrics.py +25 -0
- cli/commands/tool.py +389 -0
- cli/main.py +32 -0
- genxai/__init__.py +81 -0
- genxai/api/__init__.py +5 -0
- genxai/api/app.py +21 -0
- genxai/config/__init__.py +5 -0
- genxai/config/settings.py +37 -0
- genxai/connectors/__init__.py +19 -0
- genxai/connectors/base.py +122 -0
- genxai/connectors/kafka.py +92 -0
- genxai/connectors/postgres_cdc.py +95 -0
- genxai/connectors/registry.py +44 -0
- genxai/connectors/sqs.py +94 -0
- genxai/connectors/webhook.py +73 -0
- genxai/core/__init__.py +37 -0
- genxai/core/agent/__init__.py +32 -0
- genxai/core/agent/base.py +206 -0
- genxai/core/agent/config_io.py +59 -0
- genxai/core/agent/registry.py +98 -0
- genxai/core/agent/runtime.py +970 -0
- genxai/core/communication/__init__.py +6 -0
- genxai/core/communication/collaboration.py +44 -0
- genxai/core/communication/message_bus.py +192 -0
- genxai/core/communication/protocols.py +35 -0
- genxai/core/execution/__init__.py +22 -0
- genxai/core/execution/metadata.py +181 -0
- genxai/core/execution/queue.py +201 -0
- genxai/core/graph/__init__.py +30 -0
- genxai/core/graph/checkpoints.py +77 -0
- genxai/core/graph/edges.py +131 -0
- genxai/core/graph/engine.py +813 -0
- genxai/core/graph/executor.py +516 -0
- genxai/core/graph/nodes.py +161 -0
- genxai/core/graph/trigger_runner.py +40 -0
- genxai/core/memory/__init__.py +19 -0
- genxai/core/memory/base.py +72 -0
- genxai/core/memory/embedding.py +327 -0
- genxai/core/memory/episodic.py +448 -0
- genxai/core/memory/long_term.py +467 -0
- genxai/core/memory/manager.py +543 -0
- genxai/core/memory/persistence.py +297 -0
- genxai/core/memory/procedural.py +461 -0
- genxai/core/memory/semantic.py +526 -0
- genxai/core/memory/shared.py +62 -0
- genxai/core/memory/short_term.py +303 -0
- genxai/core/memory/vector_store.py +508 -0
- genxai/core/memory/working.py +211 -0
- genxai/core/state/__init__.py +6 -0
- genxai/core/state/manager.py +293 -0
- genxai/core/state/schema.py +115 -0
- genxai/llm/__init__.py +14 -0
- genxai/llm/base.py +150 -0
- genxai/llm/factory.py +329 -0
- genxai/llm/providers/__init__.py +1 -0
- genxai/llm/providers/anthropic.py +249 -0
- genxai/llm/providers/cohere.py +274 -0
- genxai/llm/providers/google.py +334 -0
- genxai/llm/providers/ollama.py +147 -0
- genxai/llm/providers/openai.py +257 -0
- genxai/llm/routing.py +83 -0
- genxai/observability/__init__.py +6 -0
- genxai/observability/logging.py +327 -0
- genxai/observability/metrics.py +494 -0
- genxai/observability/tracing.py +372 -0
- genxai/performance/__init__.py +39 -0
- genxai/performance/cache.py +256 -0
- genxai/performance/pooling.py +289 -0
- genxai/security/audit.py +304 -0
- genxai/security/auth.py +315 -0
- genxai/security/cost_control.py +528 -0
- genxai/security/default_policies.py +44 -0
- genxai/security/jwt.py +142 -0
- genxai/security/oauth.py +226 -0
- genxai/security/pii.py +366 -0
- genxai/security/policy_engine.py +82 -0
- genxai/security/rate_limit.py +341 -0
- genxai/security/rbac.py +247 -0
- genxai/security/validation.py +218 -0
- genxai/tools/__init__.py +21 -0
- genxai/tools/base.py +383 -0
- genxai/tools/builtin/__init__.py +131 -0
- genxai/tools/builtin/communication/__init__.py +15 -0
- genxai/tools/builtin/communication/email_sender.py +159 -0
- genxai/tools/builtin/communication/notification_manager.py +167 -0
- genxai/tools/builtin/communication/slack_notifier.py +118 -0
- genxai/tools/builtin/communication/sms_sender.py +118 -0
- genxai/tools/builtin/communication/webhook_caller.py +136 -0
- genxai/tools/builtin/computation/__init__.py +15 -0
- genxai/tools/builtin/computation/calculator.py +101 -0
- genxai/tools/builtin/computation/code_executor.py +183 -0
- genxai/tools/builtin/computation/data_validator.py +259 -0
- genxai/tools/builtin/computation/hash_generator.py +129 -0
- genxai/tools/builtin/computation/regex_matcher.py +201 -0
- genxai/tools/builtin/data/__init__.py +15 -0
- genxai/tools/builtin/data/csv_processor.py +213 -0
- genxai/tools/builtin/data/data_transformer.py +299 -0
- genxai/tools/builtin/data/json_processor.py +233 -0
- genxai/tools/builtin/data/text_analyzer.py +288 -0
- genxai/tools/builtin/data/xml_processor.py +175 -0
- genxai/tools/builtin/database/__init__.py +15 -0
- genxai/tools/builtin/database/database_inspector.py +157 -0
- genxai/tools/builtin/database/mongodb_query.py +196 -0
- genxai/tools/builtin/database/redis_cache.py +167 -0
- genxai/tools/builtin/database/sql_query.py +145 -0
- genxai/tools/builtin/database/vector_search.py +163 -0
- genxai/tools/builtin/file/__init__.py +17 -0
- genxai/tools/builtin/file/directory_scanner.py +214 -0
- genxai/tools/builtin/file/file_compressor.py +237 -0
- genxai/tools/builtin/file/file_reader.py +102 -0
- genxai/tools/builtin/file/file_writer.py +122 -0
- genxai/tools/builtin/file/image_processor.py +186 -0
- genxai/tools/builtin/file/pdf_parser.py +144 -0
- genxai/tools/builtin/test/__init__.py +15 -0
- genxai/tools/builtin/test/async_simulator.py +62 -0
- genxai/tools/builtin/test/data_transformer.py +99 -0
- genxai/tools/builtin/test/error_generator.py +82 -0
- genxai/tools/builtin/test/simple_math.py +94 -0
- genxai/tools/builtin/test/string_processor.py +72 -0
- genxai/tools/builtin/web/__init__.py +15 -0
- genxai/tools/builtin/web/api_caller.py +161 -0
- genxai/tools/builtin/web/html_parser.py +330 -0
- genxai/tools/builtin/web/http_client.py +187 -0
- genxai/tools/builtin/web/url_validator.py +162 -0
- genxai/tools/builtin/web/web_scraper.py +170 -0
- genxai/tools/custom/my_test_tool_2.py +9 -0
- genxai/tools/dynamic.py +105 -0
- genxai/tools/mcp_server.py +167 -0
- genxai/tools/persistence/__init__.py +6 -0
- genxai/tools/persistence/models.py +55 -0
- genxai/tools/persistence/service.py +322 -0
- genxai/tools/registry.py +227 -0
- genxai/tools/security/__init__.py +11 -0
- genxai/tools/security/limits.py +214 -0
- genxai/tools/security/policy.py +20 -0
- genxai/tools/security/sandbox.py +248 -0
- genxai/tools/templates.py +435 -0
- genxai/triggers/__init__.py +19 -0
- genxai/triggers/base.py +104 -0
- genxai/triggers/file_watcher.py +75 -0
- genxai/triggers/queue.py +68 -0
- genxai/triggers/registry.py +82 -0
- genxai/triggers/schedule.py +66 -0
- genxai/triggers/webhook.py +68 -0
- genxai/utils/__init__.py +1 -0
- genxai/utils/tokens.py +295 -0
- genxai_framework-0.1.0.dist-info/METADATA +495 -0
- genxai_framework-0.1.0.dist-info/RECORD +156 -0
- genxai_framework-0.1.0.dist-info/WHEEL +5 -0
- genxai_framework-0.1.0.dist-info/entry_points.txt +2 -0
- genxai_framework-0.1.0.dist-info/licenses/LICENSE +21 -0
- genxai_framework-0.1.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
"""Persistence helpers for memory modules."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, Dict, Iterable, List, Optional
|
|
8
|
+
import json
|
|
9
|
+
import logging
|
|
10
|
+
import sqlite3
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class MemoryPersistenceConfig:
|
|
17
|
+
"""Configuration for file-based memory persistence."""
|
|
18
|
+
|
|
19
|
+
base_dir: Path
|
|
20
|
+
enabled: bool = False
|
|
21
|
+
backend: str = "json" # "json" or "sqlite"
|
|
22
|
+
sqlite_path: Optional[Path] = None
|
|
23
|
+
|
|
24
|
+
def resolve(self, filename: str) -> Path:
|
|
25
|
+
"""Resolve a filename within the persistence directory."""
|
|
26
|
+
return self.base_dir / filename
|
|
27
|
+
|
|
28
|
+
def resolve_sqlite_path(self) -> Path:
|
|
29
|
+
"""Resolve SQLite database path."""
|
|
30
|
+
if self.sqlite_path:
|
|
31
|
+
return self.sqlite_path
|
|
32
|
+
return self.base_dir / "memory.db"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class JsonMemoryStore:
|
|
36
|
+
"""Simple JSON file store for memory objects."""
|
|
37
|
+
|
|
38
|
+
def __init__(self, config: MemoryPersistenceConfig) -> None:
|
|
39
|
+
self.config = config
|
|
40
|
+
|
|
41
|
+
def _ensure_dir(self) -> None:
|
|
42
|
+
if not self.config.enabled:
|
|
43
|
+
return
|
|
44
|
+
self.config.base_dir.mkdir(parents=True, exist_ok=True)
|
|
45
|
+
|
|
46
|
+
def load_list(self, filename: str) -> List[Dict[str, Any]]:
|
|
47
|
+
"""Load a list of dictionaries from disk."""
|
|
48
|
+
if not self.config.enabled:
|
|
49
|
+
return []
|
|
50
|
+
|
|
51
|
+
path = self.config.resolve(filename)
|
|
52
|
+
if not path.exists():
|
|
53
|
+
return []
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
with path.open("r", encoding="utf-8") as file:
|
|
57
|
+
data = json.load(file)
|
|
58
|
+
if isinstance(data, list):
|
|
59
|
+
return data
|
|
60
|
+
logger.warning("Unexpected data format in %s", path)
|
|
61
|
+
return []
|
|
62
|
+
except Exception as exc:
|
|
63
|
+
logger.error("Failed to load %s: %s", path, exc)
|
|
64
|
+
return []
|
|
65
|
+
|
|
66
|
+
def save_list(self, filename: str, items: Iterable[Dict[str, Any]]) -> None:
|
|
67
|
+
"""Save a list of dictionaries to disk."""
|
|
68
|
+
if not self.config.enabled:
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
self._ensure_dir()
|
|
72
|
+
path = self.config.resolve(filename)
|
|
73
|
+
try:
|
|
74
|
+
with path.open("w", encoding="utf-8") as file:
|
|
75
|
+
json.dump(list(items), file, indent=2, default=str)
|
|
76
|
+
except Exception as exc:
|
|
77
|
+
logger.error("Failed to save %s: %s", path, exc)
|
|
78
|
+
|
|
79
|
+
def load_mapping(self, filename: str) -> Dict[str, Any]:
|
|
80
|
+
"""Load a mapping from disk."""
|
|
81
|
+
if not self.config.enabled:
|
|
82
|
+
return {}
|
|
83
|
+
|
|
84
|
+
path = self.config.resolve(filename)
|
|
85
|
+
if not path.exists():
|
|
86
|
+
return {}
|
|
87
|
+
|
|
88
|
+
try:
|
|
89
|
+
with path.open("r", encoding="utf-8") as file:
|
|
90
|
+
data = json.load(file)
|
|
91
|
+
if isinstance(data, dict):
|
|
92
|
+
return data
|
|
93
|
+
logger.warning("Unexpected data format in %s", path)
|
|
94
|
+
return {}
|
|
95
|
+
except Exception as exc:
|
|
96
|
+
logger.error("Failed to load %s: %s", path, exc)
|
|
97
|
+
return {}
|
|
98
|
+
|
|
99
|
+
def save_mapping(self, filename: str, data: Dict[str, Any]) -> None:
|
|
100
|
+
"""Save a mapping to disk."""
|
|
101
|
+
if not self.config.enabled:
|
|
102
|
+
return
|
|
103
|
+
|
|
104
|
+
self._ensure_dir()
|
|
105
|
+
path = self.config.resolve(filename)
|
|
106
|
+
try:
|
|
107
|
+
with path.open("w", encoding="utf-8") as file:
|
|
108
|
+
json.dump(data, file, indent=2, default=str)
|
|
109
|
+
except Exception as exc:
|
|
110
|
+
logger.error("Failed to save %s: %s", path, exc)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class SqliteMemoryStore:
|
|
114
|
+
"""SQLite-backed key/value store for memory persistence."""
|
|
115
|
+
|
|
116
|
+
def __init__(self, config: MemoryPersistenceConfig) -> None:
|
|
117
|
+
self.config = config
|
|
118
|
+
self._initialized = False
|
|
119
|
+
|
|
120
|
+
def _ensure_db(self) -> None:
|
|
121
|
+
if not self.config.enabled:
|
|
122
|
+
return
|
|
123
|
+
if self._initialized:
|
|
124
|
+
return
|
|
125
|
+
self.config.base_dir.mkdir(parents=True, exist_ok=True)
|
|
126
|
+
db_path = self.config.resolve_sqlite_path()
|
|
127
|
+
conn = sqlite3.connect(db_path)
|
|
128
|
+
try:
|
|
129
|
+
cursor = conn.cursor()
|
|
130
|
+
cursor.execute(
|
|
131
|
+
"""
|
|
132
|
+
CREATE TABLE IF NOT EXISTS memory_blobs (
|
|
133
|
+
key TEXT PRIMARY KEY,
|
|
134
|
+
payload TEXT NOT NULL
|
|
135
|
+
)
|
|
136
|
+
"""
|
|
137
|
+
)
|
|
138
|
+
cursor.execute(
|
|
139
|
+
"""
|
|
140
|
+
CREATE TABLE IF NOT EXISTS long_term_metadata (
|
|
141
|
+
memory_id TEXT PRIMARY KEY,
|
|
142
|
+
memory_type TEXT,
|
|
143
|
+
importance REAL,
|
|
144
|
+
timestamp TEXT,
|
|
145
|
+
tags TEXT,
|
|
146
|
+
metadata TEXT
|
|
147
|
+
)
|
|
148
|
+
"""
|
|
149
|
+
)
|
|
150
|
+
conn.commit()
|
|
151
|
+
self._initialized = True
|
|
152
|
+
finally:
|
|
153
|
+
conn.close()
|
|
154
|
+
|
|
155
|
+
def _get_connection(self) -> sqlite3.Connection:
|
|
156
|
+
self._ensure_db()
|
|
157
|
+
return sqlite3.connect(self.config.resolve_sqlite_path())
|
|
158
|
+
|
|
159
|
+
def load_list(self, filename: str) -> List[Dict[str, Any]]:
|
|
160
|
+
if not self.config.enabled:
|
|
161
|
+
return []
|
|
162
|
+
|
|
163
|
+
self._ensure_db()
|
|
164
|
+
conn = self._get_connection()
|
|
165
|
+
try:
|
|
166
|
+
cursor = conn.cursor()
|
|
167
|
+
cursor.execute("SELECT payload FROM memory_blobs WHERE key = ?", (filename,))
|
|
168
|
+
row = cursor.fetchone()
|
|
169
|
+
if not row:
|
|
170
|
+
return []
|
|
171
|
+
data = json.loads(row[0])
|
|
172
|
+
return data if isinstance(data, list) else []
|
|
173
|
+
except Exception as exc:
|
|
174
|
+
logger.error("Failed to load %s from sqlite: %s", filename, exc)
|
|
175
|
+
return []
|
|
176
|
+
finally:
|
|
177
|
+
conn.close()
|
|
178
|
+
|
|
179
|
+
def save_list(self, filename: str, items: Iterable[Dict[str, Any]]) -> None:
|
|
180
|
+
if not self.config.enabled:
|
|
181
|
+
return
|
|
182
|
+
|
|
183
|
+
self._ensure_db()
|
|
184
|
+
conn = self._get_connection()
|
|
185
|
+
try:
|
|
186
|
+
payload = json.dumps(list(items), default=str)
|
|
187
|
+
cursor = conn.cursor()
|
|
188
|
+
cursor.execute(
|
|
189
|
+
"INSERT OR REPLACE INTO memory_blobs (key, payload) VALUES (?, ?)",
|
|
190
|
+
(filename, payload),
|
|
191
|
+
)
|
|
192
|
+
conn.commit()
|
|
193
|
+
except Exception as exc:
|
|
194
|
+
logger.error("Failed to save %s to sqlite: %s", filename, exc)
|
|
195
|
+
finally:
|
|
196
|
+
conn.close()
|
|
197
|
+
|
|
198
|
+
def load_mapping(self, filename: str) -> Dict[str, Any]:
|
|
199
|
+
if not self.config.enabled:
|
|
200
|
+
return {}
|
|
201
|
+
|
|
202
|
+
self._ensure_db()
|
|
203
|
+
conn = self._get_connection()
|
|
204
|
+
try:
|
|
205
|
+
cursor = conn.cursor()
|
|
206
|
+
cursor.execute("SELECT payload FROM memory_blobs WHERE key = ?", (filename,))
|
|
207
|
+
row = cursor.fetchone()
|
|
208
|
+
if not row:
|
|
209
|
+
return {}
|
|
210
|
+
data = json.loads(row[0])
|
|
211
|
+
return data if isinstance(data, dict) else {}
|
|
212
|
+
except Exception as exc:
|
|
213
|
+
logger.error("Failed to load %s from sqlite: %s", filename, exc)
|
|
214
|
+
return {}
|
|
215
|
+
finally:
|
|
216
|
+
conn.close()
|
|
217
|
+
|
|
218
|
+
def save_mapping(self, filename: str, data: Dict[str, Any]) -> None:
|
|
219
|
+
if not self.config.enabled:
|
|
220
|
+
return
|
|
221
|
+
|
|
222
|
+
self._ensure_db()
|
|
223
|
+
conn = self._get_connection()
|
|
224
|
+
try:
|
|
225
|
+
payload = json.dumps(data, default=str)
|
|
226
|
+
cursor = conn.cursor()
|
|
227
|
+
cursor.execute(
|
|
228
|
+
"INSERT OR REPLACE INTO memory_blobs (key, payload) VALUES (?, ?)",
|
|
229
|
+
(filename, payload),
|
|
230
|
+
)
|
|
231
|
+
conn.commit()
|
|
232
|
+
except Exception as exc:
|
|
233
|
+
logger.error("Failed to save %s to sqlite: %s", filename, exc)
|
|
234
|
+
finally:
|
|
235
|
+
conn.close()
|
|
236
|
+
|
|
237
|
+
def store_long_term_metadata(
|
|
238
|
+
self,
|
|
239
|
+
memory_id: str,
|
|
240
|
+
memory_type: str,
|
|
241
|
+
importance: float,
|
|
242
|
+
timestamp: str,
|
|
243
|
+
tags: List[str],
|
|
244
|
+
metadata: Dict[str, Any],
|
|
245
|
+
) -> None:
|
|
246
|
+
if not self.config.enabled:
|
|
247
|
+
return
|
|
248
|
+
|
|
249
|
+
self._ensure_db()
|
|
250
|
+
conn = self._get_connection()
|
|
251
|
+
try:
|
|
252
|
+
cursor = conn.cursor()
|
|
253
|
+
cursor.execute(
|
|
254
|
+
"""
|
|
255
|
+
INSERT OR REPLACE INTO long_term_metadata
|
|
256
|
+
(memory_id, memory_type, importance, timestamp, tags, metadata)
|
|
257
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
258
|
+
""",
|
|
259
|
+
(
|
|
260
|
+
memory_id,
|
|
261
|
+
memory_type,
|
|
262
|
+
importance,
|
|
263
|
+
timestamp,
|
|
264
|
+
",".join(tags),
|
|
265
|
+
json.dumps(metadata, default=str),
|
|
266
|
+
),
|
|
267
|
+
)
|
|
268
|
+
conn.commit()
|
|
269
|
+
except Exception as exc:
|
|
270
|
+
logger.error("Failed to store long-term metadata: %s", exc)
|
|
271
|
+
finally:
|
|
272
|
+
conn.close()
|
|
273
|
+
|
|
274
|
+
def delete_long_term_metadata(self, memory_id: str) -> None:
|
|
275
|
+
if not self.config.enabled:
|
|
276
|
+
return
|
|
277
|
+
|
|
278
|
+
self._ensure_db()
|
|
279
|
+
conn = self._get_connection()
|
|
280
|
+
try:
|
|
281
|
+
cursor = conn.cursor()
|
|
282
|
+
cursor.execute(
|
|
283
|
+
"DELETE FROM long_term_metadata WHERE memory_id = ?",
|
|
284
|
+
(memory_id,),
|
|
285
|
+
)
|
|
286
|
+
conn.commit()
|
|
287
|
+
except Exception as exc:
|
|
288
|
+
logger.error("Failed to delete long-term metadata: %s", exc)
|
|
289
|
+
finally:
|
|
290
|
+
conn.close()
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def create_memory_store(config: MemoryPersistenceConfig) -> JsonMemoryStore | SqliteMemoryStore:
|
|
294
|
+
"""Factory for memory stores based on config backend."""
|
|
295
|
+
if config.backend == "sqlite":
|
|
296
|
+
return SqliteMemoryStore(config)
|
|
297
|
+
return JsonMemoryStore(config)
|