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.
Files changed (86) hide show
  1. noesium/core/__init__.py +4 -0
  2. noesium/core/agent/__init__.py +14 -0
  3. noesium/core/agent/base.py +227 -0
  4. noesium/core/consts.py +6 -0
  5. noesium/core/goalith/conflict/conflict.py +104 -0
  6. noesium/core/goalith/conflict/detector.py +53 -0
  7. noesium/core/goalith/decomposer/__init__.py +6 -0
  8. noesium/core/goalith/decomposer/base.py +46 -0
  9. noesium/core/goalith/decomposer/callable_decomposer.py +65 -0
  10. noesium/core/goalith/decomposer/llm_decomposer.py +326 -0
  11. noesium/core/goalith/decomposer/prompts.py +140 -0
  12. noesium/core/goalith/decomposer/simple_decomposer.py +61 -0
  13. noesium/core/goalith/errors.py +22 -0
  14. noesium/core/goalith/goalgraph/graph.py +526 -0
  15. noesium/core/goalith/goalgraph/node.py +179 -0
  16. noesium/core/goalith/replanner/base.py +31 -0
  17. noesium/core/goalith/replanner/replanner.py +36 -0
  18. noesium/core/goalith/service.py +26 -0
  19. noesium/core/llm/__init__.py +154 -0
  20. noesium/core/llm/base.py +152 -0
  21. noesium/core/llm/litellm.py +528 -0
  22. noesium/core/llm/llamacpp.py +487 -0
  23. noesium/core/llm/message.py +184 -0
  24. noesium/core/llm/ollama.py +459 -0
  25. noesium/core/llm/openai.py +520 -0
  26. noesium/core/llm/openrouter.py +89 -0
  27. noesium/core/llm/prompt.py +551 -0
  28. noesium/core/memory/__init__.py +11 -0
  29. noesium/core/memory/base.py +464 -0
  30. noesium/core/memory/memu/__init__.py +24 -0
  31. noesium/core/memory/memu/config/__init__.py +26 -0
  32. noesium/core/memory/memu/config/activity/config.py +46 -0
  33. noesium/core/memory/memu/config/event/config.py +46 -0
  34. noesium/core/memory/memu/config/markdown_config.py +241 -0
  35. noesium/core/memory/memu/config/profile/config.py +48 -0
  36. noesium/core/memory/memu/llm_adapter.py +129 -0
  37. noesium/core/memory/memu/memory/__init__.py +31 -0
  38. noesium/core/memory/memu/memory/actions/__init__.py +40 -0
  39. noesium/core/memory/memu/memory/actions/add_activity_memory.py +299 -0
  40. noesium/core/memory/memu/memory/actions/base_action.py +342 -0
  41. noesium/core/memory/memu/memory/actions/cluster_memories.py +262 -0
  42. noesium/core/memory/memu/memory/actions/generate_suggestions.py +198 -0
  43. noesium/core/memory/memu/memory/actions/get_available_categories.py +66 -0
  44. noesium/core/memory/memu/memory/actions/link_related_memories.py +515 -0
  45. noesium/core/memory/memu/memory/actions/run_theory_of_mind.py +254 -0
  46. noesium/core/memory/memu/memory/actions/update_memory_with_suggestions.py +514 -0
  47. noesium/core/memory/memu/memory/embeddings.py +130 -0
  48. noesium/core/memory/memu/memory/file_manager.py +306 -0
  49. noesium/core/memory/memu/memory/memory_agent.py +578 -0
  50. noesium/core/memory/memu/memory/recall_agent.py +376 -0
  51. noesium/core/memory/memu/memory_store.py +628 -0
  52. noesium/core/memory/models.py +149 -0
  53. noesium/core/msgbus/__init__.py +12 -0
  54. noesium/core/msgbus/base.py +395 -0
  55. noesium/core/orchestrix/__init__.py +0 -0
  56. noesium/core/py.typed +0 -0
  57. noesium/core/routing/__init__.py +20 -0
  58. noesium/core/routing/base.py +66 -0
  59. noesium/core/routing/router.py +241 -0
  60. noesium/core/routing/strategies/__init__.py +9 -0
  61. noesium/core/routing/strategies/dynamic_complexity.py +361 -0
  62. noesium/core/routing/strategies/self_assessment.py +147 -0
  63. noesium/core/routing/types.py +38 -0
  64. noesium/core/toolify/__init__.py +39 -0
  65. noesium/core/toolify/base.py +360 -0
  66. noesium/core/toolify/config.py +138 -0
  67. noesium/core/toolify/mcp_integration.py +275 -0
  68. noesium/core/toolify/registry.py +214 -0
  69. noesium/core/toolify/toolkits/__init__.py +1 -0
  70. noesium/core/tracing/__init__.py +37 -0
  71. noesium/core/tracing/langgraph_hooks.py +308 -0
  72. noesium/core/tracing/opik_tracing.py +144 -0
  73. noesium/core/tracing/token_tracker.py +166 -0
  74. noesium/core/utils/__init__.py +10 -0
  75. noesium/core/utils/logging.py +172 -0
  76. noesium/core/utils/statistics.py +12 -0
  77. noesium/core/utils/typing.py +17 -0
  78. noesium/core/vector_store/__init__.py +79 -0
  79. noesium/core/vector_store/base.py +94 -0
  80. noesium/core/vector_store/pgvector.py +304 -0
  81. noesium/core/vector_store/weaviate.py +383 -0
  82. noesium-0.1.0.dist-info/METADATA +525 -0
  83. noesium-0.1.0.dist-info/RECORD +86 -0
  84. noesium-0.1.0.dist-info/WHEEL +5 -0
  85. noesium-0.1.0.dist-info/licenses/LICENSE +21 -0
  86. 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
+ }