kweaver-dolphin 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 (199) hide show
  1. DolphinLanguageSDK/__init__.py +58 -0
  2. dolphin/__init__.py +62 -0
  3. dolphin/cli/__init__.py +20 -0
  4. dolphin/cli/args/__init__.py +9 -0
  5. dolphin/cli/args/parser.py +567 -0
  6. dolphin/cli/builtin_agents/__init__.py +22 -0
  7. dolphin/cli/commands/__init__.py +4 -0
  8. dolphin/cli/interrupt/__init__.py +8 -0
  9. dolphin/cli/interrupt/handler.py +205 -0
  10. dolphin/cli/interrupt/keyboard.py +82 -0
  11. dolphin/cli/main.py +49 -0
  12. dolphin/cli/multimodal/__init__.py +34 -0
  13. dolphin/cli/multimodal/clipboard.py +327 -0
  14. dolphin/cli/multimodal/handler.py +249 -0
  15. dolphin/cli/multimodal/image_processor.py +214 -0
  16. dolphin/cli/multimodal/input_parser.py +149 -0
  17. dolphin/cli/runner/__init__.py +8 -0
  18. dolphin/cli/runner/runner.py +989 -0
  19. dolphin/cli/ui/__init__.py +10 -0
  20. dolphin/cli/ui/console.py +2795 -0
  21. dolphin/cli/ui/input.py +340 -0
  22. dolphin/cli/ui/layout.py +425 -0
  23. dolphin/cli/ui/stream_renderer.py +302 -0
  24. dolphin/cli/utils/__init__.py +8 -0
  25. dolphin/cli/utils/helpers.py +135 -0
  26. dolphin/cli/utils/version.py +49 -0
  27. dolphin/core/__init__.py +107 -0
  28. dolphin/core/agent/__init__.py +10 -0
  29. dolphin/core/agent/agent_state.py +69 -0
  30. dolphin/core/agent/base_agent.py +970 -0
  31. dolphin/core/code_block/__init__.py +0 -0
  32. dolphin/core/code_block/agent_init_block.py +0 -0
  33. dolphin/core/code_block/assign_block.py +98 -0
  34. dolphin/core/code_block/basic_code_block.py +1865 -0
  35. dolphin/core/code_block/explore_block.py +1327 -0
  36. dolphin/core/code_block/explore_block_v2.py +712 -0
  37. dolphin/core/code_block/explore_strategy.py +672 -0
  38. dolphin/core/code_block/judge_block.py +220 -0
  39. dolphin/core/code_block/prompt_block.py +32 -0
  40. dolphin/core/code_block/skill_call_deduplicator.py +291 -0
  41. dolphin/core/code_block/tool_block.py +129 -0
  42. dolphin/core/common/__init__.py +17 -0
  43. dolphin/core/common/constants.py +176 -0
  44. dolphin/core/common/enums.py +1173 -0
  45. dolphin/core/common/exceptions.py +133 -0
  46. dolphin/core/common/multimodal.py +539 -0
  47. dolphin/core/common/object_type.py +165 -0
  48. dolphin/core/common/output_format.py +432 -0
  49. dolphin/core/common/types.py +36 -0
  50. dolphin/core/config/__init__.py +16 -0
  51. dolphin/core/config/global_config.py +1289 -0
  52. dolphin/core/config/ontology_config.py +133 -0
  53. dolphin/core/context/__init__.py +12 -0
  54. dolphin/core/context/context.py +1580 -0
  55. dolphin/core/context/context_manager.py +161 -0
  56. dolphin/core/context/var_output.py +82 -0
  57. dolphin/core/context/variable_pool.py +356 -0
  58. dolphin/core/context_engineer/__init__.py +41 -0
  59. dolphin/core/context_engineer/config/__init__.py +5 -0
  60. dolphin/core/context_engineer/config/settings.py +402 -0
  61. dolphin/core/context_engineer/core/__init__.py +7 -0
  62. dolphin/core/context_engineer/core/budget_manager.py +327 -0
  63. dolphin/core/context_engineer/core/context_assembler.py +583 -0
  64. dolphin/core/context_engineer/core/context_manager.py +637 -0
  65. dolphin/core/context_engineer/core/tokenizer_service.py +260 -0
  66. dolphin/core/context_engineer/example/incremental_example.py +267 -0
  67. dolphin/core/context_engineer/example/traditional_example.py +334 -0
  68. dolphin/core/context_engineer/services/__init__.py +5 -0
  69. dolphin/core/context_engineer/services/compressor.py +399 -0
  70. dolphin/core/context_engineer/utils/__init__.py +6 -0
  71. dolphin/core/context_engineer/utils/context_utils.py +441 -0
  72. dolphin/core/context_engineer/utils/message_formatter.py +270 -0
  73. dolphin/core/context_engineer/utils/token_utils.py +139 -0
  74. dolphin/core/coroutine/__init__.py +15 -0
  75. dolphin/core/coroutine/context_snapshot.py +154 -0
  76. dolphin/core/coroutine/context_snapshot_profile.py +922 -0
  77. dolphin/core/coroutine/context_snapshot_store.py +268 -0
  78. dolphin/core/coroutine/execution_frame.py +145 -0
  79. dolphin/core/coroutine/execution_state_registry.py +161 -0
  80. dolphin/core/coroutine/resume_handle.py +101 -0
  81. dolphin/core/coroutine/step_result.py +101 -0
  82. dolphin/core/executor/__init__.py +18 -0
  83. dolphin/core/executor/debug_controller.py +630 -0
  84. dolphin/core/executor/dolphin_executor.py +1063 -0
  85. dolphin/core/executor/executor.py +624 -0
  86. dolphin/core/flags/__init__.py +27 -0
  87. dolphin/core/flags/definitions.py +49 -0
  88. dolphin/core/flags/manager.py +113 -0
  89. dolphin/core/hook/__init__.py +95 -0
  90. dolphin/core/hook/expression_evaluator.py +499 -0
  91. dolphin/core/hook/hook_dispatcher.py +380 -0
  92. dolphin/core/hook/hook_types.py +248 -0
  93. dolphin/core/hook/isolated_variable_pool.py +284 -0
  94. dolphin/core/interfaces.py +53 -0
  95. dolphin/core/llm/__init__.py +0 -0
  96. dolphin/core/llm/llm.py +495 -0
  97. dolphin/core/llm/llm_call.py +100 -0
  98. dolphin/core/llm/llm_client.py +1285 -0
  99. dolphin/core/llm/message_sanitizer.py +120 -0
  100. dolphin/core/logging/__init__.py +20 -0
  101. dolphin/core/logging/logger.py +526 -0
  102. dolphin/core/message/__init__.py +8 -0
  103. dolphin/core/message/compressor.py +749 -0
  104. dolphin/core/parser/__init__.py +8 -0
  105. dolphin/core/parser/parser.py +405 -0
  106. dolphin/core/runtime/__init__.py +10 -0
  107. dolphin/core/runtime/runtime_graph.py +926 -0
  108. dolphin/core/runtime/runtime_instance.py +446 -0
  109. dolphin/core/skill/__init__.py +14 -0
  110. dolphin/core/skill/context_retention.py +157 -0
  111. dolphin/core/skill/skill_function.py +686 -0
  112. dolphin/core/skill/skill_matcher.py +282 -0
  113. dolphin/core/skill/skillkit.py +700 -0
  114. dolphin/core/skill/skillset.py +72 -0
  115. dolphin/core/trajectory/__init__.py +10 -0
  116. dolphin/core/trajectory/recorder.py +189 -0
  117. dolphin/core/trajectory/trajectory.py +522 -0
  118. dolphin/core/utils/__init__.py +9 -0
  119. dolphin/core/utils/cache_kv.py +212 -0
  120. dolphin/core/utils/tools.py +340 -0
  121. dolphin/lib/__init__.py +93 -0
  122. dolphin/lib/debug/__init__.py +8 -0
  123. dolphin/lib/debug/visualizer.py +409 -0
  124. dolphin/lib/memory/__init__.py +28 -0
  125. dolphin/lib/memory/async_processor.py +220 -0
  126. dolphin/lib/memory/llm_calls.py +195 -0
  127. dolphin/lib/memory/manager.py +78 -0
  128. dolphin/lib/memory/sandbox.py +46 -0
  129. dolphin/lib/memory/storage.py +245 -0
  130. dolphin/lib/memory/utils.py +51 -0
  131. dolphin/lib/ontology/__init__.py +12 -0
  132. dolphin/lib/ontology/basic/__init__.py +0 -0
  133. dolphin/lib/ontology/basic/base.py +102 -0
  134. dolphin/lib/ontology/basic/concept.py +130 -0
  135. dolphin/lib/ontology/basic/object.py +11 -0
  136. dolphin/lib/ontology/basic/relation.py +63 -0
  137. dolphin/lib/ontology/datasource/__init__.py +27 -0
  138. dolphin/lib/ontology/datasource/datasource.py +66 -0
  139. dolphin/lib/ontology/datasource/oracle_datasource.py +338 -0
  140. dolphin/lib/ontology/datasource/sql.py +845 -0
  141. dolphin/lib/ontology/mapping.py +177 -0
  142. dolphin/lib/ontology/ontology.py +733 -0
  143. dolphin/lib/ontology/ontology_context.py +16 -0
  144. dolphin/lib/ontology/ontology_manager.py +107 -0
  145. dolphin/lib/skill_results/__init__.py +31 -0
  146. dolphin/lib/skill_results/cache_backend.py +559 -0
  147. dolphin/lib/skill_results/result_processor.py +181 -0
  148. dolphin/lib/skill_results/result_reference.py +179 -0
  149. dolphin/lib/skill_results/skillkit_hook.py +324 -0
  150. dolphin/lib/skill_results/strategies.py +328 -0
  151. dolphin/lib/skill_results/strategy_registry.py +150 -0
  152. dolphin/lib/skillkits/__init__.py +44 -0
  153. dolphin/lib/skillkits/agent_skillkit.py +155 -0
  154. dolphin/lib/skillkits/cognitive_skillkit.py +82 -0
  155. dolphin/lib/skillkits/env_skillkit.py +250 -0
  156. dolphin/lib/skillkits/mcp_adapter.py +616 -0
  157. dolphin/lib/skillkits/mcp_skillkit.py +771 -0
  158. dolphin/lib/skillkits/memory_skillkit.py +650 -0
  159. dolphin/lib/skillkits/noop_skillkit.py +31 -0
  160. dolphin/lib/skillkits/ontology_skillkit.py +89 -0
  161. dolphin/lib/skillkits/plan_act_skillkit.py +452 -0
  162. dolphin/lib/skillkits/resource/__init__.py +52 -0
  163. dolphin/lib/skillkits/resource/models/__init__.py +6 -0
  164. dolphin/lib/skillkits/resource/models/skill_config.py +109 -0
  165. dolphin/lib/skillkits/resource/models/skill_meta.py +127 -0
  166. dolphin/lib/skillkits/resource/resource_skillkit.py +393 -0
  167. dolphin/lib/skillkits/resource/skill_cache.py +215 -0
  168. dolphin/lib/skillkits/resource/skill_loader.py +395 -0
  169. dolphin/lib/skillkits/resource/skill_validator.py +406 -0
  170. dolphin/lib/skillkits/resource_skillkit.py +11 -0
  171. dolphin/lib/skillkits/search_skillkit.py +163 -0
  172. dolphin/lib/skillkits/sql_skillkit.py +274 -0
  173. dolphin/lib/skillkits/system_skillkit.py +509 -0
  174. dolphin/lib/skillkits/vm_skillkit.py +65 -0
  175. dolphin/lib/utils/__init__.py +9 -0
  176. dolphin/lib/utils/data_process.py +207 -0
  177. dolphin/lib/utils/handle_progress.py +178 -0
  178. dolphin/lib/utils/security.py +139 -0
  179. dolphin/lib/utils/text_retrieval.py +462 -0
  180. dolphin/lib/vm/__init__.py +11 -0
  181. dolphin/lib/vm/env_executor.py +895 -0
  182. dolphin/lib/vm/python_session_manager.py +453 -0
  183. dolphin/lib/vm/vm.py +610 -0
  184. dolphin/sdk/__init__.py +60 -0
  185. dolphin/sdk/agent/__init__.py +12 -0
  186. dolphin/sdk/agent/agent_factory.py +236 -0
  187. dolphin/sdk/agent/dolphin_agent.py +1106 -0
  188. dolphin/sdk/api/__init__.py +4 -0
  189. dolphin/sdk/runtime/__init__.py +8 -0
  190. dolphin/sdk/runtime/env.py +363 -0
  191. dolphin/sdk/skill/__init__.py +10 -0
  192. dolphin/sdk/skill/global_skills.py +706 -0
  193. dolphin/sdk/skill/traditional_toolkit.py +260 -0
  194. kweaver_dolphin-0.1.0.dist-info/METADATA +521 -0
  195. kweaver_dolphin-0.1.0.dist-info/RECORD +199 -0
  196. kweaver_dolphin-0.1.0.dist-info/WHEEL +5 -0
  197. kweaver_dolphin-0.1.0.dist-info/entry_points.txt +27 -0
  198. kweaver_dolphin-0.1.0.dist-info/licenses/LICENSE.txt +201 -0
  199. kweaver_dolphin-0.1.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,195 @@
