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.
Files changed (156) hide show
  1. cli/__init__.py +3 -0
  2. cli/commands/__init__.py +6 -0
  3. cli/commands/approval.py +85 -0
  4. cli/commands/audit.py +127 -0
  5. cli/commands/metrics.py +25 -0
  6. cli/commands/tool.py +389 -0
  7. cli/main.py +32 -0
  8. genxai/__init__.py +81 -0
  9. genxai/api/__init__.py +5 -0
  10. genxai/api/app.py +21 -0
  11. genxai/config/__init__.py +5 -0
  12. genxai/config/settings.py +37 -0
  13. genxai/connectors/__init__.py +19 -0
  14. genxai/connectors/base.py +122 -0
  15. genxai/connectors/kafka.py +92 -0
  16. genxai/connectors/postgres_cdc.py +95 -0
  17. genxai/connectors/registry.py +44 -0
  18. genxai/connectors/sqs.py +94 -0
  19. genxai/connectors/webhook.py +73 -0
  20. genxai/core/__init__.py +37 -0
  21. genxai/core/agent/__init__.py +32 -0
  22. genxai/core/agent/base.py +206 -0
  23. genxai/core/agent/config_io.py +59 -0
  24. genxai/core/agent/registry.py +98 -0
  25. genxai/core/agent/runtime.py +970 -0
  26. genxai/core/communication/__init__.py +6 -0
  27. genxai/core/communication/collaboration.py +44 -0
  28. genxai/core/communication/message_bus.py +192 -0
  29. genxai/core/communication/protocols.py +35 -0
  30. genxai/core/execution/__init__.py +22 -0
  31. genxai/core/execution/metadata.py +181 -0
  32. genxai/core/execution/queue.py +201 -0
  33. genxai/core/graph/__init__.py +30 -0
  34. genxai/core/graph/checkpoints.py +77 -0
  35. genxai/core/graph/edges.py +131 -0
  36. genxai/core/graph/engine.py +813 -0
  37. genxai/core/graph/executor.py +516 -0
  38. genxai/core/graph/nodes.py +161 -0
  39. genxai/core/graph/trigger_runner.py +40 -0
  40. genxai/core/memory/__init__.py +19 -0
  41. genxai/core/memory/base.py +72 -0
  42. genxai/core/memory/embedding.py +327 -0
  43. genxai/core/memory/episodic.py +448 -0
  44. genxai/core/memory/long_term.py +467 -0
  45. genxai/core/memory/manager.py +543 -0
  46. genxai/core/memory/persistence.py +297 -0
  47. genxai/core/memory/procedural.py +461 -0
  48. genxai/core/memory/semantic.py +526 -0
  49. genxai/core/memory/shared.py +62 -0
  50. genxai/core/memory/short_term.py +303 -0
  51. genxai/core/memory/vector_store.py +508 -0
  52. genxai/core/memory/working.py +211 -0
  53. genxai/core/state/__init__.py +6 -0
  54. genxai/core/state/manager.py +293 -0
  55. genxai/core/state/schema.py +115 -0
  56. genxai/llm/__init__.py +14 -0
  57. genxai/llm/base.py +150 -0
  58. genxai/llm/factory.py +329 -0
  59. genxai/llm/providers/__init__.py +1 -0
  60. genxai/llm/providers/anthropic.py +249 -0
  61. genxai/llm/providers/cohere.py +274 -0
  62. genxai/llm/providers/google.py +334 -0
  63. genxai/llm/providers/ollama.py +147 -0
  64. genxai/llm/providers/openai.py +257 -0
  65. genxai/llm/routing.py +83 -0
  66. genxai/observability/__init__.py +6 -0
  67. genxai/observability/logging.py +327 -0
  68. genxai/observability/metrics.py +494 -0
  69. genxai/observability/tracing.py +372 -0
  70. genxai/performance/__init__.py +39 -0
  71. genxai/performance/cache.py +256 -0
  72. genxai/performance/pooling.py +289 -0
  73. genxai/security/audit.py +304 -0
  74. genxai/security/auth.py +315 -0
  75. genxai/security/cost_control.py +528 -0
  76. genxai/security/default_policies.py +44 -0
  77. genxai/security/jwt.py +142 -0
  78. genxai/security/oauth.py +226 -0
  79. genxai/security/pii.py +366 -0
  80. genxai/security/policy_engine.py +82 -0
  81. genxai/security/rate_limit.py +341 -0
  82. genxai/security/rbac.py +247 -0
  83. genxai/security/validation.py +218 -0
  84. genxai/tools/__init__.py +21 -0
  85. genxai/tools/base.py +383 -0
  86. genxai/tools/builtin/__init__.py +131 -0
  87. genxai/tools/builtin/communication/__init__.py +15 -0
  88. genxai/tools/builtin/communication/email_sender.py +159 -0
  89. genxai/tools/builtin/communication/notification_manager.py +167 -0
  90. genxai/tools/builtin/communication/slack_notifier.py +118 -0
  91. genxai/tools/builtin/communication/sms_sender.py +118 -0
  92. genxai/tools/builtin/communication/webhook_caller.py +136 -0
  93. genxai/tools/builtin/computation/__init__.py +15 -0
  94. genxai/tools/builtin/computation/calculator.py +101 -0
  95. genxai/tools/builtin/computation/code_executor.py +183 -0
  96. genxai/tools/builtin/computation/data_validator.py +259 -0
  97. genxai/tools/builtin/computation/hash_generator.py +129 -0
  98. genxai/tools/builtin/computation/regex_matcher.py +201 -0
  99. genxai/tools/builtin/data/__init__.py +15 -0
  100. genxai/tools/builtin/data/csv_processor.py +213 -0
  101. genxai/tools/builtin/data/data_transformer.py +299 -0
  102. genxai/tools/builtin/data/json_processor.py +233 -0
  103. genxai/tools/builtin/data/text_analyzer.py +288 -0
  104. genxai/tools/builtin/data/xml_processor.py +175 -0
  105. genxai/tools/builtin/database/__init__.py +15 -0
  106. genxai/tools/builtin/database/database_inspector.py +157 -0
  107. genxai/tools/builtin/database/mongodb_query.py +196 -0
  108. genxai/tools/builtin/database/redis_cache.py +167 -0
  109. genxai/tools/builtin/database/sql_query.py +145 -0
  110. genxai/tools/builtin/database/vector_search.py +163 -0
  111. genxai/tools/builtin/file/__init__.py +17 -0
  112. genxai/tools/builtin/file/directory_scanner.py +214 -0
  113. genxai/tools/builtin/file/file_compressor.py +237 -0
  114. genxai/tools/builtin/file/file_reader.py +102 -0
  115. genxai/tools/builtin/file/file_writer.py +122 -0
  116. genxai/tools/builtin/file/image_processor.py +186 -0
  117. genxai/tools/builtin/file/pdf_parser.py +144 -0
  118. genxai/tools/builtin/test/__init__.py +15 -0
  119. genxai/tools/builtin/test/async_simulator.py +62 -0
  120. genxai/tools/builtin/test/data_transformer.py +99 -0
  121. genxai/tools/builtin/test/error_generator.py +82 -0
  122. genxai/tools/builtin/test/simple_math.py +94 -0
  123. genxai/tools/builtin/test/string_processor.py +72 -0
  124. genxai/tools/builtin/web/__init__.py +15 -0
  125. genxai/tools/builtin/web/api_caller.py +161 -0
  126. genxai/tools/builtin/web/html_parser.py +330 -0
  127. genxai/tools/builtin/web/http_client.py +187 -0
  128. genxai/tools/builtin/web/url_validator.py +162 -0
  129. genxai/tools/builtin/web/web_scraper.py +170 -0
  130. genxai/tools/custom/my_test_tool_2.py +9 -0
  131. genxai/tools/dynamic.py +105 -0
  132. genxai/tools/mcp_server.py +167 -0
  133. genxai/tools/persistence/__init__.py +6 -0
  134. genxai/tools/persistence/models.py +55 -0
  135. genxai/tools/persistence/service.py +322 -0
  136. genxai/tools/registry.py +227 -0
  137. genxai/tools/security/__init__.py +11 -0
  138. genxai/tools/security/limits.py +214 -0
  139. genxai/tools/security/policy.py +20 -0
  140. genxai/tools/security/sandbox.py +248 -0
  141. genxai/tools/templates.py +435 -0
  142. genxai/triggers/__init__.py +19 -0
  143. genxai/triggers/base.py +104 -0
  144. genxai/triggers/file_watcher.py +75 -0
  145. genxai/triggers/queue.py +68 -0
  146. genxai/triggers/registry.py +82 -0
  147. genxai/triggers/schedule.py +66 -0
  148. genxai/triggers/webhook.py +68 -0
  149. genxai/utils/__init__.py +1 -0
  150. genxai/utils/tokens.py +295 -0
  151. genxai_framework-0.1.0.dist-info/METADATA +495 -0
  152. genxai_framework-0.1.0.dist-info/RECORD +156 -0
  153. genxai_framework-0.1.0.dist-info/WHEEL +5 -0
  154. genxai_framework-0.1.0.dist-info/entry_points.txt +2 -0
  155. genxai_framework-0.1.0.dist-info/licenses/LICENSE +21 -0
  156. genxai_framework-0.1.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,461 @@
