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,207 @@
1
+ import re
2
+
3
+
4
+ def escape(prompt: str):
5
+ # to prevent 'format' exception in get_template_vars
6
+ return prompt.replace("{", "{{").replace("}", "}}")
7
+
8
+
9
+ def unescape(prompt: str):
10
+ return re.sub(r"\{{2,}", "{", re.sub(r"\}{2,}", "}", prompt))
11
+
12
+
13
+ def get_nested_value(var_name, data: dict):
14
+ """Recursively parse nested variable names, supporting a mix of array indexing and attribute access
15
+ For example: var[0].field[1].subfield, user['name'], header["x-user"]
16
+ """
17
+ if not var_name:
18
+ return data
19
+
20
+ # If the variable name starts with a square bracket, process the content within the square brackets directly.
21
+ if var_name.startswith("["):
22
+ bracket_end = var_name.find("]")
23
+ if bracket_end == -1:
24
+ raise ValueError(f"变量名 '{var_name}' 中的方括号不匹配")
25
+
26
+ # Extract content within square brackets
27
+ bracket_content = var_name[1:bracket_end]
28
+
29
+ # Check if it's a string key (quoted)
30
+ if (bracket_content.startswith("'") and bracket_content.endswith("'")) or (
31
+ bracket_content.startswith('"') and bracket_content.endswith('"')
32
+ ):
33
+ # String key, remove quotes
34
+ key_name = bracket_content[1:-1]
35
+
36
+ # Get dictionary value
37
+ if not isinstance(data, dict):
38
+ raise ValueError("数据不是字典类型,无法使用键访问")
39
+
40
+ if key_name not in data:
41
+ raise ValueError(f"键 '{key_name}' 不存在于数据中")
42
+
43
+ dict_value = data[key_name]
44
+ remaining = var_name[bracket_end + 1 :]
45
+
46
+ if not remaining:
47
+ return dict_value
48
+ elif remaining.startswith("."):
49
+ # Continue recursive processing
50
+ if not isinstance(dict_value, dict):
51
+ raise ValueError("字典值不是字典类型,无法使用点号访问")
52
+ return get_nested_value(remaining[1:], dict_value)
53
+ elif remaining.startswith("["):
54
+ # Continue recursive processing
55
+ return get_nested_value(remaining, dict_value)
56
+ else:
57
+ raise ValueError(f"变量名 '{var_name}' 格式错误,无法解析")
58
+
59
+ else:
60
+ # Numeric Indexing (Array Access)
61
+ try:
62
+ index = int(bracket_content)
63
+ except ValueError:
64
+ # Index is not a number
65
+ raise ValueError(f"数组索引 '{bracket_content}' 不是有效的数字")
66
+
67
+ # Get array elements
68
+ if not isinstance(data, list):
69
+ raise ValueError("数据不是列表类型,无法使用索引访问")
70
+
71
+ if not (0 <= index < len(data)):
72
+ raise ValueError(f"数组索引 {index} 超出范围 [0, {len(data)})")
73
+
74
+ array_element = data[index]
75
+ remaining = var_name[bracket_end + 1 :]
76
+
77
+ if not remaining:
78
+ return array_element
79
+ elif remaining.startswith("."):
80
+ # Continue recursive processing
81
+ if not isinstance(array_element, dict):
82
+ raise ValueError("数组元素不是字典类型,无法使用点号访问")
83
+ return get_nested_value(remaining[1:], array_element)
84
+ elif remaining.startswith("["):
85
+ # Continue recursive processing
86
+ if not isinstance(array_element, list):
87
+ raise ValueError("数组元素不是列表类型,无法使用索引访问")
88
+ return get_nested_value(remaining, array_element)
89
+ else:
90
+ raise ValueError(f"变量名 '{var_name}' 格式错误,无法解析")
91
+
92
+ # Find the position of the first operator
93
+ dot_pos = var_name.find(".")
94
+ bracket_start = var_name.find("[")
95
+
96
+ # If there are neither dots nor square brackets, return directly.
97
+ if dot_pos == -1 and bracket_start == -1:
98
+ if var_name not in data:
99
+ raise ValueError(f"变量 '{var_name}' 不存在于数据中")
100
+ return data.get(var_name)
101
+
102
+ # Determine the first operator
103
+ first_op_pos = -1
104
+ if dot_pos == -1:
105
+ first_op_pos = bracket_start
106
+ elif bracket_start == -1:
107
+ first_op_pos = dot_pos
108
+ else:
109
+ first_op_pos = min(dot_pos, bracket_start)
110
+
111
+ # Extract the name of the key currently being accessed
112
+ current_key = var_name[:first_op_pos]
113
+ rest = var_name[first_op_pos:]
114
+
115
+ # Get current value
116
+ if current_key not in data:
117
+ raise ValueError(f"变量 '{current_key}' 不存在于数据中")
118
+ current_value = data.get(current_key)
119
+
120
+ # If there are no remaining parts, return the current value
121
+ if not rest:
122
+ return current_value
123
+
124
+ # Process the remaining part
125
+ if rest.startswith("."):
126
+ # Dot access, recursively process the remaining part
127
+ if not isinstance(current_value, dict):
128
+ raise ValueError(f"变量 '{current_key}' 不是字典类型,无法使用点号访问")
129
+ return get_nested_value(rest[1:], current_value)
130
+ elif rest.startswith("["):
131
+ # Bracket Access
132
+ bracket_end = rest.find("]")
133
+ if bracket_end == -1:
134
+ # No matching right parenthesis found
135
+ raise ValueError(f"变量名 '{var_name}' 中的方括号不匹配")
136
+
137
+ # Extract content within square brackets
138
+ bracket_content = rest[1:bracket_end]
139
+
140
+ # Check if it's a string key (quoted)
141
+ if (bracket_content.startswith("'") and bracket_content.endswith("'")) or (
142
+ bracket_content.startswith('"') and bracket_content.endswith('"')
143
+ ):
144
+ # String key, remove quotes
145
+ key_name = bracket_content[1:-1]
146
+
147
+ # Get dictionary value
148
+ if not isinstance(current_value, dict):
149
+ raise ValueError(f"变量 '{current_key}' 不是字典类型,无法使用键访问")
150
+
151
+ if key_name not in current_value:
152
+ raise ValueError(f"键 '{key_name}' 不存在于字典 '{current_key}' 中")
153
+
154
+ dict_value = current_value[key_name]
155
+ remaining = rest[bracket_end + 1 :]
156
+
157
+ if not remaining:
158
+ return dict_value
159
+ elif remaining.startswith("."):
160
+ # Continue recursive processing
161
+ if not isinstance(dict_value, dict):
162
+ raise ValueError(
163
+ f"字典值 '{current_key}['{key_name}']' 不是字典类型,无法使用点号访问"
164
+ )
165
+ return get_nested_value(remaining[1:], dict_value)
166
+ elif remaining.startswith("["):
167
+ # Continue recursive processing
168
+ return get_nested_value(remaining, dict_value)
169
+
170
+ else:
171
+ # Numeric Indexing (Array Access)
172
+ try:
173
+ index = int(bracket_content)
174
+ except ValueError:
175
+ # Index is not a number
176
+ raise ValueError(f"数组索引 '{bracket_content}' 不是有效的数字")
177
+
178
+ # Get array elements
179
+ if not isinstance(current_value, list):
180
+ raise ValueError(f"变量 '{current_key}' 不是列表类型,无法使用索引访问")
181
+
182
+ if not (0 <= index < len(current_value)):
183
+ raise ValueError(f"数组索引 {index} 超出范围 [0, {len(current_value)})")
184
+
185
+ array_element = current_value[index]
186
+ remaining = rest[bracket_end + 1 :]
187
+
188
+ if not remaining:
189
+ return array_element
190
+ elif remaining.startswith("."):
191
+ # Continue recursive processing
192
+ if not isinstance(array_element, dict):
193
+ raise ValueError(
194
+ f"数组元素 '{current_key}[{index}]' 不是字典类型,无法使用点号访问"
195
+ )
196
+ return get_nested_value(remaining[1:], array_element)
197
+ elif remaining.startswith("["):
198
+ # Continue recursive processing
199
+ if not isinstance(array_element, list):
200
+ raise ValueError(
201
+ f"数组元素 '{current_key}[{index}]' 不是列表类型,无法使用索引访问"
202
+ )
203
+ return get_nested_value(remaining, array_element)
204
+ else:
205
+ raise ValueError(f"变量名 '{var_name}' 格式错误,无法解析")
206
+
207
+ raise ValueError(f"变量名 '{var_name}' 格式错误,无法解析")
@@ -0,0 +1,178 @@
1
+ import json
2
+ import threading
3
+ import time
4
+ from collections import OrderedDict
5
+ from typing import Dict, List, Optional
6
+ from dolphin.core.logging.logger import console
7
+
8
+
9
+ # Thread-safe global variables
10
+ class ProgressManager:
11
+ def __init__(self, max_size=1000, expire_seconds=3600):
12
+ self.progress_map: Dict[str, List[Dict]] = OrderedDict()
13
+ self.progress_set: Dict[str, Dict[str, bool]] = {}
14
+ self.lock = threading.RLock()
15
+ self.max_size = max_size
16
+ self.expire_seconds = expire_seconds
17
+ self.last_access: Dict[str, float] = {}
18
+
19
+ def _cleanup_expired(self):
20
+ """Clean up expired progress data"""
21
+ current_time = time.time()
22
+ expired_keys = []
23
+
24
+ for key, last_time in self.last_access.items():
25
+ if current_time - last_time > self.expire_seconds:
26
+ expired_keys.append(key)
27
+
28
+ for key in expired_keys:
29
+ self._remove_key(key)
30
+
31
+ def _remove_key(self, key: str):
32
+ """Remove data for the specified key"""
33
+ if key in self.progress_map:
34
+ del self.progress_map[key]
35
+ if key in self.progress_set:
36
+ del self.progress_set[key]
37
+ if key in self.last_access:
38
+ del self.last_access[key]
39
+
40
+ def _enforce_max_size(self):
41
+ """Enforce maximum size limit"""
42
+ while len(self.progress_map) > self.max_size:
43
+ # Remove the oldest entry
44
+ oldest_key = next(iter(self.progress_map))
45
+ self._remove_key(oldest_key)
46
+
47
+
48
+ # Global progress manager instance
49
+ _progress_manager = ProgressManager()
50
+
51
+
52
+ def handle_progress(assistant_message_id: str, progresses: List[Dict]) -> List[Dict]:
53
+ """Thread-safe version for processing progress information
54
+
55
+ Args:
56
+ assistant_message_id: Assistant message ID
57
+ progresses: List of progress information (in dictionary format)
58
+
59
+ Returns:
60
+ Processed list of progress information
61
+ """
62
+ with _progress_manager.lock:
63
+ # Clean up expired data
64
+ _progress_manager._cleanup_expired()
65
+
66
+ # Update access time
67
+ _progress_manager.last_access[assistant_message_id] = time.time()
68
+
69
+ # Get the progress collection of the current message
70
+ progress_set_for_message = _progress_manager.progress_set.get(
71
+ assistant_message_id, {}
72
+ )
73
+ current_progress: Optional[Dict] = None
74
+
75
+ # Traverse all progress information
76
+ for progress in progresses:
77
+ status = progress.get("status", "")
78
+
79
+ if status in ["completed", "failed"]:
80
+ # Use more efficient hash values for deduplication
81
+ progress_hash = hash(json.dumps(progress, sort_keys=True))
82
+
83
+ # Check if already exists to avoid duplication
84
+ if progress_hash not in progress_set_for_message:
85
+ # Ensure that there is a corresponding list in progress_map.
86
+ if assistant_message_id not in _progress_manager.progress_map:
87
+ _progress_manager.progress_map[assistant_message_id] = []
88
+
89
+ # Add to history progress list
90
+ _progress_manager.progress_map[assistant_message_id].append(
91
+ progress
92
+ )
93
+ # Marked as existing
94
+ progress_set_for_message[progress_hash] = True
95
+
96
+ elif status == "processing":
97
+ # Record the current progress being processed
98
+ current_progress = progress
99
+
100
+ # Update progress_set
101
+ _progress_manager.progress_set[assistant_message_id] = progress_set_for_message
102
+
103
+ # Enforce size limits
104
+ _progress_manager._enforce_max_size()
105
+
106
+ # Build return result
107
+ result = []
108
+
109
+ # Add historical progress (completed and failed)
110
+ if assistant_message_id in _progress_manager.progress_map:
111
+ result.extend(_progress_manager.progress_map[assistant_message_id])
112
+
113
+ # Add the progress of the currently processed item (if any)
114
+ if current_progress is not None:
115
+ result.append(current_progress)
116
+
117
+ return result
118
+
119
+
120
+ def cleanup_progress(assistant_message_id: str) -> None:
121
+ """Thread-safe version to clear progress data for the specified message ID"""
122
+ with _progress_manager.lock:
123
+ _progress_manager._remove_key(assistant_message_id)
124
+
125
+
126
+ def initialize_progress(assistant_message_id: str) -> None:
127
+ """Initialize progress data for the specified message ID (thread-safe version)"""
128
+ with _progress_manager.lock:
129
+ _progress_manager.progress_map[assistant_message_id] = []
130
+ _progress_manager.progress_set[assistant_message_id] = {}
131
+ _progress_manager.last_access[assistant_message_id] = time.time()
132
+
133
+
134
+ def get_progress_stats() -> Dict:
135
+ """Get progress manager statistics (for monitoring)"""
136
+ with _progress_manager.lock:
137
+ return {
138
+ "total_sessions": len(_progress_manager.progress_map),
139
+ "total_progress_items": sum(
140
+ len(items) for items in _progress_manager.progress_map.values()
141
+ ),
142
+ "max_size": _progress_manager.max_size,
143
+ "expire_seconds": _progress_manager.expire_seconds,
144
+ }
145
+
146
+
147
+ # Usage Examples
148
+ if __name__ == "__main__":
149
+ # Initialize
150
+ message_id = "msg_123"
151
+ initialize_progress(message_id)
152
+
153
+ # Example data
154
+ progresses = [
155
+ {
156
+ "agent_name": "agent1",
157
+ "stage": "thinking",
158
+ "status": "completed",
159
+ "answer": "Thinking completed",
160
+ },
161
+ {
162
+ "agent_name": "agent1",
163
+ "stage": "executing",
164
+ "status": "processing",
165
+ "answer": "正在执行",
166
+ },
167
+ ]
168
+
169
+ # Processing Progress
170
+ result = handle_progress(message_id, progresses)
171
+ console(f"处理结果: {len(result)} 个进度")
172
+
173
+ # Cleanup
174
+ cleanup_progress(message_id)
175
+
176
+ # Get statistical information
177
+ stats = get_progress_stats()
178
+ console(f"统计信息: {stats}")
@@ -0,0 +1,139 @@
1
+ import base64
2
+ import os
3
+ from typing import Optional
4
+ from dolphin.core.logging.logger import get_logger
5
+
6
+ logger = get_logger()
7
+
8
+
9
+ class SecurityUtils:
10
+ """Security utility class, providing encryption and decryption functions"""
11
+
12
+ # Default salt value; in actual applications, it should be obtained from environment variables or configurations.
13
+ DEFAULT_SALT = b"dolphin_default_salt_value"
14
+
15
+ # 缓存 cryptography 模块(延迟导入)
16
+ _cryptography = None
17
+
18
+ @classmethod
19
+ def _get_cryptography(cls):
20
+ """获取 cryptography 模块(延迟导入)"""
21
+ if cls._cryptography is None:
22
+ try:
23
+ from cryptography.fernet import Fernet
24
+ from cryptography.hazmat.backends import default_backend
25
+ from cryptography.hazmat.primitives import hashes
26
+ from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
27
+ cls._cryptography = {
28
+ 'Fernet': Fernet,
29
+ 'default_backend': default_backend,
30
+ 'hashes': hashes,
31
+ 'PBKDF2HMAC': PBKDF2HMAC,
32
+ }
33
+ except ImportError:
34
+ logger.error("cryptography is required for SecurityUtils but not installed. Please install it: pip install cryptography")
35
+ raise ImportError("cryptography is required for SecurityUtils but not installed. Please install it: pip install cryptography")
36
+ return cls._cryptography
37
+
38
+ @staticmethod
39
+ def _get_key(password: str, salt: Optional[bytes] = None) -> bytes:
40
+ """Generate encryption key
41
+
42
+ Args:
43
+ password: The password used to generate the key
44
+ salt: Optional salt value; if not provided, a default salt value is used
45
+
46
+ Returns:
47
+ bytes: The generated key
48
+ """
49
+ crypto = SecurityUtils._get_cryptography()
50
+ PBKDF2HMAC = crypto['PBKDF2HMAC']
51
+ hashes = crypto['hashes']
52
+ default_backend = crypto['default_backend']
53
+
54
+ if not salt:
55
+ salt = SecurityUtils.DEFAULT_SALT
56
+
57
+ kdf = PBKDF2HMAC(
58
+ algorithm=hashes.SHA256(),
59
+ length=32,
60
+ salt=salt,
61
+ iterations=100000,
62
+ backend=default_backend(),
63
+ )
64
+ return base64.urlsafe_b64encode(kdf.derive(password.encode()))
65
+
66
+ @staticmethod
67
+ def encrypt(text: str, password: str, salt: Optional[bytes] = None) -> str:
68
+ """Encrypted text
69
+
70
+ Args:
71
+ text: The text to be encrypted
72
+ password: The password used to generate the key
73
+ salt: Optional salt value
74
+
75
+ Returns:
76
+ str: The encrypted text (Base64 encoded)
77
+ """
78
+ try:
79
+ crypto = SecurityUtils._get_cryptography()
80
+ Fernet = crypto['Fernet']
81
+ key = SecurityUtils._get_key(password, salt)
82
+ f = Fernet(key)
83
+ encrypted_data = f.encrypt(text.encode())
84
+ return base64.urlsafe_b64encode(encrypted_data).decode()
85
+ except ImportError:
86
+ raise
87
+ except Exception as e:
88
+ logger.error(f"加密failed: {str(e)}")
89
+ raise RuntimeError(f"加密failed: {str(e)}")
90
+
91
+ @staticmethod
92
+ def decrypt(
93
+ encrypted_text: str, password: str, salt: Optional[bytes] = None
94
+ ) -> str:
95
+ """Decrypt text
96
+
97
+ Args:
98
+ encrypted_text: The encrypted text (Base64 encoded)
99
+ password: The password used to generate the key
100
+ salt: Optional salt value
101
+
102
+ Returns:
103
+ str: The decrypted text
104
+ """
105
+ try:
106
+ crypto = SecurityUtils._get_cryptography()
107
+ Fernet = crypto['Fernet']
108
+ key = SecurityUtils._get_key(password, salt)
109
+ f = Fernet(key)
110
+ # First, perform Base64 decoding on the encrypted text.
111
+ decoded_data = base64.urlsafe_b64decode(encrypted_text.encode())
112
+ decrypted_data = f.decrypt(decoded_data)
113
+ return decrypted_data.decode()
114
+ except ImportError:
115
+ raise
116
+ except Exception as e:
117
+ logger.error(f"解密failed: {str(e)}")
118
+ return encrypted_text # Return the original encrypted text when decryption fails
119
+
120
+ @staticmethod
121
+ def generate_key() -> str:
122
+ """Generate a new Fernet key
123
+
124
+ Returns:
125
+ str: The generated key (Base64-encoded string)
126
+ """
127
+ crypto = SecurityUtils._get_cryptography()
128
+ Fernet = crypto['Fernet']
129
+ key = Fernet.generate_key()
130
+ return key.decode()
131
+
132
+ @staticmethod
133
+ def get_env_password() -> str:
134
+ """Get password from environment variables
135
+
136
+ Returns:
137
+ str: Password obtained from environment variables, or default value if not set
138
+ """
139
+ return os.environ.get("DOLPHIN_PASSWORD", "default_password_please_change_me")