1
+ """
2
+ LLM Call abstractions for the memory system.
3
+
4
+ This module provides abstract interfaces for LLM operations in the memory system,
5
+ including prompt assembly, post-processing, and formatting.
6
+ """
7
+
8
+ import json
9
+ from datetime import datetime
10
+ from typing import List, Any
11
+
12
+ from dolphin.core.llm.llm_call import LLMCall
13
+
14
+ from dolphin.core.common.enums import KnowledgePoint, MessageRole, Messages
15
+ from dolphin.core.logging.logger import get_logger
16
+ from dolphin.lib.memory.utils import validate_knowledge_point
17
+
18
+ logger = get_logger("mem")
19
+
20
+
21
+ class KnowledgeExtractionCall(LLMCall):
22
+ """LLM call for extracting knowledge from conversations."""
23
+
24
+ def execute(
25
+ self, conversation_history: Messages, user_id: str
26
+ ) -> List[KnowledgePoint]:
27
+ """Execute knowledge extraction for a specific conversation and user."""
28
+ if len(conversation_history.get_messages()) == 0:
29
+ return []
30
+
31
+ return super().execute(
32
+ llm_args={"knowledge_extraction": True},
33
+ conversation_history=conversation_history,
34
+ user_id=user_id,
35
+ )
36
+
37
+ def _log(self, time_cost: float, **kwargs) -> str:
38
+ """Log the execution result."""
39
+ logger.info(
40
+ f"Knowledge extraction execution user_id[{kwargs.get('user_id', 'unknown')}] time[{time_cost} seconds]"
41
+ )
42
+
43
+ def _build_prompt(self, conversation_history: Messages, **kwargs) -> str:
44
+ """Build prompt for knowledge extraction."""
45
+ conversation_text = ""
46
+ for msg in conversation_history.get_messages():
47
+ if msg.role == MessageRole.SYSTEM:
48
+ continue
49
+
50
+ conversation_text += f"{msg.role.value}: {msg.content}\n"
51
+
52
+ """You are a professional knowledge management expert. Your task is to analyze the following conversation and extract valuable knowledge. The knowledge should be categorized into one of the following three types:
53
+
54
+ 1. **WorldModel**: Facts about the external world, ontologies, data sources, or user-provided correction information.
55
+ 2. **ExperientialKnowledge**: Methodologies, strategies, or lessons learned from agent task execution.
56
+ 3. **OtherKnowledge**: Any other information worth remembering, such as user preferences or specific details.
57
+
58
+ For each piece of knowledge, provide a concise summary, its type, and a relevance score from 0 to 100 indicating its importance for future interactions.
59
+
60
+ Notes:
61
+ 1. Do not include information related to specific database data, as this information is volatile.
62
+ 2. Knowledge points should have complete predicates, not just single nouns.
63
+
64
+ Please output your findings strictly in JSONL format, with one JSON object per line. Each object should contain the fields: content, type, score, metadata. Do not include any other text or explanations.
65
+
66
+ Conversation history:
67
+ {conversation_text}
68
+ """
69
+
70
+ def _post_process(
71
+ self, llm_output: str, user_id: str, **kwargs
72
+ ) -> List[KnowledgePoint]:
73
+ """Parse and validate LLM extraction result."""
74
+ knowledge_points = []
75
+
76
+ for line in llm_output.strip().split("\n"):
77
+ line = line.strip()
78
+ if not line:
79
+ continue
80
+
81
+ try:
82
+ raw_point = json.loads(line)
83
+ # Add user_id and timestamp to metadata
84
+ raw_point["user_id"] = user_id
85
+ if "metadata" not in raw_point:
86
+ raw_point["metadata"] = {}
87
+ raw_point["metadata"]["extraction_time"] = datetime.now().isoformat()
88
+
89
+ # Validate and convert
90
+ validated_point = validate_knowledge_point(
91
+ KnowledgePoint(**raw_point), user_id
92
+ )
93
+ knowledge_points.append(validated_point)
94
+
95
+ except Exception as e:
96
+ logger.warning(f"Failed to parse knowledge point: {line} - {e}")
97
+ continue
98
+
99
+ return knowledge_points
100
+
101
+
102
+ class KnowledgeMergeCall(LLMCall):
103
+ """LLM call for merging and deduplicating knowledge."""
104
+
105
+ def execute(
106
+ self, all_knowledge_points: List[KnowledgePoint]
107
+ ) -> List[KnowledgePoint]:
108
+ """Execute knowledge merging for a list of knowledge points."""
109
+ return super().execute(
110
+ llm_args={"knowledge_extraction": True},
111
+ all_knowledge_points=all_knowledge_points,
112
+ )
113
+
114
+ def _log(self, time_cost: float, **kwargs) -> str:
115
+ """Log the execution result."""
116
+ logger.info(
117
+ f"Knowledge merge execution user_id[{kwargs.get('user_id', 'unknown')}] time[{time_cost} seconds]"
118
+ )
119
+
120
+ def _no_merge_result(self, **kwargs) -> Any:
121
+ """No merge result."""
122
+ return kwargs.get("all_knowledge_points", None)
123
+
124
+ def _build_prompt(self, all_knowledge_points: List[KnowledgePoint]) -> str:
125
+ """Build prompt for knowledge merging."""
126
+ if len(all_knowledge_points) <= 1:
127
+ return "" # No need to merge if only one or zero points
128
+
129
+ knowledge_json = json.dumps(all_knowledge_points, indent=2, ensure_ascii=False)
130
+
131
+ """You are a knowledge management expert. Please analyze the following knowledge points and perform intelligent merging:
132
+
133
+ 1. Identify similar, redundant, or mergeable knowledge points
134
+ 2. Merge related knowledge points into more comprehensive content
135
+ 3. Keep unique and valuable knowledge points unchanged
136
+ 4. Ensure no important information is lost during merging
137
+ 5. Update scores based on综合 importance and reliability
138
+
139
+ Current knowledge points:
140
+ {knowledge_json}
141
+
142
+ Return the optimized knowledge base as a JSON array, where each element follows the KnowledgePoint format.
143
+ Prioritize quality over quantity — fewer but higher-quality knowledge points are better.
144
+
145
+ Return only the JSON array, without any additional text.
146
+ """
147
+
148
+ def _post_process(
149
+ self, llm_output: str, all_knowledge_points: List[KnowledgePoint]
150
+ ) -> List[KnowledgePoint]:
151
+ """Parse and validate LLM merge result."""
152
+ if len(all_knowledge_points) <= 1:
153
+ return all_knowledge_points
154
+
155
+ try:
156
+ merged_knowledge = json.loads(llm_output)
157
+
158
+ # Validate each point and ensure user_id consistency
159
+ user_id = (
160
+ all_knowledge_points[0]["user_id"] if all_knowledge_points else None
161
+ )
162
+ validated_knowledge = []
163
+
164
+ for point in merged_knowledge:
165
+ try:
166
+ validated_point = validate_knowledge_point(point, user_id)
167
+ validated_knowledge.append(validated_point)
168
+ except Exception as e:
169
+ logger.warning(f"Invalid merged knowledge point: {e}")
170
+ continue
171
+
172
+ return validated_knowledge
173
+
174
+ except Exception as e:
175
+ # Fallback: return original knowledge if LLM output is invalid
176
+ logger.warning(f"LLM merge failed, keeping original knowledge: {e}")
177
+ return self._simple_deduplication(all_knowledge_points)
178
+
179
+ def _simple_deduplication(
180
+ self, knowledge_points: List[KnowledgePoint]
181
+ ) -> List[KnowledgePoint]:
182
+ """Simple deduplication as fallback."""
183
+ seen_content = set()
184
+ deduplicated = []
185
+
186
+ # Sort by score to keep highest scored items
187
+ sorted_points = sorted(knowledge_points, key=lambda p: p["score"], reverse=True)
188
+
189
+ for point in sorted_points:
190
+ content_key = point["content"].strip().lower()
191
+ if content_key not in seen_content:
192
+ seen_content.add(content_key)
193
+ deduplicated.append(point)
194
+
195
+ return deduplicated
@@ -0,0 +1,78 @@
1
+ """
2
+ Memory Manager - the main orchestrator for the memory system.
3
+ """
4
+
5
+ from typing import List, Optional
6
+
7
+ from dolphin.core.common.enums import KnowledgePoint
8
+ from .storage import MemoryFileSys
9
+ from dolphin.core.config.global_config import GlobalConfig
10
+ from dolphin.core.logging.logger import get_logger
11
+
12
+ logger = get_logger("mem")
13
+
14
+
15
+ class MemoryManager:
16
+ """The main orchestrator for the memory system - focused on complex knowledge management."""
17
+
18
+ def __init__(self, global_config: GlobalConfig):
19
+ """
20
+ Initializes the MemoryManager.
21
+
22
+ :param global_config: Global configuration instance, will create default if None.
23
+ """
24
+ self.global_config = global_config
25
+ self.memory_config = global_config.memory_config
26
+
27
+ self.memory_storage = MemoryFileSys(self.memory_config.storage_path)
28
+
29
+ def retrieve_relevant_memory(
30
+ self, context, user_id: str, query: str = None, top_k: Optional[int] = None
31
+ ) -> List[KnowledgePoint]:
32
+ """
33
+ Retrieves knowledge relevant to a given query for a specific user to be injected into context.
34
+
35
+ :param context: Context instance for accessing skillkits
36
+ :param user_id: The user whose knowledge should be retrieved
37
+ :param query: Query string (not currently used in this implementation)
38
+ :param top_k: Number of knowledge points to return
39
+ """
40
+ if top_k is None:
41
+ top_k = self.memory_config.default_top_k
42
+
43
+ if context.get_cur_agent() is None:
44
+ return []
45
+
46
+ try:
47
+ memory_skill_result = context.get_skillkit().exec(
48
+ "_read_memory",
49
+ agent_name=context.get_cur_agent().get_name(),
50
+ user_id=user_id,
51
+ )
52
+ if not memory_skill_result:
53
+ return []
54
+
55
+ memory_items = memory_skill_result.result
56
+ if not memory_items:
57
+ return []
58
+
59
+ converted_points = []
60
+ for item in memory_items:
61
+ try:
62
+ point = KnowledgePoint(
63
+ content=item.get("content", ""),
64
+ score=item.get("score", 50),
65
+ user_id=item.get("user_id", ""),
66
+ )
67
+ converted_points.append(point)
68
+ except Exception as e:
69
+ logger.warning(
70
+ f"Failed to convert dialog log to KnowledgePoint: {e}"
71
+ )
72
+ continue
73
+
74
+ return converted_points[:top_k]
75
+
76
+ except Exception as e:
77
+ logger.error(f"Failed to retrieve knowledge for user {user_id}: {e}")
78
+ return []
@@ -0,0 +1,46 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+
6
+ class MemorySandbox:
7
+ """Session-scoped filesystem sandbox for memory persistence.
8
+
9
+ - Only allows relative paths under `<storage_base>/memories/<session_id>/`.
10
+ - Only allows `.json` files.
11
+ - Provides simple size checks.
12
+ """
13
+
14
+ MAX_SIZE = 10 * 1024 * 1024 # 10MB
15
+ MAX_PATH_LENGTH = 512
16
+
17
+ def __init__(self, storage_base: str | Path = "data/memory/") -> None:
18
+ self.root = (Path(storage_base) / "memories").resolve()
19
+ self.root.mkdir(parents=True, exist_ok=True)
20
+
21
+ def resolve_session_path(self, session_id: str, rel_path: str) -> Path:
22
+ if not session_id:
23
+ raise ValueError("session_id must not be empty")
24
+ if not rel_path or rel_path.startswith("/"):
25
+ raise ValueError("Only relative paths are allowed")
26
+ if len(rel_path) > self.MAX_PATH_LENGTH:
27
+ raise ValueError("Path too long")
28
+
29
+ rel = Path(rel_path)
30
+ if ".." in rel.parts:
31
+ raise ValueError("Path escapes sandbox")
32
+ if rel.suffix.lower() != ".json":
33
+ raise ValueError("Only .json is allowed")
34
+
35
+ session_dir = (self.root / session_id).resolve()
36
+ session_dir.mkdir(parents=True, exist_ok=True)
37
+ full_path = (session_dir / rel).resolve()
38
+ # Ensure no escape from session_dir
39
+ full_path.relative_to(session_dir)
40
+ # Ensure parent dirs exist
41
+ full_path.parent.mkdir(parents=True, exist_ok=True)
42
+ return full_path
43
+
44
+ def check_size_bytes(self, size: int) -> None:
45
+ if size > self.MAX_SIZE:
46
+ raise ValueError("File too large")
@@ -0,0 +1,245 @@
1
+ """
2
+ Storage layer for the memory management system.
3
+ """
4
+
5
+ import abc
6
+ import json
7
+ import os
8
+ import glob
9
+ from pathlib import Path
10
+ from typing import List, Dict, Any
11
+ from datetime import datetime, timedelta
12
+
13
+
14
+ class MemoryStorage(abc.ABC):
15
+ """Simple memory storage abstract interface - for basic memory read and write operations"""
16
+
17
+ @abc.abstractmethod
18
+ def write_memory(
19
+ self, agent_name: str, user_id: str, memory_items: List[Dict[str, Any]]
20
+ ) -> bool:
21
+ """Write memory items
22
+
23
+ Args:
24
+ agent_name: Agent name
25
+ user_id: User ID, empty string indicates agent memory
26
+ memory_items: List of memory items
27
+
28
+ Returns:
29
+ bool: Whether successful
30
+ """
31
+ raise NotImplementedError
32
+
33
+ @abc.abstractmethod
34
+ def read_memory(
35
+ self, agent_name: str, user_id: str, days_back: int = 7
36
+ ) -> List[Dict[str, Any]]:
37
+ """Read memory items
38
+
39
+ Args:
40
+ agent_name: Agent name
41
+ user_id: User ID, empty string indicates agent memory
42
+ days_back: Read memories from the last N days
43
+
44
+ Returns:
45
+ List[Dict[str, Any]]: List of memory items
46
+ """
47
+ raise NotImplementedError
48
+
49
+ @abc.abstractmethod
50
+ def get_dialog_logs(
51
+ self, agent_name: str, user_id: str = "", count: int = 30
52
+ ) -> List[Dict[str, Any]]:
53
+ """Get conversation logs
54
+
55
+ Args:
56
+ agent_name: Agent name
57
+ user_id: User ID, empty string to get logs for all users
58
+ count: Number of entries to return
59
+
60
+ Returns:
61
+ List[Dict[str, Any]]: List of conversation logs
62
+ """
63
+ raise NotImplementedError
64
+
65
+
66
+ class MemoryFileSys(MemoryStorage):
67
+ """A simple memory storage for filesystem implementation - for MemorySkillkit"""
68
+
69
+ TimeFormat = "%Y%m%d%H%M"
70
+
71
+ def __init__(self, base_path: str):
72
+ """
73
+ Initialize the simple memory storage.
74
+
75
+ :param base_path: Base directory path for storing memory data
76
+ """
77
+ self.base_path = Path(base_path)
78
+ self.base_path.mkdir(parents=True, exist_ok=True)
79
+
80
+ def write_memory(
81
+ self, agent_name: str, user_id: str, memory_items: List[Dict[str, Any]]
82
+ ) -> bool:
83
+ """Write memory items to date-organized files
84
+
85
+ Args:
86
+ agent_name: Agent name
87
+ user_id: User ID, empty string indicates agent memory (using "_agent")
88
+ memory_items: List of memory items
89
+
90
+ Returns:
91
+ bool: Whether successful
92
+ """
93
+ try:
94
+ # If user_id is empty, use "_agent" for agent memory
95
+ actual_user_id = user_id if user_id else "_agent"
96
+
97
+ current_date = datetime.now().strftime(self.TimeFormat)
98
+ memory_dir = self.base_path / agent_name / f"user_{actual_user_id}"
99
+ memory_file = memory_dir / f"memory_{current_date}.jsonl"
100
+
101
+ # Create directory if not exists
102
+ memory_dir.mkdir(parents=True, exist_ok=True)
103
+
104
+ # Write memory items to JSONL file
105
+ with open(memory_file, "a", encoding="utf-8") as f:
106
+ for item in memory_items:
107
+ if isinstance(item, dict):
108
+ # Add timestamp if not present
109
+ if "timestamp" not in item:
110
+ item["timestamp"] = datetime.now().isoformat()
111
+ f.write(json.dumps(item, ensure_ascii=False) + "\n")
112
+
113
+ return True
114
+
115
+ except Exception:
116
+ return False
117
+
118
+ def read_memory(
119
+ self, agent_name: str, user_id: str, days_back: int = 7
120
+ ) -> List[Dict[str, Any]]:
121
+ """Read memory items from the past few days
122
+
123
+ Args:
124
+ agent_name: Agent name
125
+ user_id: User ID, empty string indicates agent memory
126
+ days_back: Number of days to look back
127
+
128
+ Returns:
129
+ List[Dict[str, Any]]: List of memory items
130
+ """
131
+ try:
132
+ # If user_id is empty, use "_agent" for agent memory
133
+ actual_user_id = user_id if user_id else "_agent"
134
+
135
+ memory_dir = self.base_path / agent_name / f"user_{actual_user_id}"
136
+
137
+ if not memory_dir.exists():
138
+ return []
139
+
140
+ # Find memory files from recent days
141
+ memory_items = []
142
+ for file in memory_dir.iterdir():
143
+ if not file.is_file() or not file.name.startswith("memory_"):
144
+ continue
145
+
146
+ date_str = file.name.split("_")[1].split(".")[0]
147
+ date = datetime.strptime(date_str, self.TimeFormat)
148
+ if date < (datetime.now() - timedelta(days=days_back)):
149
+ continue
150
+
151
+ with open(file, "r", encoding="utf-8") as f:
152
+ for line in f:
153
+ if line.strip():
154
+ try:
155
+ item = json.loads(line.strip())
156
+ memory_items.append(item)
157
+ except json.JSONDecodeError:
158
+ continue
159
+
160
+ # Sort by timestamp if available
161
+ memory_items.sort(key=lambda x: x.get("timestamp", ""), reverse=True)
162
+ return memory_items
163
+
164
+ except Exception:
165
+ return []
166
+
167
+ def get_dialog_logs(
168
+ self, agent_name: str, user_id: str = "", count: int = 30
169
+ ) -> List[Dict[str, Any]]:
170
+ """Get conversation log information
171
+
172
+ Args:
173
+ agent_name: Agent name
174
+ user_id: User ID, empty string to get logs for all users
175
+ count: Number of entries to return
176
+
177
+ Returns:
178
+ List[Dict[str, Any]]: List of conversation logs
179
+ """
180
+ try:
181
+ logs = []
182
+
183
+ # Dialog path structure: dialog_base_path/{agent_name}/user_{user_id}/dialog_*.jsonl
184
+ dialog_base_path = self.base_path.parent / "dialog"
185
+
186
+ if user_id:
187
+ # Get logs for a specific user
188
+ dialog_dir = dialog_base_path / agent_name / f"user_{user_id}"
189
+ if dialog_dir.exists():
190
+ # Get the latest dialog file
191
+ dialog_files = sorted(
192
+ glob.glob(str(dialog_dir / "dialog_*.jsonl")), reverse=True
193
+ )
194
+ for file_path in dialog_files:
195
+ with open(file_path, "r", encoding="utf-8") as f:
196
+ for line in f:
197
+ if line.strip():
198
+ try:
199
+ msg = json.loads(line.strip())
200
+ # Filter out system messages
201
+ if msg.get("role") != "system":
202
+ logs.append(msg)
203
+ if len(logs) >= count:
204
+ break
205
+ except json.JSONDecodeError:
206
+ continue
207
+ if len(logs) >= count:
208
+ break
209
+ else:
210
+ # Get logs for all users
211
+ dialog_base_dir = dialog_base_path / agent_name
212
+ if dialog_base_dir.exists():
213
+ all_files = []
214
+ for user_dir in dialog_base_dir.iterdir():
215
+ if user_dir.is_dir():
216
+ dialog_files = glob.glob(str(user_dir / "dialog_*.jsonl"))
217
+ for file_path in dialog_files:
218
+ # Get file modification time for sorting
219
+ mtime = os.path.getmtime(file_path)
220
+ all_files.append((mtime, file_path))
221
+
222
+ # Sort by modification time (newest first)
223
+ all_files.sort(reverse=True)
224
+
225
+ for _, file_path in all_files:
226
+ with open(file_path, "r", encoding="utf-8") as f:
227
+ for line in f:
228
+ if line.strip():
229
+ try:
230
+ msg = json.loads(line.strip())
231
+ # Filter out system messages
232
+ if msg.get("role") != "system":
233
+ logs.append(msg)
234
+ if len(logs) >= count:
235
+ break
236
+ except json.JSONDecodeError:
237
+ continue
238
+ if len(logs) >= count:
239
+ break
240
+
241
+ # Return the most recent count messages
242
+ return logs[:count]
243
+
244
+ except Exception:
245
+ return []
@@ -0,0 +1,51 @@
1
+ """
2
+ Utility functions for the memory management system.
3
+ """
4
+
5
+ from dolphin.core.common.enums import KnowledgePoint
6
+
7
+
8
+ def validate_knowledge_point(
9
+ data: KnowledgePoint, expected_user_id: str = None
10
+ ) -> KnowledgePoint:
11
+ """
12
+ Validate and convert raw data to KnowledgePoint.
13
+
14
+ :param data: Raw dictionary data
15
+ :param expected_user_id: Expected user_id for validation (optional)
16
+ :return: Validated KnowledgePoint
17
+ :raises KnowledgeValidationError: If validation fails
18
+ """
19
+ if data.type not in ["WorldModel", "ExperientialKnowledge", "OtherKnowledge"]:
20
+ raise Exception(f"Invalid knowledge type: {data.type}")
21
+
22
+ if not isinstance(data.score, int) or not (0 <= data.score <= 100):
23
+ raise Exception(f"Score must be integer between 0-100: {data['score']}")
24
+
25
+ if expected_user_id and data.user_id != expected_user_id:
26
+ raise Exception(
27
+ f"User ID mismatch: expected {expected_user_id}, got {data.user_id}"
28
+ )
29
+
30
+ return KnowledgePoint(
31
+ content=str(data.content),
32
+ type=data.type,
33
+ score=int(data.score),
34
+ user_id=str(data.user_id),
35
+ metadata=data.metadata,
36
+ )
37
+
38
+
39
+ def sanitize_user_id(user_id: str) -> str:
40
+ """
41
+ Sanitize user_id for safe filesystem usage.
42
+
43
+ :param user_id: Raw user ID
44
+ :return: Sanitized user ID safe for filesystem
45
+ """
46
+ # Remove or replace potentially problematic characters
47
+ # Allow only word characters, hyphens, and underscores
48
+ import re
49
+
50
+ sanitized = re.sub(r"[^\w\-_]", "_", user_id)
51
+ return sanitized[:50] # Limit length to avoid filesystem issues
@@ -0,0 +1,12 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Ontology 模块 - 本体管理系统"""
3
+
4
+ from dolphin.lib.ontology.ontology import Ontology
5
+ from dolphin.lib.ontology.ontology_manager import OntologyManager
6
+ from dolphin.lib.ontology.ontology_context import OntologyContext
7
+
8
+ __all__ = [
9
+ "Ontology",
10
+ "OntologyManager",
11
+ "OntologyContext",
12
+ ]
File without changes