powermem 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.
- powermem/__init__.py +103 -0
- powermem/agent/__init__.py +35 -0
- powermem/agent/abstract/__init__.py +22 -0
- powermem/agent/abstract/collaboration.py +259 -0
- powermem/agent/abstract/context.py +187 -0
- powermem/agent/abstract/manager.py +232 -0
- powermem/agent/abstract/permission.py +217 -0
- powermem/agent/abstract/privacy.py +267 -0
- powermem/agent/abstract/scope.py +199 -0
- powermem/agent/agent.py +791 -0
- powermem/agent/components/__init__.py +18 -0
- powermem/agent/components/collaboration_coordinator.py +645 -0
- powermem/agent/components/permission_controller.py +586 -0
- powermem/agent/components/privacy_protector.py +767 -0
- powermem/agent/components/scope_controller.py +685 -0
- powermem/agent/factories/__init__.py +16 -0
- powermem/agent/factories/agent_factory.py +266 -0
- powermem/agent/factories/config_factory.py +308 -0
- powermem/agent/factories/memory_factory.py +229 -0
- powermem/agent/implementations/__init__.py +16 -0
- powermem/agent/implementations/hybrid.py +728 -0
- powermem/agent/implementations/multi_agent.py +1040 -0
- powermem/agent/implementations/multi_user.py +1020 -0
- powermem/agent/types.py +53 -0
- powermem/agent/wrappers/__init__.py +14 -0
- powermem/agent/wrappers/agent_memory_wrapper.py +427 -0
- powermem/agent/wrappers/compatibility_wrapper.py +520 -0
- powermem/config_loader.py +318 -0
- powermem/configs.py +249 -0
- powermem/core/__init__.py +19 -0
- powermem/core/async_memory.py +1493 -0
- powermem/core/audit.py +258 -0
- powermem/core/base.py +165 -0
- powermem/core/memory.py +1567 -0
- powermem/core/setup.py +162 -0
- powermem/core/telemetry.py +215 -0
- powermem/integrations/__init__.py +17 -0
- powermem/integrations/embeddings/__init__.py +13 -0
- powermem/integrations/embeddings/aws_bedrock.py +100 -0
- powermem/integrations/embeddings/azure_openai.py +55 -0
- powermem/integrations/embeddings/base.py +31 -0
- powermem/integrations/embeddings/config/base.py +132 -0
- powermem/integrations/embeddings/configs.py +31 -0
- powermem/integrations/embeddings/factory.py +48 -0
- powermem/integrations/embeddings/gemini.py +39 -0
- powermem/integrations/embeddings/huggingface.py +41 -0
- powermem/integrations/embeddings/langchain.py +35 -0
- powermem/integrations/embeddings/lmstudio.py +29 -0
- powermem/integrations/embeddings/mock.py +11 -0
- powermem/integrations/embeddings/ollama.py +53 -0
- powermem/integrations/embeddings/openai.py +49 -0
- powermem/integrations/embeddings/qwen.py +102 -0
- powermem/integrations/embeddings/together.py +31 -0
- powermem/integrations/embeddings/vertexai.py +54 -0
- powermem/integrations/llm/__init__.py +18 -0
- powermem/integrations/llm/anthropic.py +87 -0
- powermem/integrations/llm/base.py +132 -0
- powermem/integrations/llm/config/anthropic.py +56 -0
- powermem/integrations/llm/config/azure.py +56 -0
- powermem/integrations/llm/config/base.py +62 -0
- powermem/integrations/llm/config/deepseek.py +56 -0
- powermem/integrations/llm/config/ollama.py +56 -0
- powermem/integrations/llm/config/openai.py +79 -0
- powermem/integrations/llm/config/qwen.py +68 -0
- powermem/integrations/llm/config/qwen_asr.py +46 -0
- powermem/integrations/llm/config/vllm.py +56 -0
- powermem/integrations/llm/configs.py +26 -0
- powermem/integrations/llm/deepseek.py +106 -0
- powermem/integrations/llm/factory.py +118 -0
- powermem/integrations/llm/gemini.py +201 -0
- powermem/integrations/llm/langchain.py +65 -0
- powermem/integrations/llm/ollama.py +106 -0
- powermem/integrations/llm/openai.py +166 -0
- powermem/integrations/llm/openai_structured.py +80 -0
- powermem/integrations/llm/qwen.py +207 -0
- powermem/integrations/llm/qwen_asr.py +171 -0
- powermem/integrations/llm/vllm.py +106 -0
- powermem/integrations/rerank/__init__.py +20 -0
- powermem/integrations/rerank/base.py +43 -0
- powermem/integrations/rerank/config/__init__.py +7 -0
- powermem/integrations/rerank/config/base.py +27 -0
- powermem/integrations/rerank/configs.py +23 -0
- powermem/integrations/rerank/factory.py +68 -0
- powermem/integrations/rerank/qwen.py +159 -0
- powermem/intelligence/__init__.py +17 -0
- powermem/intelligence/ebbinghaus_algorithm.py +354 -0
- powermem/intelligence/importance_evaluator.py +361 -0
- powermem/intelligence/intelligent_memory_manager.py +284 -0
- powermem/intelligence/manager.py +148 -0
- powermem/intelligence/plugin.py +229 -0
- powermem/prompts/__init__.py +29 -0
- powermem/prompts/graph/graph_prompts.py +217 -0
- powermem/prompts/graph/graph_tools_prompts.py +469 -0
- powermem/prompts/importance_evaluation.py +246 -0
- powermem/prompts/intelligent_memory_prompts.py +163 -0
- powermem/prompts/templates.py +193 -0
- powermem/storage/__init__.py +14 -0
- powermem/storage/adapter.py +896 -0
- powermem/storage/base.py +109 -0
- powermem/storage/config/base.py +13 -0
- powermem/storage/config/oceanbase.py +58 -0
- powermem/storage/config/pgvector.py +52 -0
- powermem/storage/config/sqlite.py +27 -0
- powermem/storage/configs.py +159 -0
- powermem/storage/factory.py +59 -0
- powermem/storage/migration_manager.py +438 -0
- powermem/storage/oceanbase/__init__.py +8 -0
- powermem/storage/oceanbase/constants.py +162 -0
- powermem/storage/oceanbase/oceanbase.py +1384 -0
- powermem/storage/oceanbase/oceanbase_graph.py +1441 -0
- powermem/storage/pgvector/__init__.py +7 -0
- powermem/storage/pgvector/pgvector.py +420 -0
- powermem/storage/sqlite/__init__.py +0 -0
- powermem/storage/sqlite/sqlite.py +218 -0
- powermem/storage/sqlite/sqlite_vector_store.py +311 -0
- powermem/utils/__init__.py +35 -0
- powermem/utils/utils.py +605 -0
- powermem/version.py +23 -0
- powermem-0.1.0.dist-info/METADATA +187 -0
- powermem-0.1.0.dist-info/RECORD +123 -0
- powermem-0.1.0.dist-info/WHEEL +5 -0
- powermem-0.1.0.dist-info/licenses/LICENSE +206 -0
- powermem-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Intelligence manager
|
|
3
|
+
|
|
4
|
+
This module provides the main intelligence management interface.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from typing import Any, Dict, List, Optional
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
|
|
11
|
+
from .intelligent_memory_manager import IntelligentMemoryManager
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class IntelligenceManager:
|
|
17
|
+
"""
|
|
18
|
+
Main intelligence manager for memory processing.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
|
22
|
+
"""
|
|
23
|
+
Initialize intelligence manager.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
config: Configuration dictionary
|
|
27
|
+
"""
|
|
28
|
+
self.config = config or {}
|
|
29
|
+
# Check if intelligent memory is enabled
|
|
30
|
+
intelligent_config = self.config.get("intelligent_memory", {})
|
|
31
|
+
self.enabled = intelligent_config.get("enabled", False) # Default to False for backward compatibility
|
|
32
|
+
|
|
33
|
+
if self.enabled:
|
|
34
|
+
self.intelligent_memory_manager = IntelligentMemoryManager(self.config)
|
|
35
|
+
else:
|
|
36
|
+
self.intelligent_memory_manager = None
|
|
37
|
+
|
|
38
|
+
logger.info(f"IntelligenceManager initialized (enabled: {self.enabled})")
|
|
39
|
+
|
|
40
|
+
def process_metadata(
|
|
41
|
+
self,
|
|
42
|
+
content: str,
|
|
43
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
44
|
+
context: Optional[Dict[str, Any]] = None
|
|
45
|
+
) -> Dict[str, Any]:
|
|
46
|
+
"""
|
|
47
|
+
Process metadata with intelligence.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
content: Content to analyze
|
|
51
|
+
metadata: Additional metadata
|
|
52
|
+
context: Additional context
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Enhanced metadata with intelligence analysis
|
|
56
|
+
"""
|
|
57
|
+
if not self.enabled or not self.intelligent_memory_manager:
|
|
58
|
+
return metadata or {}
|
|
59
|
+
|
|
60
|
+
return self.intelligent_memory_manager.process_metadata(content, metadata, context)
|
|
61
|
+
|
|
62
|
+
async def process_metadata_async(
|
|
63
|
+
self,
|
|
64
|
+
content: str,
|
|
65
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
66
|
+
context: Optional[Dict[str, Any]] = None
|
|
67
|
+
) -> Dict[str, Any]:
|
|
68
|
+
"""
|
|
69
|
+
Process metadata with intelligence asynchronously.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
content: Content to analyze
|
|
73
|
+
metadata: Additional metadata
|
|
74
|
+
context: Additional context
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Enhanced metadata with intelligence analysis
|
|
78
|
+
"""
|
|
79
|
+
if not self.enabled or not self.intelligent_memory_manager:
|
|
80
|
+
return metadata or {}
|
|
81
|
+
|
|
82
|
+
return await self.intelligent_memory_manager.process_metadata_async(content, metadata, context)
|
|
83
|
+
|
|
84
|
+
def process_search_results(
|
|
85
|
+
self,
|
|
86
|
+
results: List[Dict[str, Any]],
|
|
87
|
+
query: str
|
|
88
|
+
) -> List[Dict[str, Any]]:
|
|
89
|
+
"""
|
|
90
|
+
Process search results with intelligence.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
results: Search results
|
|
94
|
+
query: Original query
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
Processed and ranked results
|
|
98
|
+
"""
|
|
99
|
+
if not self.enabled or not self.intelligent_memory_manager:
|
|
100
|
+
# Return original results if intelligence is disabled
|
|
101
|
+
return results
|
|
102
|
+
|
|
103
|
+
return self.intelligent_memory_manager.process_search_results(results, query)
|
|
104
|
+
|
|
105
|
+
async def process_search_results_async(
|
|
106
|
+
self,
|
|
107
|
+
results: List[Dict[str, Any]],
|
|
108
|
+
query: str
|
|
109
|
+
) -> List[Dict[str, Any]]:
|
|
110
|
+
"""
|
|
111
|
+
Process search results with intelligence asynchronously.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
results: Search results
|
|
115
|
+
query: Original query
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
Processed and ranked results
|
|
119
|
+
"""
|
|
120
|
+
if not self.enabled or not self.intelligent_memory_manager:
|
|
121
|
+
# Return original results if intelligence is disabled
|
|
122
|
+
return results
|
|
123
|
+
|
|
124
|
+
return await self.intelligent_memory_manager.process_search_results_async(results, query)
|
|
125
|
+
|
|
126
|
+
def optimize_memories(self) -> Dict[str, Any]:
|
|
127
|
+
"""
|
|
128
|
+
Optimize memory storage.
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
Optimization results
|
|
132
|
+
"""
|
|
133
|
+
if not self.enabled or not self.intelligent_memory_manager:
|
|
134
|
+
return {"optimized": False, "reason": "intelligence disabled"}
|
|
135
|
+
|
|
136
|
+
return self.intelligent_memory_manager.optimize_memories()
|
|
137
|
+
|
|
138
|
+
def get_memory_stats(self) -> Dict[str, Any]:
|
|
139
|
+
"""
|
|
140
|
+
Get memory statistics.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
Memory statistics
|
|
144
|
+
"""
|
|
145
|
+
if not self.enabled or not self.intelligent_memory_manager:
|
|
146
|
+
return {"stats": "intelligence disabled"}
|
|
147
|
+
|
|
148
|
+
return self.intelligent_memory_manager.get_memory_stats()
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pluggable intelligent memory plugin interface and default implementation.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
10
|
+
|
|
11
|
+
from .importance_evaluator import ImportanceEvaluator
|
|
12
|
+
from .ebbinghaus_algorithm import EbbinghausAlgorithm
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class IntelligentMemoryPlugin:
|
|
19
|
+
"""
|
|
20
|
+
Interface for intelligent memory plugins.
|
|
21
|
+
Implementations can annotate new memories and manage lifecycle on access/search.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, config: Dict[str, Any]):
|
|
25
|
+
self.config = config or {}
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def enabled(self) -> bool: # pragma: no cover - trivial
|
|
29
|
+
return bool(self.config.get("enabled", False))
|
|
30
|
+
|
|
31
|
+
def on_add(self, *, content: str, metadata: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
32
|
+
"""
|
|
33
|
+
Hook invoked before persisting a memory. Return extra fields to merge into record.
|
|
34
|
+
"""
|
|
35
|
+
return {}
|
|
36
|
+
|
|
37
|
+
def on_get(self, memory: Dict[str, Any]) -> Tuple[Optional[Dict[str, Any]], bool]:
|
|
38
|
+
"""
|
|
39
|
+
Hook invoked on single memory access.
|
|
40
|
+
Returns (updates, delete_flag). If delete_flag is True, caller should delete memory.
|
|
41
|
+
"""
|
|
42
|
+
return None, False
|
|
43
|
+
|
|
44
|
+
def on_search(self, results: List[Dict[str, Any]]) -> Tuple[List[Tuple[str, Dict[str, Any]]], List[str]]:
|
|
45
|
+
"""
|
|
46
|
+
Hook invoked on batch search results.
|
|
47
|
+
Returns (updates, delete_ids).
|
|
48
|
+
updates: list of (memory_id, update_dict)
|
|
49
|
+
delete_ids: list of memory ids to delete
|
|
50
|
+
"""
|
|
51
|
+
return [], []
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class EbbinghausIntelligencePlugin(IntelligentMemoryPlugin):
|
|
55
|
+
"""
|
|
56
|
+
Default plugin implementing importance evaluation and lifecycle with Ebbinghaus.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def __init__(self, config: Dict[str, Any]):
|
|
60
|
+
super().__init__(config)
|
|
61
|
+
self._importance = None
|
|
62
|
+
self._algo = None
|
|
63
|
+
if self.enabled:
|
|
64
|
+
try:
|
|
65
|
+
# Prepare importance config, merging custom_importance_evaluation_prompt if present
|
|
66
|
+
importance_config = self.config.get("importance", {})
|
|
67
|
+
if "custom_importance_evaluation_prompt" in self.config:
|
|
68
|
+
importance_config = importance_config.copy() if isinstance(importance_config, dict) else {}
|
|
69
|
+
importance_config["custom_importance_evaluation_prompt"] = self.config["custom_importance_evaluation_prompt"]
|
|
70
|
+
|
|
71
|
+
self._importance = ImportanceEvaluator(
|
|
72
|
+
importance_config,
|
|
73
|
+
self.config.get("llm", {}),
|
|
74
|
+
)
|
|
75
|
+
# Support both "ebbinghaus" and direct intelligent_memory config
|
|
76
|
+
ebbinghaus_cfg = self.config.get("ebbinghaus", {})
|
|
77
|
+
# If no ebbinghaus config, use the main config as ebbinghaus config
|
|
78
|
+
if not ebbinghaus_cfg:
|
|
79
|
+
ebbinghaus_cfg = self.config
|
|
80
|
+
self._algo = EbbinghausAlgorithm(ebbinghaus_cfg)
|
|
81
|
+
except Exception as e: # pragma: no cover - defensive
|
|
82
|
+
logger.warning(f"Failed to init Ebbinghaus plugin: {e}")
|
|
83
|
+
self.config["enabled"] = False
|
|
84
|
+
|
|
85
|
+
def _classify(self, score: float) -> str:
|
|
86
|
+
if not self._algo:
|
|
87
|
+
return "working"
|
|
88
|
+
if score >= self._algo.long_term_threshold:
|
|
89
|
+
return "long_term"
|
|
90
|
+
if score >= self._algo.short_term_threshold:
|
|
91
|
+
return "short_term"
|
|
92
|
+
return "working"
|
|
93
|
+
|
|
94
|
+
def on_add(self, *, content: str, metadata: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
95
|
+
if not self.enabled or not self._importance or not self._algo:
|
|
96
|
+
return {}
|
|
97
|
+
try:
|
|
98
|
+
# Evaluate importance
|
|
99
|
+
score = self._importance.evaluate_importance(content, metadata)
|
|
100
|
+
|
|
101
|
+
# Classify memory type
|
|
102
|
+
memory_type = self._classify(score)
|
|
103
|
+
|
|
104
|
+
# Process with Ebbinghaus algorithm to get intelligence metadata
|
|
105
|
+
intelligence_metadata = self._algo.process_memory_metadata(content, score, memory_type)
|
|
106
|
+
|
|
107
|
+
# Return enhanced metadata
|
|
108
|
+
return {
|
|
109
|
+
"importance_score": score,
|
|
110
|
+
"memory_type": memory_type,
|
|
111
|
+
"access_count": 0,
|
|
112
|
+
"intelligence": intelligence_metadata.get('intelligence', {}),
|
|
113
|
+
"memory_management": intelligence_metadata.get('memory_management', {}),
|
|
114
|
+
"processing_applied": True,
|
|
115
|
+
}
|
|
116
|
+
except Exception as e:
|
|
117
|
+
logger.warning(f"Failed to process memory in on_add: {e}")
|
|
118
|
+
return {"access_count": 0}
|
|
119
|
+
|
|
120
|
+
def on_get(self, memory: Dict[str, Any]) -> Tuple[Optional[Dict[str, Any]], bool]:
|
|
121
|
+
if not self.enabled or not self._algo:
|
|
122
|
+
return None, False
|
|
123
|
+
try:
|
|
124
|
+
updates: Dict[str, Any] = {
|
|
125
|
+
"access_count": (memory.get("access_count") or 0) + 1,
|
|
126
|
+
"updated_at": datetime.utcnow(),
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
# Check if memory should be forgotten
|
|
130
|
+
if self._algo.should_forget(memory):
|
|
131
|
+
return None, True
|
|
132
|
+
|
|
133
|
+
# Check if memory should be promoted
|
|
134
|
+
if self._algo.should_promote(memory):
|
|
135
|
+
current = memory.get("memory_type")
|
|
136
|
+
if current == "working":
|
|
137
|
+
updates["memory_type"] = "short_term"
|
|
138
|
+
elif current == "short_term":
|
|
139
|
+
updates["memory_type"] = "long_term"
|
|
140
|
+
|
|
141
|
+
# Check if memory should be archived
|
|
142
|
+
if self._algo.should_archive(memory):
|
|
143
|
+
meta = memory.get("metadata") or {}
|
|
144
|
+
meta["archived"] = True
|
|
145
|
+
updates["metadata"] = meta
|
|
146
|
+
|
|
147
|
+
# Re-process content if memory type changed or if it's been accessed multiple times
|
|
148
|
+
access_count = memory.get("access_count", 0) + 1
|
|
149
|
+
if (updates.get("memory_type") != memory.get("memory_type") or
|
|
150
|
+
access_count % 5 == 0): # Re-process every 5 accesses
|
|
151
|
+
|
|
152
|
+
original_content = memory.get("original_content") or memory.get("content", "")
|
|
153
|
+
importance_score = memory.get("importance_score", 0.5)
|
|
154
|
+
memory_type = updates.get("memory_type") or memory.get("memory_type", "working")
|
|
155
|
+
|
|
156
|
+
# Re-process with updated parameters
|
|
157
|
+
intelligence_metadata = self._algo.process_memory_metadata(original_content, importance_score, memory_type)
|
|
158
|
+
updates.update(intelligence_metadata)
|
|
159
|
+
updates["last_reprocessed_at"] = datetime.utcnow()
|
|
160
|
+
|
|
161
|
+
return updates, False
|
|
162
|
+
except Exception as e:
|
|
163
|
+
logger.warning(f"Failed to process memory in on_get: {e}")
|
|
164
|
+
return None, False
|
|
165
|
+
|
|
166
|
+
def on_search(self, results: List[Dict[str, Any]]) -> Tuple[List[Tuple[str, Dict[str, Any]]], List[str]]:
|
|
167
|
+
if not self.enabled or not self._algo:
|
|
168
|
+
return [], []
|
|
169
|
+
updates: List[Tuple[str, Dict[str, Any]]] = []
|
|
170
|
+
deletes: List[str] = []
|
|
171
|
+
|
|
172
|
+
for item in results:
|
|
173
|
+
try:
|
|
174
|
+
mem_id = item.get("id") or item.get("memory_id")
|
|
175
|
+
if not mem_id:
|
|
176
|
+
continue
|
|
177
|
+
|
|
178
|
+
# Process individual memory
|
|
179
|
+
upd, delete_flag = self.on_get(item)
|
|
180
|
+
|
|
181
|
+
if delete_flag:
|
|
182
|
+
deletes.append(mem_id)
|
|
183
|
+
elif upd:
|
|
184
|
+
# Add search-specific enhancements
|
|
185
|
+
search_updates = self._enhance_for_search(item, upd)
|
|
186
|
+
updates.append((mem_id, search_updates))
|
|
187
|
+
|
|
188
|
+
except Exception as e:
|
|
189
|
+
logger.warning(f"Failed to process memory {mem_id} in on_search: {e}")
|
|
190
|
+
continue
|
|
191
|
+
|
|
192
|
+
return updates, deletes
|
|
193
|
+
|
|
194
|
+
def _enhance_for_search(self, memory: Dict[str, Any], base_updates: Dict[str, Any]) -> Dict[str, Any]:
|
|
195
|
+
"""
|
|
196
|
+
Enhance memory for search context.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
memory: Memory data
|
|
200
|
+
base_updates: Base updates from on_get
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
Enhanced updates for search context
|
|
204
|
+
"""
|
|
205
|
+
try:
|
|
206
|
+
# Add search-specific metadata
|
|
207
|
+
search_metadata = memory.get("metadata", {})
|
|
208
|
+
search_metadata["last_searched_at"] = datetime.utcnow()
|
|
209
|
+
search_metadata["search_count"] = search_metadata.get("search_count", 0) + 1
|
|
210
|
+
|
|
211
|
+
# Update base updates with search metadata
|
|
212
|
+
enhanced_updates = base_updates.copy()
|
|
213
|
+
enhanced_updates["metadata"] = search_metadata
|
|
214
|
+
|
|
215
|
+
# Add search relevance score if not present
|
|
216
|
+
if "search_relevance_score" not in memory:
|
|
217
|
+
# Simple relevance calculation based on access patterns
|
|
218
|
+
access_count = memory.get("access_count", 0)
|
|
219
|
+
importance_score = memory.get("importance_score", 0.5)
|
|
220
|
+
search_relevance = min(1.0, (access_count * 0.1) + (importance_score * 0.5))
|
|
221
|
+
enhanced_updates["search_relevance_score"] = search_relevance
|
|
222
|
+
|
|
223
|
+
return enhanced_updates
|
|
224
|
+
|
|
225
|
+
except Exception as e:
|
|
226
|
+
logger.warning(f"Failed to enhance memory for search: {e}")
|
|
227
|
+
return base_updates
|
|
228
|
+
|
|
229
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Prompt templates for memory operations
|
|
3
|
+
|
|
4
|
+
This module provides prompt templates for different memory operations.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .templates import PromptTemplates
|
|
8
|
+
from .graph.graph_prompts import GraphPrompts
|
|
9
|
+
from .graph.graph_tools_prompts import GraphToolsPrompts
|
|
10
|
+
from .intelligent_memory_prompts import (
|
|
11
|
+
FACT_RETRIEVAL_PROMPT,
|
|
12
|
+
FACT_EXTRACTION_PROMPT,
|
|
13
|
+
DEFAULT_UPDATE_MEMORY_PROMPT,
|
|
14
|
+
MEMORY_UPDATE_PROMPT,
|
|
15
|
+
get_memory_update_prompt,
|
|
16
|
+
parse_messages_for_facts
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"PromptTemplates",
|
|
21
|
+
"GraphPrompts",
|
|
22
|
+
"GraphToolsPrompts",
|
|
23
|
+
"FACT_RETRIEVAL_PROMPT",
|
|
24
|
+
"FACT_EXTRACTION_PROMPT",
|
|
25
|
+
"DEFAULT_UPDATE_MEMORY_PROMPT",
|
|
26
|
+
"MEMORY_UPDATE_PROMPT",
|
|
27
|
+
"get_memory_update_prompt",
|
|
28
|
+
"parse_messages_for_facts",
|
|
29
|
+
]
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Graph prompts for memory operations
|
|
3
|
+
|
|
4
|
+
This module provides prompts for graph-based memory operations.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from typing import Dict, Any, Optional
|
|
9
|
+
from ..templates import PromptTemplates
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
# Graph prompts constants
|
|
14
|
+
UPDATE_GRAPH_PROMPT = """
|
|
15
|
+
You are an AI expert specializing in graph memory management and optimization. Your task is to analyze existing graph memories alongside new information, and update the relationships in the memory list to ensure the most accurate, current, and coherent representation of knowledge.
|
|
16
|
+
|
|
17
|
+
Input:
|
|
18
|
+
1. Existing Graph Memories: A list of current graph memories, each containing source, target, and relationship information.
|
|
19
|
+
2. New Graph Memory: Fresh information to be integrated into the existing graph structure.
|
|
20
|
+
|
|
21
|
+
Guidelines:
|
|
22
|
+
1. Identification: Use the source and target as primary identifiers when matching existing memories with new information.
|
|
23
|
+
2. Conflict Resolution:
|
|
24
|
+
- If new information contradicts an existing memory:
|
|
25
|
+
a) For matching source and target but differing content, update the relationship of the existing memory.
|
|
26
|
+
b) If the new memory provides more recent or accurate information, update the existing memory accordingly.
|
|
27
|
+
3. Comprehensive Review: Thoroughly examine each existing graph memory against the new information, updating relationships as necessary. Multiple updates may be required.
|
|
28
|
+
4. Consistency: Maintain a uniform and clear style across all memories. Each entry should be concise yet comprehensive.
|
|
29
|
+
5. Semantic Coherence: Ensure that updates maintain or improve the overall semantic structure of the graph.
|
|
30
|
+
6. Temporal Awareness: If timestamps are available, consider the recency of information when making updates.
|
|
31
|
+
7. Relationship Refinement: Look for opportunities to refine relationship descriptions for greater precision or clarity.
|
|
32
|
+
8. Redundancy Elimination: Identify and merge any redundant or highly similar relationships that may result from the update.
|
|
33
|
+
|
|
34
|
+
Memory Format:
|
|
35
|
+
source -- RELATIONSHIP -- destination
|
|
36
|
+
|
|
37
|
+
Task Details:
|
|
38
|
+
======= Existing Graph Memories:=======
|
|
39
|
+
{existing_memories}
|
|
40
|
+
|
|
41
|
+
======= New Graph Memory:=======
|
|
42
|
+
{new_memories}
|
|
43
|
+
|
|
44
|
+
Output:
|
|
45
|
+
Provide a list of update instructions, each specifying the source, target, and the new relationship to be set. Only include memories that require updates.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
EXTRACT_RELATIONS_PROMPT = """
|
|
49
|
+
|
|
50
|
+
You are an advanced algorithm designed to extract structured information from text to construct knowledge graphs. Your goal is to capture comprehensive and accurate information. Follow these key principles:
|
|
51
|
+
|
|
52
|
+
1. Extract only explicitly stated information from the text.
|
|
53
|
+
2. Establish relationships among the entities provided.
|
|
54
|
+
3. Use "USER_ID" as the source entity for any self-references (e.g., "I," "me," "my," etc.) in user messages.
|
|
55
|
+
CUSTOM_PROMPT
|
|
56
|
+
|
|
57
|
+
Relationships:
|
|
58
|
+
- Use consistent, general, and timeless relationship types.
|
|
59
|
+
- Example: Prefer "professor" over "became_professor."
|
|
60
|
+
- Relationships should only be established among the entities explicitly mentioned in the user message.
|
|
61
|
+
|
|
62
|
+
Entity Consistency:
|
|
63
|
+
- Ensure that relationships are coherent and logically align with the context of the message.
|
|
64
|
+
- Maintain consistent naming for entities across the extracted data.
|
|
65
|
+
|
|
66
|
+
Strive to construct a coherent and easily understandable knowledge graph by eshtablishing all the relationships among the entities and adherence to the user's context.
|
|
67
|
+
|
|
68
|
+
Adhere strictly to these guidelines to ensure high-quality knowledge graph extraction."""
|
|
69
|
+
|
|
70
|
+
DELETE_RELATIONS_SYSTEM_PROMPT = """
|
|
71
|
+
You are a graph memory manager specializing in identifying, managing, and optimizing relationships within graph-based memories. Your primary task is to analyze a list of existing relationships and determine which ones should be deleted based on the new information provided.
|
|
72
|
+
Input:
|
|
73
|
+
1. Existing Graph Memories: A list of current graph memories, each containing source, relationship, and destination information.
|
|
74
|
+
2. New Text: The new information to be integrated into the existing graph structure.
|
|
75
|
+
3. Use "USER_ID" as node for any self-references (e.g., "I," "me," "my," etc.) in user messages.
|
|
76
|
+
|
|
77
|
+
Guidelines:
|
|
78
|
+
1. Identification: Use the new information to evaluate existing relationships in the memory graph.
|
|
79
|
+
2. Deletion Criteria: Delete a relationship only if it meets at least one of these conditions:
|
|
80
|
+
- Outdated or Inaccurate: The new information is more recent or accurate.
|
|
81
|
+
- Contradictory: The new information conflicts with or negates the existing information.
|
|
82
|
+
3. DO NOT DELETE if their is a possibility of same type of relationship but different destination nodes.
|
|
83
|
+
4. Comprehensive Analysis:
|
|
84
|
+
- Thoroughly examine each existing relationship against the new information and delete as necessary.
|
|
85
|
+
- Multiple deletions may be required based on the new information.
|
|
86
|
+
5. Semantic Integrity:
|
|
87
|
+
- Ensure that deletions maintain or improve the overall semantic structure of the graph.
|
|
88
|
+
- Avoid deleting relationships that are NOT contradictory/outdated to the new information.
|
|
89
|
+
6. Temporal Awareness: Prioritize recency when timestamps are available.
|
|
90
|
+
7. Necessity Principle: Only DELETE relationships that must be deleted and are contradictory/outdated to the new information to maintain an accurate and coherent memory graph.
|
|
91
|
+
|
|
92
|
+
Note: DO NOT DELETE if their is a possibility of same type of relationship but different destination nodes.
|
|
93
|
+
|
|
94
|
+
For example:
|
|
95
|
+
Existing Memory: alice -- loves_to_eat -- pizza
|
|
96
|
+
New Information: Alice also loves to eat burger.
|
|
97
|
+
|
|
98
|
+
Do not delete in the above example because there is a possibility that Alice loves to eat both pizza and burger.
|
|
99
|
+
|
|
100
|
+
Memory Format:
|
|
101
|
+
source -- relationship -- destination
|
|
102
|
+
|
|
103
|
+
Provide a list of deletion instructions, each specifying the relationship to be deleted.
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
class GraphPrompts(PromptTemplates):
|
|
107
|
+
"""
|
|
108
|
+
Prompts for graph-based memory operations.
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
|
112
|
+
"""
|
|
113
|
+
Initialize graph prompts.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
config: Configuration dictionary
|
|
117
|
+
"""
|
|
118
|
+
super().__init__(config)
|
|
119
|
+
# Store custom prompts from config if provided
|
|
120
|
+
self._custom_update_graph_prompt = None
|
|
121
|
+
self._custom_extract_relations_prompt = None
|
|
122
|
+
self._custom_delete_relations_prompt = None
|
|
123
|
+
|
|
124
|
+
if config:
|
|
125
|
+
# Support both graph_store config structure and direct config
|
|
126
|
+
graph_store_config = config.get("graph_store", {})
|
|
127
|
+
if isinstance(graph_store_config, dict):
|
|
128
|
+
self._custom_update_graph_prompt = graph_store_config.get("custom_update_graph_prompt")
|
|
129
|
+
self._custom_extract_relations_prompt = graph_store_config.get("custom_extract_relations_prompt") or graph_store_config.get("custom_prompt")
|
|
130
|
+
self._custom_delete_relations_prompt = graph_store_config.get("custom_delete_relations_prompt")
|
|
131
|
+
# Also support direct config keys
|
|
132
|
+
if not self._custom_update_graph_prompt:
|
|
133
|
+
self._custom_update_graph_prompt = config.get("custom_update_graph_prompt")
|
|
134
|
+
if not self._custom_extract_relations_prompt:
|
|
135
|
+
self._custom_extract_relations_prompt = config.get("custom_extract_relations_prompt") or config.get("custom_prompt")
|
|
136
|
+
if not self._custom_delete_relations_prompt:
|
|
137
|
+
self._custom_delete_relations_prompt = config.get("custom_delete_relations_prompt")
|
|
138
|
+
|
|
139
|
+
def get_update_graph_prompt(self, existing_memories: str, new_memories: str) -> str:
|
|
140
|
+
"""
|
|
141
|
+
Get graph update prompt.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
existing_memories: Existing graph memories
|
|
145
|
+
new_memories: New graph memories to integrate
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
Formatted prompt
|
|
149
|
+
"""
|
|
150
|
+
# Use custom prompt if provided, otherwise use default
|
|
151
|
+
prompt_template = self._custom_update_graph_prompt or UPDATE_GRAPH_PROMPT
|
|
152
|
+
try:
|
|
153
|
+
return prompt_template.format(
|
|
154
|
+
existing_memories=existing_memories,
|
|
155
|
+
new_memories=new_memories
|
|
156
|
+
)
|
|
157
|
+
except KeyError:
|
|
158
|
+
# If custom prompt doesn't have format placeholders, return as-is or append
|
|
159
|
+
if "{existing_memories}" not in prompt_template or "{new_memories}" not in prompt_template:
|
|
160
|
+
logger.warning("Custom update graph prompt missing format placeholders, appending data")
|
|
161
|
+
return f"{prompt_template}\n\nExisting Memories:\n{existing_memories}\n\nNew Memories:\n{new_memories}"
|
|
162
|
+
return prompt_template.format(
|
|
163
|
+
existing_memories=existing_memories,
|
|
164
|
+
new_memories=new_memories
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
def get_extract_relations_prompt(self, text: str) -> str:
|
|
168
|
+
"""
|
|
169
|
+
Get relations extraction prompt.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
text: Text to extract relations from
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
Formatted prompt
|
|
176
|
+
"""
|
|
177
|
+
# Use custom prompt if provided, otherwise use default
|
|
178
|
+
prompt = self._custom_extract_relations_prompt or EXTRACT_RELATIONS_PROMPT
|
|
179
|
+
return prompt.replace("USER_ID", "USER_ID")
|
|
180
|
+
|
|
181
|
+
def get_delete_relations_prompt(self, existing_memories: str, new_text: str, user_id: str = "USER_ID") -> tuple[str, str]:
|
|
182
|
+
"""
|
|
183
|
+
Get delete relations prompt.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
existing_memories: Existing graph memories
|
|
187
|
+
new_text: New text information
|
|
188
|
+
user_id: User ID for self-references
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
Tuple of (system_prompt, user_prompt)
|
|
192
|
+
"""
|
|
193
|
+
# Use custom prompt if provided, otherwise use default
|
|
194
|
+
system_prompt = self._custom_delete_relations_prompt or DELETE_RELATIONS_SYSTEM_PROMPT
|
|
195
|
+
system_prompt = system_prompt.replace("USER_ID", user_id)
|
|
196
|
+
user_prompt = f"Here are the existing memories: {existing_memories} \n\n New Information: {new_text}"
|
|
197
|
+
|
|
198
|
+
return system_prompt, user_prompt
|
|
199
|
+
|
|
200
|
+
def get_system_prompt(self, prompt_type: str = "extract_relations") -> str:
|
|
201
|
+
"""
|
|
202
|
+
Get system prompt for graph operations.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
prompt_type: Type of system prompt (extract_relations, delete_relations)
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
System prompt
|
|
209
|
+
"""
|
|
210
|
+
if prompt_type == "extract_relations":
|
|
211
|
+
# Use custom prompt if provided, otherwise use default
|
|
212
|
+
return self._custom_extract_relations_prompt or EXTRACT_RELATIONS_PROMPT
|
|
213
|
+
elif prompt_type == "delete_relations":
|
|
214
|
+
# Use custom prompt if provided, otherwise use default
|
|
215
|
+
return self._custom_delete_relations_prompt or DELETE_RELATIONS_SYSTEM_PROMPT
|
|
216
|
+
else:
|
|
217
|
+
return self._custom_extract_relations_prompt or EXTRACT_RELATIONS_PROMPT
|