noesium 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.
- noesium/core/__init__.py +4 -0
- noesium/core/agent/__init__.py +14 -0
- noesium/core/agent/base.py +227 -0
- noesium/core/consts.py +6 -0
- noesium/core/goalith/conflict/conflict.py +104 -0
- noesium/core/goalith/conflict/detector.py +53 -0
- noesium/core/goalith/decomposer/__init__.py +6 -0
- noesium/core/goalith/decomposer/base.py +46 -0
- noesium/core/goalith/decomposer/callable_decomposer.py +65 -0
- noesium/core/goalith/decomposer/llm_decomposer.py +326 -0
- noesium/core/goalith/decomposer/prompts.py +140 -0
- noesium/core/goalith/decomposer/simple_decomposer.py +61 -0
- noesium/core/goalith/errors.py +22 -0
- noesium/core/goalith/goalgraph/graph.py +526 -0
- noesium/core/goalith/goalgraph/node.py +179 -0
- noesium/core/goalith/replanner/base.py +31 -0
- noesium/core/goalith/replanner/replanner.py +36 -0
- noesium/core/goalith/service.py +26 -0
- noesium/core/llm/__init__.py +154 -0
- noesium/core/llm/base.py +152 -0
- noesium/core/llm/litellm.py +528 -0
- noesium/core/llm/llamacpp.py +487 -0
- noesium/core/llm/message.py +184 -0
- noesium/core/llm/ollama.py +459 -0
- noesium/core/llm/openai.py +520 -0
- noesium/core/llm/openrouter.py +89 -0
- noesium/core/llm/prompt.py +551 -0
- noesium/core/memory/__init__.py +11 -0
- noesium/core/memory/base.py +464 -0
- noesium/core/memory/memu/__init__.py +24 -0
- noesium/core/memory/memu/config/__init__.py +26 -0
- noesium/core/memory/memu/config/activity/config.py +46 -0
- noesium/core/memory/memu/config/event/config.py +46 -0
- noesium/core/memory/memu/config/markdown_config.py +241 -0
- noesium/core/memory/memu/config/profile/config.py +48 -0
- noesium/core/memory/memu/llm_adapter.py +129 -0
- noesium/core/memory/memu/memory/__init__.py +31 -0
- noesium/core/memory/memu/memory/actions/__init__.py +40 -0
- noesium/core/memory/memu/memory/actions/add_activity_memory.py +299 -0
- noesium/core/memory/memu/memory/actions/base_action.py +342 -0
- noesium/core/memory/memu/memory/actions/cluster_memories.py +262 -0
- noesium/core/memory/memu/memory/actions/generate_suggestions.py +198 -0
- noesium/core/memory/memu/memory/actions/get_available_categories.py +66 -0
- noesium/core/memory/memu/memory/actions/link_related_memories.py +515 -0
- noesium/core/memory/memu/memory/actions/run_theory_of_mind.py +254 -0
- noesium/core/memory/memu/memory/actions/update_memory_with_suggestions.py +514 -0
- noesium/core/memory/memu/memory/embeddings.py +130 -0
- noesium/core/memory/memu/memory/file_manager.py +306 -0
- noesium/core/memory/memu/memory/memory_agent.py +578 -0
- noesium/core/memory/memu/memory/recall_agent.py +376 -0
- noesium/core/memory/memu/memory_store.py +628 -0
- noesium/core/memory/models.py +149 -0
- noesium/core/msgbus/__init__.py +12 -0
- noesium/core/msgbus/base.py +395 -0
- noesium/core/orchestrix/__init__.py +0 -0
- noesium/core/py.typed +0 -0
- noesium/core/routing/__init__.py +20 -0
- noesium/core/routing/base.py +66 -0
- noesium/core/routing/router.py +241 -0
- noesium/core/routing/strategies/__init__.py +9 -0
- noesium/core/routing/strategies/dynamic_complexity.py +361 -0
- noesium/core/routing/strategies/self_assessment.py +147 -0
- noesium/core/routing/types.py +38 -0
- noesium/core/toolify/__init__.py +39 -0
- noesium/core/toolify/base.py +360 -0
- noesium/core/toolify/config.py +138 -0
- noesium/core/toolify/mcp_integration.py +275 -0
- noesium/core/toolify/registry.py +214 -0
- noesium/core/toolify/toolkits/__init__.py +1 -0
- noesium/core/tracing/__init__.py +37 -0
- noesium/core/tracing/langgraph_hooks.py +308 -0
- noesium/core/tracing/opik_tracing.py +144 -0
- noesium/core/tracing/token_tracker.py +166 -0
- noesium/core/utils/__init__.py +10 -0
- noesium/core/utils/logging.py +172 -0
- noesium/core/utils/statistics.py +12 -0
- noesium/core/utils/typing.py +17 -0
- noesium/core/vector_store/__init__.py +79 -0
- noesium/core/vector_store/base.py +94 -0
- noesium/core/vector_store/pgvector.py +304 -0
- noesium/core/vector_store/weaviate.py +383 -0
- noesium-0.1.0.dist-info/METADATA +525 -0
- noesium-0.1.0.dist-info/RECORD +86 -0
- noesium-0.1.0.dist-info/WHEEL +5 -0
- noesium-0.1.0.dist-info/licenses/LICENSE +21 -0
- noesium-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"""
|
|
2
|
+
File-based Memory Management for MemU
|
|
3
|
+
|
|
4
|
+
Provides file operations for storing and retrieving character memory data
|
|
5
|
+
in markdown format.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any, Dict, List, Literal, Set
|
|
12
|
+
|
|
13
|
+
from ..config.markdown_config import get_config_manager
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MemoryFileManager:
|
|
19
|
+
"""
|
|
20
|
+
File-based memory manager for character profiles and memories.
|
|
21
|
+
|
|
22
|
+
Manages memory storage in markdown files:
|
|
23
|
+
- profile.md: Character profile information
|
|
24
|
+
- events.md: Character event records
|
|
25
|
+
- xxx.md: Other memory files
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
# Default file extension for all categories
|
|
29
|
+
DEFAULT_EXTENSION = ".md"
|
|
30
|
+
|
|
31
|
+
def __init__(self, memory_dir: str = "memu/server/memory", agent_id: str = None, user_id: str = None):
|
|
32
|
+
"""
|
|
33
|
+
Initialize File Manager
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
memory_dir: Directory to store memory files
|
|
37
|
+
"""
|
|
38
|
+
self.memory_dir = Path(memory_dir)
|
|
39
|
+
self.memory_dir.mkdir(parents=True, exist_ok=True)
|
|
40
|
+
self.embeddings_dir = self.memory_dir / "embeddings"
|
|
41
|
+
self.embeddings_dir.mkdir(parents=True, exist_ok=True)
|
|
42
|
+
|
|
43
|
+
# Context for current agent/user (optional)
|
|
44
|
+
self.agent_id = agent_id
|
|
45
|
+
self.user_id = user_id
|
|
46
|
+
|
|
47
|
+
self.config_manager = get_config_manager()
|
|
48
|
+
|
|
49
|
+
# Maintain memory types by group
|
|
50
|
+
self.memory_types: Dict[str, Dict[str, str]] = {"basic": {}, "cluster": {}}
|
|
51
|
+
self._initialize_memory_types()
|
|
52
|
+
|
|
53
|
+
logger.info(f"MemoryFileManager initialized with directory: {self.memory_dir}")
|
|
54
|
+
|
|
55
|
+
def _initialize_memory_types(self) -> None:
|
|
56
|
+
"""Load basic categories from MarkdownConfigManager into memory_types['basic']."""
|
|
57
|
+
basic_map = self.config_manager.get_file_types_mapping()
|
|
58
|
+
# Ensure a copy to avoid accidental external mutation
|
|
59
|
+
self.memory_types["basic"] = dict(basic_map)
|
|
60
|
+
cluster_set = self._get_cluster_categories(self.agent_id, self.user_id, set(self.memory_types["basic"].keys()))
|
|
61
|
+
self.memory_types["cluster"] = cluster_set
|
|
62
|
+
# Do not touch cluster here; it's context dependent
|
|
63
|
+
|
|
64
|
+
# set_context removed: context is now provided at initialization time
|
|
65
|
+
|
|
66
|
+
def get_flat_memory_types(self) -> Dict[str, str]:
|
|
67
|
+
"""Return a flattened mapping of all categories (basic + cluster) to filenames."""
|
|
68
|
+
flat: Dict[str, str] = {}
|
|
69
|
+
flat.update(self.memory_types.get("basic", {}))
|
|
70
|
+
flat.update(self.memory_types.get("cluster", {}))
|
|
71
|
+
return flat
|
|
72
|
+
|
|
73
|
+
def _get_memory_file_path(self, agent_id: str, user_id: str, category: str) -> Path:
|
|
74
|
+
"""
|
|
75
|
+
Get the file path for a memory file with agent_id/user_id structure
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
agent_id: Agent identifier
|
|
79
|
+
user_id: User identifier
|
|
80
|
+
category: Memory category
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
Path: Full path to the memory file
|
|
84
|
+
"""
|
|
85
|
+
if category in self.memory_types["basic"]:
|
|
86
|
+
filename = self.memory_types["basic"][category]
|
|
87
|
+
elif category in self.memory_types["cluster"]:
|
|
88
|
+
filename = self.memory_types["cluster"][category]
|
|
89
|
+
else:
|
|
90
|
+
filename = f"{category.replace(' ', '_')}{self.DEFAULT_EXTENSION}"
|
|
91
|
+
return self.memory_dir / agent_id / user_id / filename
|
|
92
|
+
|
|
93
|
+
def read_memory_file(self, category: str) -> str:
|
|
94
|
+
"""
|
|
95
|
+
Read content from a memory file using agent_id/user_id structure
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
agent_id: Agent identifier
|
|
99
|
+
user_id: User identifier
|
|
100
|
+
category: Category to read
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
str: File content or empty string if not found
|
|
104
|
+
"""
|
|
105
|
+
try:
|
|
106
|
+
file_path = self._get_memory_file_path(self.agent_id, self.user_id, category)
|
|
107
|
+
if file_path.exists():
|
|
108
|
+
return file_path.read_text(encoding="utf-8")
|
|
109
|
+
return ""
|
|
110
|
+
except Exception as e:
|
|
111
|
+
logger.error(f"Error reading {category} for agent {self.agent_id}, user {self.user_id}: {e}")
|
|
112
|
+
return ""
|
|
113
|
+
|
|
114
|
+
def write_memory_file(self, category: str, content: str) -> bool:
|
|
115
|
+
"""
|
|
116
|
+
Write content to a memory file using agent_id/user_id structure
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
agent_id: Agent identifier
|
|
120
|
+
user_id: User identifier
|
|
121
|
+
category: Category to write
|
|
122
|
+
content: Content to write
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
bool: True if successful, False otherwise
|
|
126
|
+
"""
|
|
127
|
+
try:
|
|
128
|
+
file_path = self._get_memory_file_path(self.agent_id, self.user_id, category)
|
|
129
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
130
|
+
file_path.write_text(content, encoding="utf-8")
|
|
131
|
+
logger.debug(f"Written {category} for agent {self.agent_id}, user {self.user_id}")
|
|
132
|
+
return True
|
|
133
|
+
except Exception as e:
|
|
134
|
+
logger.error(f"Error writing {category} for agent {self.agent_id}, user {self.user_id}: {e}")
|
|
135
|
+
return False
|
|
136
|
+
|
|
137
|
+
def append_memory_file(self, category: str, content: str) -> bool:
|
|
138
|
+
"""
|
|
139
|
+
Append content to a memory file using agent_id/user_id structure
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
agent_id: Agent identifier
|
|
143
|
+
user_id: User identifier
|
|
144
|
+
category: Category to append to
|
|
145
|
+
content: Content to append
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
bool: True if successful, False otherwise
|
|
149
|
+
"""
|
|
150
|
+
try:
|
|
151
|
+
existing_content = self.read_memory_file(category)
|
|
152
|
+
if existing_content:
|
|
153
|
+
new_content = existing_content + "\n" + content
|
|
154
|
+
else:
|
|
155
|
+
new_content = content
|
|
156
|
+
return self.write_memory_file(category, new_content)
|
|
157
|
+
except Exception as e:
|
|
158
|
+
logger.error(f"Error appending {category} for agent {self.agent_id}, user {self.user_id}: {e}")
|
|
159
|
+
return False
|
|
160
|
+
|
|
161
|
+
def delete_memory_file(self, category: str) -> bool:
|
|
162
|
+
"""
|
|
163
|
+
Delete a memory file using agent_id/user_id structure
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
agent_id: Agent identifier
|
|
167
|
+
user_id: User identifier
|
|
168
|
+
category: Category to delete
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
bool: True if successful, False otherwise
|
|
172
|
+
"""
|
|
173
|
+
try:
|
|
174
|
+
file_path = self._get_memory_file_path(self.agent_id, self.user_id, category)
|
|
175
|
+
if file_path.exists():
|
|
176
|
+
file_path.unlink()
|
|
177
|
+
logger.debug(f"Deleted {category} for agent {self.agent_id}, user {self.user_id}")
|
|
178
|
+
return True
|
|
179
|
+
except Exception as e:
|
|
180
|
+
logger.error(f"Error deleting {category} for agent {self.agent_id}, user {self.user_id}: {e}")
|
|
181
|
+
return False
|
|
182
|
+
|
|
183
|
+
def list_memory_files(
|
|
184
|
+
self,
|
|
185
|
+
category_group: Literal["basic", "cluster", "all"] = "basic",
|
|
186
|
+
) -> List[str]:
|
|
187
|
+
"""
|
|
188
|
+
List memory categories for an agent-user pair.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
agent_id: Agent identifier
|
|
192
|
+
user_id: User identifier
|
|
193
|
+
category_group: Which group to list: "basic", "cluster", or "all".
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
List[str]: List of category names
|
|
197
|
+
"""
|
|
198
|
+
basic_categories = self._get_basic_categories()
|
|
199
|
+
cluster_categories = self._get_cluster_categories(self.agent_id, self.user_id, basic_categories).keys()
|
|
200
|
+
|
|
201
|
+
if category_group == "basic":
|
|
202
|
+
# Return all basic categories from config (not filtered by existence)
|
|
203
|
+
return sorted(list(basic_categories))
|
|
204
|
+
if category_group == "cluster":
|
|
205
|
+
return sorted(list(cluster_categories))
|
|
206
|
+
# all
|
|
207
|
+
# all = all basic categories + cluster categories
|
|
208
|
+
return sorted(list(basic_categories.union(cluster_categories)))
|
|
209
|
+
|
|
210
|
+
def _get_basic_categories(self) -> Set[str]:
|
|
211
|
+
"""Get basic categories from MarkdownConfigManager."""
|
|
212
|
+
return set(self.config_manager.get_all_file_types())
|
|
213
|
+
|
|
214
|
+
def _get_cluster_categories(self, agent_id: str, user_id: str, basic_categories: Set[str]) -> Dict[str, str]:
|
|
215
|
+
"""Scan existing files and derive cluster categories not in basic."""
|
|
216
|
+
user_dir = self.memory_dir / agent_id / user_id
|
|
217
|
+
if not user_dir.exists():
|
|
218
|
+
return {}
|
|
219
|
+
cluster: Dict[str, str] = {}
|
|
220
|
+
for item in user_dir.glob(f"*{self.DEFAULT_EXTENSION}"):
|
|
221
|
+
if item.is_file():
|
|
222
|
+
category_name = item.stem
|
|
223
|
+
if category_name not in basic_categories:
|
|
224
|
+
cluster[category_name.replace("_", " ")] = f"{category_name}{self.DEFAULT_EXTENSION}"
|
|
225
|
+
return cluster
|
|
226
|
+
|
|
227
|
+
def create_cluster_category(self, category: str) -> bool:
|
|
228
|
+
"""
|
|
229
|
+
Create a new empty cluster category file if it does not exist.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
agent_id: Agent identifier
|
|
233
|
+
user_id: User identifier
|
|
234
|
+
category: New cluster category name
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
bool: True if created or already exists, False on error
|
|
238
|
+
"""
|
|
239
|
+
try:
|
|
240
|
+
_category = category.replace(" ", "_")
|
|
241
|
+
file_path = self._get_memory_file_path(self.agent_id, self.user_id, _category)
|
|
242
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
243
|
+
if not file_path.exists():
|
|
244
|
+
file_path.write_text("", encoding="utf-8")
|
|
245
|
+
# Update in-memory cluster categories mapping
|
|
246
|
+
self.memory_types.setdefault("cluster", {})[category] = f"{_category}{self.DEFAULT_EXTENSION}"
|
|
247
|
+
return True
|
|
248
|
+
except Exception as e:
|
|
249
|
+
logger.error(
|
|
250
|
+
f"Error creating cluster category {category} for agent {self.agent_id}, user {self.user_id}: {e}"
|
|
251
|
+
)
|
|
252
|
+
return False
|
|
253
|
+
|
|
254
|
+
def get_char_embeddings_dir(self) -> Path:
|
|
255
|
+
"""
|
|
256
|
+
Get the embeddings directory path for the current agent/user context
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
embeddings_base_dir: Base embeddings directory (usually memory_dir/embeddings)
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
Path: Full path to the character's embeddings directory
|
|
263
|
+
"""
|
|
264
|
+
if not self.agent_id or not self.user_id:
|
|
265
|
+
raise ValueError("agent_id and user_id must be set to get embeddings directory")
|
|
266
|
+
|
|
267
|
+
char_embeddings_dir = self.embeddings_dir / self.agent_id / self.user_id
|
|
268
|
+
char_embeddings_dir.mkdir(parents=True, exist_ok=True)
|
|
269
|
+
return char_embeddings_dir
|
|
270
|
+
|
|
271
|
+
def get_file_info(self, category: str) -> Dict[str, Any]:
|
|
272
|
+
"""
|
|
273
|
+
Get information about a memory file using agent_id/user_id structure
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
agent_id: Agent identifier
|
|
277
|
+
user_id: User identifier
|
|
278
|
+
category: Category name
|
|
279
|
+
|
|
280
|
+
Returns:
|
|
281
|
+
Dict containing file information
|
|
282
|
+
"""
|
|
283
|
+
file_path = self._get_memory_file_path(self.agent_id, self.user_id, category)
|
|
284
|
+
|
|
285
|
+
if file_path.exists():
|
|
286
|
+
stat = file_path.stat()
|
|
287
|
+
content = self.read_memory_file(category)
|
|
288
|
+
return {
|
|
289
|
+
"exists": True,
|
|
290
|
+
"file_size": stat.st_size,
|
|
291
|
+
"last_modified": datetime.fromtimestamp(stat.st_mtime).isoformat(),
|
|
292
|
+
"content_length": len(content),
|
|
293
|
+
"file_path": str(file_path),
|
|
294
|
+
"agent_id": self.agent_id,
|
|
295
|
+
"user_id": self.user_id,
|
|
296
|
+
}
|
|
297
|
+
else:
|
|
298
|
+
return {
|
|
299
|
+
"exists": False,
|
|
300
|
+
"file_size": 0,
|
|
301
|
+
"last_modified": None,
|
|
302
|
+
"content_length": 0,
|
|
303
|
+
"file_path": str(file_path),
|
|
304
|
+
"agent_id": self.agent_id,
|
|
305
|
+
"user_id": self.user_id,
|
|
306
|
+
}
|