1
+ """Procedural memory implementation for storing learned skills and procedures."""
2
+
3
+ from typing import Any, Dict, List, Optional
4
+ from datetime import datetime
5
+ import logging
6
+ import uuid
7
+
8
+ from genxai.core.memory.persistence import JsonMemoryStore, MemoryPersistenceConfig
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ class Procedure:
14
+ """Represents a learned procedure or skill."""
15
+
16
+ def __init__(
17
+ self,
18
+ id: str,
19
+ name: str,
20
+ description: str,
21
+ steps: List[Dict[str, Any]],
22
+ preconditions: Optional[List[str]] = None,
23
+ postconditions: Optional[List[str]] = None,
24
+ success_count: int = 0,
25
+ failure_count: int = 0,
26
+ avg_duration: float = 0.0,
27
+ timestamp: Optional[datetime] = None,
28
+ metadata: Optional[Dict[str, Any]] = None,
29
+ ) -> None:
30
+ """Initialize procedure.
31
+
32
+ Args:
33
+ id: Unique procedure ID
34
+ name: Procedure name
35
+ description: Description of what the procedure does
36
+ steps: List of steps to execute
37
+ preconditions: Conditions that must be true before execution
38
+ postconditions: Expected conditions after execution
39
+ success_count: Number of successful executions
40
+ failure_count: Number of failed executions
41
+ avg_duration: Average execution duration
42
+ timestamp: When procedure was learned
43
+ metadata: Additional metadata
44
+ """
45
+ self.id = id
46
+ self.name = name
47
+ self.description = description
48
+ self.steps = steps
49
+ self.preconditions = preconditions or []
50
+ self.postconditions = postconditions or []
51
+ self.success_count = success_count
52
+ self.failure_count = failure_count
53
+ self.avg_duration = avg_duration
54
+ self.timestamp = timestamp or datetime.now()
55
+ self.last_used = timestamp or datetime.now()
56
+ self.metadata = metadata or {}
57
+
58
+ @property
59
+ def success_rate(self) -> float:
60
+ """Calculate success rate."""
61
+ total = self.success_count + self.failure_count
62
+ if total == 0:
63
+ return 0.0
64
+ return self.success_count / total
65
+
66
+ @property
67
+ def total_executions(self) -> int:
68
+ """Get total number of executions."""
69
+ return self.success_count + self.failure_count
70
+
71
+ def record_execution(
72
+ self,
73
+ success: bool,
74
+ duration: float,
75
+ ) -> None:
76
+ """Record an execution of this procedure.
77
+
78
+ Args:
79
+ success: Whether execution was successful
80
+ duration: Execution duration in seconds
81
+ """
82
+ if success:
83
+ self.success_count += 1
84
+ else:
85
+ self.failure_count += 1
86
+
87
+ # Update average duration
88
+ total = self.total_executions
89
+ self.avg_duration = (
90
+ (self.avg_duration * (total - 1) + duration) / total
91
+ )
92
+
93
+ self.last_used = datetime.now()
94
+
95
+ def to_dict(self) -> Dict[str, Any]:
96
+ """Convert to dictionary."""
97
+ return {
98
+ "id": self.id,
99
+ "name": self.name,
100
+ "description": self.description,
101
+ "steps": self.steps,
102
+ "preconditions": self.preconditions,
103
+ "postconditions": self.postconditions,
104
+ "success_count": self.success_count,
105
+ "failure_count": self.failure_count,
106
+ "success_rate": self.success_rate,
107
+ "avg_duration": self.avg_duration,
108
+ "timestamp": self.timestamp.isoformat(),
109
+ "last_used": self.last_used.isoformat(),
110
+ "metadata": self.metadata,
111
+ }
112
+
113
+ @classmethod
114
+ def from_dict(cls, data: Dict[str, Any]) -> "Procedure":
115
+ """Create procedure from dictionary."""
116
+ return cls(
117
+ id=data["id"],
118
+ name=data["name"],
119
+ description=data["description"],
120
+ steps=data["steps"],
121
+ preconditions=data.get("preconditions", []),
122
+ postconditions=data.get("postconditions", []),
123
+ success_count=data.get("success_count", 0),
124
+ failure_count=data.get("failure_count", 0),
125
+ avg_duration=data.get("avg_duration", 0.0),
126
+ timestamp=datetime.fromisoformat(data["timestamp"]) if data.get("timestamp") else None,
127
+ metadata=data.get("metadata", {}),
128
+ )
129
+
130
+ def __repr__(self) -> str:
131
+ """String representation."""
132
+ return f"Procedure({self.name}, success_rate={self.success_rate:.2f})"
133
+
134
+
135
+ class ProceduralMemory:
136
+ """Procedural memory for storing and retrieving learned procedures.
137
+
138
+ Stores:
139
+ - Step-by-step procedures
140
+ - Learned skills
141
+ - Execution statistics
142
+ - Success/failure patterns
143
+ """
144
+
145
+ def __init__(
146
+ self,
147
+ max_procedures: int = 100,
148
+ persistence: Optional[MemoryPersistenceConfig] = None,
149
+ ) -> None:
150
+ """Initialize procedural memory.
151
+
152
+ Args:
153
+ max_procedures: Maximum number of procedures to store
154
+ """
155
+ self._max_procedures = max_procedures
156
+ self._procedures: Dict[str, Procedure] = {}
157
+ self._name_index: Dict[str, str] = {} # name -> procedure_id
158
+ self._persistence = persistence
159
+ self._store = JsonMemoryStore(persistence) if persistence else None
160
+
161
+ if self._store and self._persistence and self._persistence.enabled:
162
+ self._load_from_disk()
163
+
164
+ logger.info("Initialized procedural memory")
165
+
166
+ async def store_procedure(
167
+ self,
168
+ name: str,
169
+ description: str,
170
+ steps: List[Dict[str, Any]],
171
+ preconditions: Optional[List[str]] = None,
172
+ postconditions: Optional[List[str]] = None,
173
+ metadata: Optional[Dict[str, Any]] = None,
174
+ ) -> Procedure:
175
+ """Store a new procedure.
176
+
177
+ Args:
178
+ name: Procedure name
179
+ description: Description
180
+ steps: List of steps
181
+ preconditions: Required preconditions
182
+ postconditions: Expected postconditions
183
+ metadata: Additional metadata
184
+
185
+ Returns:
186
+ Created procedure
187
+ """
188
+ # Check if procedure with same name exists
189
+ if name in self._name_index:
190
+ existing_id = self._name_index[name]
191
+ existing = self._procedures[existing_id]
192
+ logger.debug(f"Procedure '{name}' already exists, updating...")
193
+
194
+ # Update existing procedure
195
+ existing.description = description
196
+ existing.steps = steps
197
+ existing.preconditions = preconditions or []
198
+ existing.postconditions = postconditions or []
199
+ existing.metadata = metadata or {}
200
+ existing.timestamp = datetime.now()
201
+
202
+ return existing
203
+
204
+ # Create new procedure
205
+ procedure = Procedure(
206
+ id=str(uuid.uuid4()),
207
+ name=name,
208
+ description=description,
209
+ steps=steps,
210
+ preconditions=preconditions,
211
+ postconditions=postconditions,
212
+ metadata=metadata,
213
+ )
214
+
215
+ # Store procedure
216
+ self._procedures[procedure.id] = procedure
217
+ self._name_index[name] = procedure.id
218
+
219
+ # Enforce max procedures limit
220
+ if len(self._procedures) > self._max_procedures:
221
+ # Remove least successful procedure
222
+ least_successful = min(
223
+ self._procedures.values(),
224
+ key=lambda p: (p.success_rate, p.total_executions)
225
+ )
226
+ await self.delete_procedure(least_successful.id)
227
+
228
+ logger.debug(f"Stored procedure: {procedure}")
229
+ self._persist()
230
+ return procedure
231
+
232
+ async def retrieve_procedure(
233
+ self,
234
+ procedure_id: Optional[str] = None,
235
+ name: Optional[str] = None,
236
+ ) -> Optional[Procedure]:
237
+ """Retrieve a procedure by ID or name.
238
+
239
+ Args:
240
+ procedure_id: Procedure ID
241
+ name: Procedure name
242
+
243
+ Returns:
244
+ Procedure if found, None otherwise
245
+ """
246
+ if procedure_id:
247
+ return self._procedures.get(procedure_id)
248
+
249
+ if name and name in self._name_index:
250
+ procedure_id = self._name_index[name]
251
+ return self._procedures.get(procedure_id)
252
+
253
+ return None
254
+
255
+ async def retrieve_all(
256
+ self,
257
+ min_success_rate: float = 0.0,
258
+ sort_by: str = "success_rate",
259
+ ) -> List[Procedure]:
260
+ """Retrieve all procedures.
261
+
262
+ Args:
263
+ min_success_rate: Minimum success rate filter
264
+ sort_by: Sort key ("success_rate", "executions", "recent")
265
+
266
+ Returns:
267
+ List of procedures
268
+ """
269
+ procedures = list(self._procedures.values())
270
+
271
+ # Filter by success rate
272
+ if min_success_rate > 0.0:
273
+ procedures = [
274
+ p for p in procedures
275
+ if p.success_rate >= min_success_rate
276
+ ]
277
+
278
+ # Sort
279
+ if sort_by == "success_rate":
280
+ procedures.sort(key=lambda p: p.success_rate, reverse=True)
281
+ elif sort_by == "executions":
282
+ procedures.sort(key=lambda p: p.total_executions, reverse=True)
283
+ elif sort_by == "recent":
284
+ procedures.sort(key=lambda p: p.last_used, reverse=True)
285
+
286
+ return procedures
287
+
288
+ async def search_procedures(
289
+ self,
290
+ query: str,
291
+ limit: int = 5,
292
+ ) -> List[Procedure]:
293
+ """Search procedures by name or description.
294
+
295
+ Args:
296
+ query: Search query
297
+ limit: Maximum number of results
298
+
299
+ Returns:
300
+ List of matching procedures
301
+ """
302
+ query_lower = query.lower()
303
+ matches = []
304
+
305
+ for procedure in self._procedures.values():
306
+ # Check name and description
307
+ if (query_lower in procedure.name.lower() or
308
+ query_lower in procedure.description.lower()):
309
+ matches.append(procedure)
310
+
311
+ # Sort by success rate
312
+ matches.sort(key=lambda p: p.success_rate, reverse=True)
313
+
314
+ return matches[:limit]
315
+
316
+ async def record_execution(
317
+ self,
318
+ procedure_id: str,
319
+ success: bool,
320
+ duration: float,
321
+ ) -> bool:
322
+ """Record an execution of a procedure.
323
+
324
+ Args:
325
+ procedure_id: Procedure ID
326
+ success: Whether execution was successful
327
+ duration: Execution duration
328
+
329
+ Returns:
330
+ True if recorded, False if procedure not found
331
+ """
332
+ procedure = self._procedures.get(procedure_id)
333
+ if not procedure:
334
+ return False
335
+
336
+ procedure.record_execution(success, duration)
337
+ self._persist()
338
+ logger.debug(
339
+ f"Recorded execution for {procedure.name}: "
340
+ f"success={success}, duration={duration:.2f}s"
341
+ )
342
+ return True
343
+
344
+ async def get_best_procedures(
345
+ self,
346
+ limit: int = 10,
347
+ min_executions: int = 3,
348
+ ) -> List[Procedure]:
349
+ """Get best performing procedures.
350
+
351
+ Args:
352
+ limit: Maximum number of procedures
353
+ min_executions: Minimum number of executions required
354
+
355
+ Returns:
356
+ List of best procedures
357
+ """
358
+ procedures = [
359
+ p for p in self._procedures.values()
360
+ if p.total_executions >= min_executions
361
+ ]
362
+
363
+ # Sort by success rate, then by executions
364
+ procedures.sort(
365
+ key=lambda p: (p.success_rate, p.total_executions),
366
+ reverse=True
367
+ )
368
+
369
+ return procedures[:limit]
370
+
371
+ async def delete_procedure(self, procedure_id: str) -> bool:
372
+ """Delete a procedure.
373
+
374
+ Args:
375
+ procedure_id: Procedure ID
376
+
377
+ Returns:
378
+ True if deleted, False if not found
379
+ """
380
+ if procedure_id not in self._procedures:
381
+ return False
382
+
383
+ procedure = self._procedures[procedure_id]
384
+
385
+ # Remove from indexes
386
+ if procedure.name in self._name_index:
387
+ del self._name_index[procedure.name]
388
+
389
+ # Remove procedure
390
+ del self._procedures[procedure_id]
391
+
392
+ self._persist()
393
+
394
+ logger.debug(f"Deleted procedure: {procedure}")
395
+ return True
396
+
397
+ async def clear(self) -> None:
398
+ """Clear all procedures."""
399
+ self._procedures.clear()
400
+ self._name_index.clear()
401
+ logger.info("Cleared all procedures")
402
+
403
+ self._persist()
404
+
405
+ async def get_stats(self) -> Dict[str, Any]:
406
+ """Get procedural memory statistics.
407
+
408
+ Returns:
409
+ Statistics dictionary
410
+ """
411
+ if not self._procedures:
412
+ return {
413
+ "total_procedures": 0,
414
+ "persistence": bool(self._persistence and self._persistence.enabled),
415
+ }
416
+
417
+ procedures = list(self._procedures.values())
418
+
419
+ total_executions = sum(p.total_executions for p in procedures)
420
+ total_successes = sum(p.success_count for p in procedures)
421
+
422
+ return {
423
+ "total_procedures": len(procedures),
424
+ "total_executions": total_executions,
425
+ "total_successes": total_successes,
426
+ "total_failures": total_executions - total_successes,
427
+ "overall_success_rate": total_successes / total_executions if total_executions > 0 else 0.0,
428
+ "avg_success_rate": sum(p.success_rate for p in procedures) / len(procedures),
429
+ "most_used": max(procedures, key=lambda p: p.total_executions).name,
430
+ "most_successful": max(procedures, key=lambda p: p.success_rate).name,
431
+ "oldest_procedure": min(p.timestamp for p in procedures).isoformat(),
432
+ "newest_procedure": max(p.timestamp for p in procedures).isoformat(),
433
+ "persistence": bool(self._persistence and self._persistence.enabled),
434
+ }
435
+
436
+ def _persist(self) -> None:
437
+ if not self._store:
438
+ return
439
+ payload = {
440
+ "procedures": [proc.to_dict() for proc in self._procedures.values()],
441
+ "name_index": self._name_index,
442
+ }
443
+ self._store.save_mapping("procedural_memory.json", payload)
444
+
445
+ def _load_from_disk(self) -> None:
446
+ if not self._store:
447
+ return
448
+ data = self._store.load_mapping("procedural_memory.json")
449
+ if not data:
450
+ return
451
+ procedures = data.get("procedures", [])
452
+ self._procedures = {proc["id"]: Procedure.from_dict(proc) for proc in procedures}
453
+ self._name_index = data.get("name_index", {})
454
+
455
+ def __len__(self) -> int:
456
+ """Get number of stored procedures."""
457
+ return len(self._procedures)
458
+
459
+ def __repr__(self) -> str:
460
+ """String representation."""
461
+ return f"ProceduralMemory(procedures={len(self._procedures)})"