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,543 @@
|
|
|
1
|
+
"""Memory system manager coordinating all memory types."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from genxai.core.memory.base import Memory, MemoryType, MemoryConfig
|
|
8
|
+
from genxai.core.memory.short_term import ShortTermMemory
|
|
9
|
+
from genxai.core.memory.long_term import LongTermMemory
|
|
10
|
+
from genxai.core.memory.episodic import EpisodicMemory, Episode
|
|
11
|
+
from genxai.core.memory.semantic import SemanticMemory, Fact
|
|
12
|
+
from genxai.core.memory.procedural import ProceduralMemory, Procedure
|
|
13
|
+
from genxai.core.memory.working import WorkingMemory
|
|
14
|
+
from genxai.core.memory.vector_store import VectorStoreFactory
|
|
15
|
+
from genxai.core.memory.embedding import EmbeddingServiceFactory
|
|
16
|
+
from genxai.core.memory.persistence import MemoryPersistenceConfig
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class MemorySystem:
|
|
22
|
+
"""Comprehensive memory management system.
|
|
23
|
+
|
|
24
|
+
Orchestrates all memory types:
|
|
25
|
+
- Short-term: Recent interactions
|
|
26
|
+
- Long-term: Persistent memories with vector search
|
|
27
|
+
- Episodic: Agent experiences and episodes
|
|
28
|
+
- Semantic: Facts and knowledge
|
|
29
|
+
- Procedural: Learned skills and procedures
|
|
30
|
+
- Working: Active processing context
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
agent_id: str,
|
|
36
|
+
config: Optional[MemoryConfig] = None,
|
|
37
|
+
vector_store_backend: Optional[str] = None,
|
|
38
|
+
embedding_provider: Optional[str] = None,
|
|
39
|
+
persistence_enabled: bool = False,
|
|
40
|
+
persistence_path: Optional[Path] = None,
|
|
41
|
+
persistence_backend: str = "json",
|
|
42
|
+
persistence_sqlite_path: Optional[Path] = None,
|
|
43
|
+
) -> None:
|
|
44
|
+
"""Initialize memory system.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
agent_id: ID of the agent this memory belongs to
|
|
48
|
+
config: Memory configuration
|
|
49
|
+
vector_store_backend: Vector store backend ("chromadb", "pinecone")
|
|
50
|
+
embedding_provider: Embedding provider ("openai", "local", "cohere")
|
|
51
|
+
"""
|
|
52
|
+
self.agent_id = agent_id
|
|
53
|
+
self.config = config or MemoryConfig()
|
|
54
|
+
self._persistence = MemoryPersistenceConfig(
|
|
55
|
+
base_dir=persistence_path or Path(".genxai/memory"),
|
|
56
|
+
enabled=persistence_enabled,
|
|
57
|
+
backend=persistence_backend,
|
|
58
|
+
sqlite_path=persistence_sqlite_path,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# Initialize short-term memory
|
|
62
|
+
self.short_term = ShortTermMemory(capacity=self.config.short_term_capacity)
|
|
63
|
+
|
|
64
|
+
# Initialize working memory
|
|
65
|
+
self.working = WorkingMemory(capacity=self.config.working_capacity)
|
|
66
|
+
|
|
67
|
+
# Initialize long-term memory with vector store
|
|
68
|
+
self.long_term: Optional[LongTermMemory] = None
|
|
69
|
+
self.vector_store = None
|
|
70
|
+
self.embedding_service = None
|
|
71
|
+
|
|
72
|
+
if self.config.long_term_enabled:
|
|
73
|
+
try:
|
|
74
|
+
# Create vector store
|
|
75
|
+
backend = vector_store_backend or self.config.vector_db
|
|
76
|
+
if backend:
|
|
77
|
+
self.vector_store = VectorStoreFactory.create(
|
|
78
|
+
backend=backend,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# Create embedding service
|
|
82
|
+
provider = embedding_provider or "openai"
|
|
83
|
+
if provider:
|
|
84
|
+
self.embedding_service = EmbeddingServiceFactory.create(
|
|
85
|
+
provider=provider,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Initialize long-term memory
|
|
89
|
+
self.long_term = LongTermMemory(
|
|
90
|
+
config=self.config,
|
|
91
|
+
vector_store=self.vector_store,
|
|
92
|
+
embedding_service=self.embedding_service,
|
|
93
|
+
persistence=self._persistence,
|
|
94
|
+
)
|
|
95
|
+
logger.info("Long-term memory initialized with vector store")
|
|
96
|
+
except Exception as e:
|
|
97
|
+
logger.warning(f"Failed to initialize long-term memory: {e}")
|
|
98
|
+
self.long_term = LongTermMemory(
|
|
99
|
+
config=self.config,
|
|
100
|
+
persistence=self._persistence,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# Initialize episodic memory
|
|
104
|
+
self.episodic: Optional[EpisodicMemory] = None
|
|
105
|
+
if self.config.episodic_enabled:
|
|
106
|
+
self.episodic = EpisodicMemory(persistence=self._persistence)
|
|
107
|
+
logger.info("Episodic memory initialized")
|
|
108
|
+
|
|
109
|
+
# Initialize semantic memory
|
|
110
|
+
self.semantic: Optional[SemanticMemory] = None
|
|
111
|
+
if self.config.semantic_enabled:
|
|
112
|
+
self.semantic = SemanticMemory(persistence=self._persistence)
|
|
113
|
+
logger.info("Semantic memory initialized")
|
|
114
|
+
|
|
115
|
+
# Initialize procedural memory
|
|
116
|
+
self.procedural: Optional[ProceduralMemory] = None
|
|
117
|
+
if self.config.procedural_enabled:
|
|
118
|
+
self.procedural = ProceduralMemory(persistence=self._persistence)
|
|
119
|
+
logger.info("Procedural memory initialized")
|
|
120
|
+
|
|
121
|
+
logger.info(f"Memory system initialized for agent: {agent_id}")
|
|
122
|
+
|
|
123
|
+
# ==================== Short-term Memory ====================
|
|
124
|
+
|
|
125
|
+
async def add_to_short_term(
|
|
126
|
+
self,
|
|
127
|
+
content: Any,
|
|
128
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
129
|
+
) -> None:
|
|
130
|
+
"""Add content to short-term memory.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
content: Content to store
|
|
134
|
+
metadata: Optional metadata
|
|
135
|
+
"""
|
|
136
|
+
await self.short_term.add(content, metadata)
|
|
137
|
+
|
|
138
|
+
async def get_short_term_context(self, max_tokens: int = 4000) -> str:
|
|
139
|
+
"""Get context from short-term memory for LLM.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
max_tokens: Maximum tokens to include
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
Formatted context string
|
|
146
|
+
"""
|
|
147
|
+
return await self.short_term.get_context(max_tokens)
|
|
148
|
+
|
|
149
|
+
async def clear_short_term(self) -> None:
|
|
150
|
+
"""Clear short-term memory."""
|
|
151
|
+
# clear() is sync; clear_async() is the async variant.
|
|
152
|
+
await self.short_term.clear_async()
|
|
153
|
+
|
|
154
|
+
# ==================== Working Memory ====================
|
|
155
|
+
|
|
156
|
+
def add_to_working(
|
|
157
|
+
self,
|
|
158
|
+
key: str,
|
|
159
|
+
value: Any,
|
|
160
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
161
|
+
) -> None:
|
|
162
|
+
"""Add item to working memory.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
key: Item key
|
|
166
|
+
value: Item value
|
|
167
|
+
metadata: Optional metadata
|
|
168
|
+
"""
|
|
169
|
+
self.working.add(key, value, metadata)
|
|
170
|
+
|
|
171
|
+
def get_from_working(self, key: str) -> Optional[Any]:
|
|
172
|
+
"""Get item from working memory.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
key: Item key
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
Item value if found
|
|
179
|
+
"""
|
|
180
|
+
return self.working.get(key)
|
|
181
|
+
|
|
182
|
+
def clear_working(self) -> None:
|
|
183
|
+
"""Clear working memory."""
|
|
184
|
+
self.working.clear()
|
|
185
|
+
|
|
186
|
+
# ==================== Long-term Memory ====================
|
|
187
|
+
|
|
188
|
+
async def add_to_long_term(
|
|
189
|
+
self,
|
|
190
|
+
memory: Memory,
|
|
191
|
+
ttl: Optional[int] = None,
|
|
192
|
+
) -> None:
|
|
193
|
+
"""Add memory to long-term storage.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
memory: Memory to store
|
|
197
|
+
ttl: Time-to-live in seconds
|
|
198
|
+
"""
|
|
199
|
+
if self.long_term is None:
|
|
200
|
+
logger.warning("Long-term memory not enabled")
|
|
201
|
+
return
|
|
202
|
+
|
|
203
|
+
# Generate embedding if vector store available
|
|
204
|
+
if self.embedding_service and self.vector_store:
|
|
205
|
+
try:
|
|
206
|
+
embedding = await self.embedding_service.embed(str(memory.content))
|
|
207
|
+
await self.vector_store.store(memory, embedding)
|
|
208
|
+
logger.debug(f"Stored memory in vector store: {memory.id}")
|
|
209
|
+
except Exception as e:
|
|
210
|
+
logger.error(f"Failed to store in vector store: {e}")
|
|
211
|
+
|
|
212
|
+
# Store in long-term memory
|
|
213
|
+
self.long_term.store(memory, ttl)
|
|
214
|
+
|
|
215
|
+
async def search_long_term(
|
|
216
|
+
self,
|
|
217
|
+
query: str,
|
|
218
|
+
limit: int = 10,
|
|
219
|
+
) -> List[Tuple[Memory, float]]:
|
|
220
|
+
"""Search long-term memory by semantic similarity.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
query: Search query
|
|
224
|
+
limit: Maximum results
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
List of (memory, similarity_score) tuples
|
|
228
|
+
"""
|
|
229
|
+
if self.vector_store is None or self.embedding_service is None:
|
|
230
|
+
logger.warning("Vector search not available")
|
|
231
|
+
return []
|
|
232
|
+
|
|
233
|
+
try:
|
|
234
|
+
# Generate query embedding
|
|
235
|
+
query_embedding = await self.embedding_service.embed(query)
|
|
236
|
+
|
|
237
|
+
# Search vector store
|
|
238
|
+
results = await self.vector_store.search(query_embedding, limit=limit)
|
|
239
|
+
return results
|
|
240
|
+
except Exception as e:
|
|
241
|
+
logger.error(f"Failed to search long-term memory: {e}")
|
|
242
|
+
return []
|
|
243
|
+
|
|
244
|
+
# ==================== Episodic Memory ====================
|
|
245
|
+
|
|
246
|
+
async def store_episode(
|
|
247
|
+
self,
|
|
248
|
+
task: str,
|
|
249
|
+
actions: List[Dict[str, Any]],
|
|
250
|
+
outcome: Dict[str, Any],
|
|
251
|
+
duration: float,
|
|
252
|
+
success: bool,
|
|
253
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
254
|
+
) -> Optional[Episode]:
|
|
255
|
+
"""Store an episode.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
task: Task description
|
|
259
|
+
actions: Actions taken
|
|
260
|
+
outcome: Outcome
|
|
261
|
+
duration: Duration in seconds
|
|
262
|
+
success: Success flag
|
|
263
|
+
metadata: Optional metadata
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
Created episode
|
|
267
|
+
"""
|
|
268
|
+
if self.episodic is None:
|
|
269
|
+
logger.warning("Episodic memory not enabled")
|
|
270
|
+
return None
|
|
271
|
+
|
|
272
|
+
return await self.episodic.store_episode(
|
|
273
|
+
agent_id=self.agent_id,
|
|
274
|
+
task=task,
|
|
275
|
+
actions=actions,
|
|
276
|
+
outcome=outcome,
|
|
277
|
+
duration=duration,
|
|
278
|
+
success=success,
|
|
279
|
+
metadata=metadata,
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
async def get_similar_episodes(
|
|
283
|
+
self,
|
|
284
|
+
task: str,
|
|
285
|
+
limit: int = 5,
|
|
286
|
+
) -> List[Episode]:
|
|
287
|
+
"""Get episodes similar to a task.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
task: Task description
|
|
291
|
+
limit: Maximum results
|
|
292
|
+
|
|
293
|
+
Returns:
|
|
294
|
+
List of similar episodes
|
|
295
|
+
"""
|
|
296
|
+
if self.episodic is None:
|
|
297
|
+
return []
|
|
298
|
+
|
|
299
|
+
return await self.episodic.retrieve_similar_tasks(task, limit)
|
|
300
|
+
|
|
301
|
+
async def get_success_rate(
|
|
302
|
+
self,
|
|
303
|
+
task_pattern: Optional[str] = None,
|
|
304
|
+
) -> float:
|
|
305
|
+
"""Get success rate for tasks.
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
task_pattern: Optional task pattern filter
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
Success rate (0.0 to 1.0)
|
|
312
|
+
"""
|
|
313
|
+
if self.episodic is None:
|
|
314
|
+
return 0.0
|
|
315
|
+
|
|
316
|
+
return await self.episodic.get_success_rate(
|
|
317
|
+
agent_id=self.agent_id,
|
|
318
|
+
task_pattern=task_pattern,
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
# ==================== Semantic Memory ====================
|
|
322
|
+
|
|
323
|
+
async def store_fact(
|
|
324
|
+
self,
|
|
325
|
+
subject: str,
|
|
326
|
+
predicate: str,
|
|
327
|
+
object: str,
|
|
328
|
+
confidence: float = 1.0,
|
|
329
|
+
source: Optional[str] = None,
|
|
330
|
+
) -> Optional[Fact]:
|
|
331
|
+
"""Store a fact.
|
|
332
|
+
|
|
333
|
+
Args:
|
|
334
|
+
subject: Subject entity
|
|
335
|
+
predicate: Relationship/property
|
|
336
|
+
object: Object entity/value
|
|
337
|
+
confidence: Confidence score
|
|
338
|
+
source: Source of fact
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
Created fact
|
|
342
|
+
"""
|
|
343
|
+
if self.semantic is None:
|
|
344
|
+
logger.warning("Semantic memory not enabled")
|
|
345
|
+
return None
|
|
346
|
+
|
|
347
|
+
return await self.semantic.store_fact(
|
|
348
|
+
subject=subject,
|
|
349
|
+
predicate=predicate,
|
|
350
|
+
object=object,
|
|
351
|
+
confidence=confidence,
|
|
352
|
+
source=source,
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
async def query_facts(
|
|
356
|
+
self,
|
|
357
|
+
subject: Optional[str] = None,
|
|
358
|
+
predicate: Optional[str] = None,
|
|
359
|
+
object: Optional[str] = None,
|
|
360
|
+
) -> List[Fact]:
|
|
361
|
+
"""Query facts.
|
|
362
|
+
|
|
363
|
+
Args:
|
|
364
|
+
subject: Optional subject filter
|
|
365
|
+
predicate: Optional predicate filter
|
|
366
|
+
object: Optional object filter
|
|
367
|
+
|
|
368
|
+
Returns:
|
|
369
|
+
List of matching facts
|
|
370
|
+
"""
|
|
371
|
+
if self.semantic is None:
|
|
372
|
+
return []
|
|
373
|
+
|
|
374
|
+
return await self.semantic.query(
|
|
375
|
+
subject=subject,
|
|
376
|
+
predicate=predicate,
|
|
377
|
+
object=object,
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
# ==================== Procedural Memory ====================
|
|
381
|
+
|
|
382
|
+
async def store_procedure(
|
|
383
|
+
self,
|
|
384
|
+
name: str,
|
|
385
|
+
description: str,
|
|
386
|
+
steps: List[Dict[str, Any]],
|
|
387
|
+
preconditions: Optional[List[str]] = None,
|
|
388
|
+
postconditions: Optional[List[str]] = None,
|
|
389
|
+
) -> Optional[Procedure]:
|
|
390
|
+
"""Store a procedure.
|
|
391
|
+
|
|
392
|
+
Args:
|
|
393
|
+
name: Procedure name
|
|
394
|
+
description: Description
|
|
395
|
+
steps: List of steps
|
|
396
|
+
preconditions: Required preconditions
|
|
397
|
+
postconditions: Expected postconditions
|
|
398
|
+
|
|
399
|
+
Returns:
|
|
400
|
+
Created procedure
|
|
401
|
+
"""
|
|
402
|
+
if self.procedural is None:
|
|
403
|
+
logger.warning("Procedural memory not enabled")
|
|
404
|
+
return None
|
|
405
|
+
|
|
406
|
+
return await self.procedural.store_procedure(
|
|
407
|
+
name=name,
|
|
408
|
+
description=description,
|
|
409
|
+
steps=steps,
|
|
410
|
+
preconditions=preconditions,
|
|
411
|
+
postconditions=postconditions,
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
async def get_procedure(self, name: str) -> Optional[Procedure]:
|
|
415
|
+
"""Get a procedure by name.
|
|
416
|
+
|
|
417
|
+
Args:
|
|
418
|
+
name: Procedure name
|
|
419
|
+
|
|
420
|
+
Returns:
|
|
421
|
+
Procedure if found
|
|
422
|
+
"""
|
|
423
|
+
if self.procedural is None:
|
|
424
|
+
return None
|
|
425
|
+
|
|
426
|
+
return await self.procedural.retrieve_procedure(name=name)
|
|
427
|
+
|
|
428
|
+
async def record_procedure_execution(
|
|
429
|
+
self,
|
|
430
|
+
procedure_id: str,
|
|
431
|
+
success: bool,
|
|
432
|
+
duration: float,
|
|
433
|
+
) -> bool:
|
|
434
|
+
"""Record procedure execution.
|
|
435
|
+
|
|
436
|
+
Args:
|
|
437
|
+
procedure_id: Procedure ID
|
|
438
|
+
success: Success flag
|
|
439
|
+
duration: Duration in seconds
|
|
440
|
+
|
|
441
|
+
Returns:
|
|
442
|
+
True if recorded
|
|
443
|
+
"""
|
|
444
|
+
if self.procedural is None:
|
|
445
|
+
return False
|
|
446
|
+
|
|
447
|
+
return await self.procedural.record_execution(
|
|
448
|
+
procedure_id=procedure_id,
|
|
449
|
+
success=success,
|
|
450
|
+
duration=duration,
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
# ==================== Memory Consolidation ====================
|
|
454
|
+
|
|
455
|
+
async def consolidate_memories(
|
|
456
|
+
self,
|
|
457
|
+
importance_threshold: float = 0.7,
|
|
458
|
+
) -> Dict[str, int]:
|
|
459
|
+
"""Consolidate important memories from short-term to long-term.
|
|
460
|
+
|
|
461
|
+
Args:
|
|
462
|
+
importance_threshold: Minimum importance to consolidate
|
|
463
|
+
|
|
464
|
+
Returns:
|
|
465
|
+
Statistics about consolidation
|
|
466
|
+
"""
|
|
467
|
+
if self.long_term is None:
|
|
468
|
+
logger.warning("Long-term memory not enabled")
|
|
469
|
+
return {"consolidated": 0}
|
|
470
|
+
|
|
471
|
+
consolidated = 0
|
|
472
|
+
|
|
473
|
+
# Get important memories from short-term
|
|
474
|
+
for memory in self.short_term.memories:
|
|
475
|
+
if memory.importance >= importance_threshold:
|
|
476
|
+
# Move to long-term
|
|
477
|
+
await self.add_to_long_term(memory)
|
|
478
|
+
consolidated += 1
|
|
479
|
+
|
|
480
|
+
logger.info(f"Consolidated {consolidated} memories to long-term")
|
|
481
|
+
|
|
482
|
+
return {
|
|
483
|
+
"consolidated": consolidated,
|
|
484
|
+
"threshold": importance_threshold,
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
# ==================== Statistics ====================
|
|
488
|
+
|
|
489
|
+
async def get_stats(self) -> Dict[str, Any]:
|
|
490
|
+
"""Get comprehensive memory system statistics.
|
|
491
|
+
|
|
492
|
+
Returns:
|
|
493
|
+
Statistics dictionary
|
|
494
|
+
"""
|
|
495
|
+
stats = {
|
|
496
|
+
"agent_id": self.agent_id,
|
|
497
|
+
"short_term": self.short_term.get_stats(),
|
|
498
|
+
"working": self.working.get_stats(),
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if self.long_term:
|
|
502
|
+
stats["long_term"] = self.long_term.get_stats()
|
|
503
|
+
|
|
504
|
+
if self.episodic:
|
|
505
|
+
stats["episodic"] = await self.episodic.get_stats()
|
|
506
|
+
|
|
507
|
+
if self.semantic:
|
|
508
|
+
stats["semantic"] = await self.semantic.get_stats()
|
|
509
|
+
|
|
510
|
+
if self.procedural:
|
|
511
|
+
stats["procedural"] = await self.procedural.get_stats()
|
|
512
|
+
|
|
513
|
+
if self.vector_store:
|
|
514
|
+
try:
|
|
515
|
+
stats["vector_store"] = await self.vector_store.get_stats()
|
|
516
|
+
except Exception as exc:
|
|
517
|
+
logger.warning("Vector store stats unavailable: %s", exc)
|
|
518
|
+
stats["vector_store"] = {"error": str(exc)}
|
|
519
|
+
|
|
520
|
+
stats["persistence"] = {
|
|
521
|
+
"enabled": self._persistence.enabled,
|
|
522
|
+
"path": str(self._persistence.base_dir),
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
return stats
|
|
526
|
+
|
|
527
|
+
def __repr__(self) -> str:
|
|
528
|
+
"""String representation."""
|
|
529
|
+
enabled = []
|
|
530
|
+
if self.short_term:
|
|
531
|
+
enabled.append("short_term")
|
|
532
|
+
if self.working:
|
|
533
|
+
enabled.append("working")
|
|
534
|
+
if self.long_term:
|
|
535
|
+
enabled.append("long_term")
|
|
536
|
+
if self.episodic:
|
|
537
|
+
enabled.append("episodic")
|
|
538
|
+
if self.semantic:
|
|
539
|
+
enabled.append("semantic")
|
|
540
|
+
if self.procedural:
|
|
541
|
+
enabled.append("procedural")
|
|
542
|
+
|
|
543
|
+
return f"MemorySystem(agent_id={self.agent_id}, enabled={enabled})"
|