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,509 @@
1
+ from __future__ import annotations
2
+ import ast
3
+ from datetime import datetime
4
+ import json
5
+ import os
6
+ import glob
7
+ import re
8
+ import time
9
+ from typing import Any, List, Union, Optional
10
+
11
+ from dolphin.core.skill.skillkit import SkillFunction, Skillkit
12
+
13
+ """System function configuration mapping:
14
+
15
+ Configuration Name Actual Function
16
+ system_functions._date -> _date
17
+ system_functions._write_file -> _write_file
18
+ system_functions._read_file -> _read_file
19
+ system_functions._read_folder -> _read_folder
20
+ system_functions._grep -> _grep
21
+ system_functions._extract_code -> _extract_code
22
+ system_functions._write_jsonl -> _write_jsonl
23
+ system_functions._sleep -> _sleep
24
+
25
+ Usage example:
26
+ skill:
27
+ enabled_skills:
28
+ - "system_functions.*" # Load all system functions (recommended)
29
+ - "system_functions._date" # Or load specific functions
30
+ - "system_functions._write_file"
31
+ - "system_functions._read_file"
32
+ - "vm_skillkit" # Other skills configured normally
33
+
34
+ Notes:
35
+ - Function names include an underscore prefix (e.g., _date)
36
+ - Use wildcard "system_functions.*" to load all functions
37
+ - When configuring specific functions, use the full system_functions._date format
38
+ - If enabled_skills is None, all system functions will be loaded (backward compatibility)
39
+ - If enabled_skills is an empty list [], no system functions will be loaded
40
+ - If enabled_skills includes other skills but not system_functions.*, system functions will not be loaded
41
+ """
42
+
43
+
44
+ class SystemFunctionsSkillKit(Skillkit):
45
+ def __init__(self, enabled_functions: List[str] | None = None):
46
+ """Initialize system function toolkit
47
+
48
+ Args:
49
+ enabled_functions: List of enabled functions
50
+ - None: Load all functions (backward compatibility)
51
+ - []: Load no functions
52
+ - ["date", "write_file"]: Load only specified functions
53
+ """
54
+ super().__init__()
55
+ self.enabled_functions = enabled_functions
56
+
57
+ def getName(self) -> str:
58
+ return "system_functions"
59
+
60
+ def _date(self, **kwargs) -> str:
61
+ """Get current date"""
62
+ return datetime.now().strftime("%Y-%m-%d")
63
+
64
+ def _extract_code(self, content: str, **kwargs) -> str:
65
+ """Extract code from markdown block, removing language specifier if present.
66
+
67
+ Args:
68
+ content (str): content
69
+
70
+ Returns:
71
+ str: code
72
+ """
73
+ # Split the content by triple backticks
74
+ parts = content.split("```")
75
+ if len(parts) < 3:
76
+ return "" # No code block found
77
+
78
+ code_section = parts[1].strip()
79
+
80
+ # Split into lines
81
+ lines = code_section.splitlines()
82
+
83
+ # Check if first line is likely a language identifier
84
+ if lines and lines[0].strip() and lines[0].strip().isidentifier():
85
+ # Remove the first line (language) and join the rest
86
+ code = "\n".join(lines[1:]).strip()
87
+ else:
88
+ code = code_section
89
+
90
+ return code
91
+
92
+ def _write_file(self, file_path: str, content: str, **kwargs) -> str:
93
+ """Write content to file, create file if it does not exist
94
+
95
+ Args:
96
+ file_path (str): File path
97
+ content (str): File content
98
+
99
+ Returns:
100
+ str: File path
101
+ """
102
+ file_dir = os.path.dirname(file_path)
103
+ # Only create directory if there is a directory component
104
+ if file_dir and not os.path.exists(file_dir):
105
+ os.makedirs(file_dir, exist_ok=True)
106
+
107
+ with open(file_path, "w", encoding="utf-8") as f:
108
+ f.write(str(content))
109
+ return file_path
110
+
111
+ def _write_jsonl(self, file_path: str, content: Any) -> str:
112
+ """Write content to a JSONL file, creating the file if it does not exist
113
+
114
+ Args:
115
+ file_path (str): File path
116
+ content (List[Dict[str, Any]]): Content
117
+
118
+ Returns:
119
+ str: File path
120
+ """
121
+ file_dir = os.path.dirname(file_path)
122
+ if not os.path.exists(file_dir):
123
+ os.makedirs(file_dir, exist_ok=True)
124
+
125
+ if isinstance(content, str):
126
+ try:
127
+ content = ast.literal_eval(content)
128
+ except Exception:
129
+ try:
130
+ content = json.loads(content, strict=False)
131
+ except Exception:
132
+ raise ValueError(f"Invalid content: {content}")
133
+
134
+ with open(file_path, "w", encoding="utf-8") as f:
135
+ for item in content:
136
+ f.write(json.dumps(item, ensure_ascii=False) + "\n")
137
+ return file_path
138
+
139
+ def _read_file(self, file_path: str, **kwargs) -> str:
140
+ """Read file
141
+
142
+ Args:
143
+ file_path (str): File path
144
+
145
+ Returns:
146
+ str: File content
147
+ """
148
+ with open(file_path, "r", encoding="utf-8") as f:
149
+ return f.read()
150
+
151
+ def _read_folder(
152
+ self,
153
+ folder_path: str,
154
+ extensions: Union[str, List[str]] = None,
155
+ start_symbol: str = None,
156
+ end_symbol: str = None,
157
+ **kwargs,
158
+ ) -> str:
159
+ """Read the content of files with specific extensions in a folder.
160
+
161
+ Args:
162
+ folder_path (str): Folder path
163
+ extensions (str or List[str], optional): File extensions, can be a single extension or a list of extensions
164
+ e.g., "txt" or ["txt", "md", "py"]
165
+ start_symbol (str, optional): Start symbol, only read content after start_symbol
166
+ end_symbol (str, optional): End symbol, only read content before end_symbol
167
+ Used together with start_symbol to read content between the two symbols
168
+
169
+ Returns:
170
+ str: Content of all matching files, sorted by filename, with each file's content prefixed by its filename
171
+ """
172
+ if not os.path.exists(folder_path):
173
+ raise FileNotFoundError(f"Folder not found: {folder_path}")
174
+
175
+ if not os.path.isdir(folder_path):
176
+ raise ValueError(f"Path is not a directory: {folder_path}")
177
+
178
+ # Handling the extension parameter
179
+ if extensions is None:
180
+ # If no extension is specified, read all files
181
+ pattern = "*"
182
+ elif isinstance(extensions, str):
183
+ # Single extension
184
+ pattern = f"*.{extensions}"
185
+ elif isinstance(extensions, list):
186
+ # Multiple extensions, using glob's brace syntax
187
+ if len(extensions) == 1:
188
+ pattern = f"*.{extensions[0]}"
189
+ else:
190
+ ext_pattern = "{" + ",".join(extensions) + "}"
191
+ pattern = f"*.{ext_pattern}"
192
+ else:
193
+ raise ValueError("extensions must be None, str, or List[str]")
194
+
195
+ # Get matching files
196
+ search_pattern = os.path.join(folder_path, pattern)
197
+ files = glob.glob(search_pattern)
198
+
199
+ # Filter out files (excluding directories) and sort them
200
+ files = [f for f in files if os.path.isfile(f)]
201
+ files.sort()
202
+
203
+ if not files:
204
+ return f"No files found in {folder_path} with pattern {pattern}"
205
+
206
+ result_parts = []
207
+
208
+ for file_path in files:
209
+ try:
210
+ with open(file_path, "r", encoding="utf-8") as f:
211
+ content = f.read()
212
+
213
+ # Handling start and end symbols
214
+ if start_symbol is not None or end_symbol is not None:
215
+ if start_symbol is not None:
216
+ start_pos = content.find(start_symbol)
217
+ if start_pos != -1:
218
+ content = content[start_pos + len(start_symbol) :]
219
+ else:
220
+ # If the start symbol is not found, skip this file or include a warning message.
221
+ content = f"[WARNING: start_symbol '{start_symbol}' not found in {os.path.basename(file_path)}]"
222
+
223
+ if end_symbol is not None and not content.startswith("[WARNING"):
224
+ end_pos = content.find(end_symbol)
225
+ if end_pos != -1:
226
+ content = content[:end_pos]
227
+ else:
228
+ # If the ending symbol is not found, a warning message is included.
229
+ content = f"[WARNING: end_symbol '{end_symbol}' not found in {os.path.basename(file_path)}]\n{content}"
230
+
231
+ # Add file identifier and content
232
+ file_name = os.path.basename(file_path)
233
+ result_parts.append(f"=== FILE: {file_name} ===\n{content.strip()}")
234
+
235
+ except Exception as e:
236
+ # If reading the file fails, add an error message
237
+ file_name = os.path.basename(file_path)
238
+ result_parts.append(
239
+ f"=== FILE: {file_name} ===\n[ERROR: Failed to read file - {str(e)}]"
240
+ )
241
+
242
+ return "\n\n".join(result_parts)
243
+
244
+ def _grep(
245
+ self,
246
+ target_path: str,
247
+ pattern: str,
248
+ before: int = 10,
249
+ after: int = 10,
250
+ recursive: bool = True,
251
+ file_extensions: Union[str, List[str], None] = None,
252
+ case_sensitive: bool = True,
253
+ use_regex: bool = True,
254
+ **kwargs,
255
+ ) -> str:
256
+ """Similar to grep functionality, searches for matching content under the specified path and can display context.
257
+
258
+ Args:
259
+ target_path (str): Path to a file or folder.
260
+ pattern (str): Matching pattern, default is regex matching.
261
+ before (int, optional): Number of lines to show before matches. Default is 0.
262
+ after (int, optional): Number of lines to show after matches. Default is 0.
263
+ recursive (bool, optional): Whether to recursively search folders. Default is True.
264
+ file_extensions (Union[str, List[str], None], optional): Only match files with specified extensions (do not include the dot).
265
+ case_sensitive (bool, optional): Whether to distinguish case. Default is True.
266
+ use_regex (bool, optional): Whether to treat pattern as a regular expression. Default is True.
267
+
268
+ Returns:
269
+ str: Matching results, including file paths, line numbers, and context.
270
+ """
271
+ if not target_path:
272
+ raise ValueError("target_path is required")
273
+ if pattern is None or pattern == "":
274
+ raise ValueError("pattern cannot be empty")
275
+ if before < 0 or after < 0:
276
+ raise ValueError("before and after must be non-negative integers")
277
+
278
+ if not os.path.exists(target_path):
279
+ raise FileNotFoundError(f"Path not found: {target_path}")
280
+
281
+ if isinstance(file_extensions, str):
282
+ file_exts = [file_extensions]
283
+ else:
284
+ file_exts = file_extensions
285
+
286
+ normalized_exts = None
287
+ if file_exts:
288
+ normalized_exts = []
289
+ for ext in file_exts:
290
+ if not isinstance(ext, str) or not ext:
291
+ raise ValueError("file_extensions must contain non-empty strings")
292
+ normalized_exts.append(ext if ext.startswith(".") else f".{ext}")
293
+
294
+ files: List[str] = []
295
+ if os.path.isfile(target_path):
296
+ files = [target_path]
297
+ elif os.path.isdir(target_path):
298
+ if recursive:
299
+ for root, _, filenames in os.walk(target_path):
300
+ for filename in filenames:
301
+ file_path = os.path.join(root, filename)
302
+ if normalized_exts and not file_path.endswith(
303
+ tuple(normalized_exts)
304
+ ):
305
+ continue
306
+ files.append(file_path)
307
+ else:
308
+ for filename in os.listdir(target_path):
309
+ file_path = os.path.join(target_path, filename)
310
+ if not os.path.isfile(file_path):
311
+ continue
312
+ if normalized_exts and not file_path.endswith(
313
+ tuple(normalized_exts)
314
+ ):
315
+ continue
316
+ files.append(file_path)
317
+ else:
318
+ raise ValueError(f"Path is neither file nor directory: {target_path}")
319
+
320
+ if not files:
321
+ return f"No files to search in {target_path}"
322
+
323
+ flags = 0 if case_sensitive else re.IGNORECASE
324
+ try:
325
+ regex = re.compile(pattern if use_regex else re.escape(pattern), flags)
326
+ except re.error as exc:
327
+ raise ValueError(f"Invalid pattern: {exc}") from exc
328
+
329
+ results: List[str] = []
330
+ match_found = False
331
+ for file_path in sorted(files):
332
+ try:
333
+ with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
334
+ lines = f.readlines()
335
+ except (OSError, UnicodeDecodeError) as exc:
336
+ results.append(
337
+ f"=== {file_path} ===\n[ERROR: Failed to read file - {str(exc)}]"
338
+ )
339
+ continue
340
+
341
+ match_indexes: List[int] = []
342
+ for idx, line in enumerate(lines):
343
+ if regex.search(line):
344
+ match_indexes.append(idx)
345
+
346
+ if not match_indexes:
347
+ continue
348
+
349
+ match_found = True
350
+ match_index_set = set(match_indexes)
351
+ context_indexes: List[int] = []
352
+ for idx in match_indexes:
353
+ start = max(0, idx - before)
354
+ end = min(len(lines) - 1, idx + after)
355
+ context_indexes.extend(range(start, end + 1))
356
+
357
+ unique_indexes = sorted(set(context_indexes))
358
+
359
+ groups: List[List[int]] = []
360
+ current_group: List[int] = []
361
+ prev_index = None
362
+ for index in unique_indexes:
363
+ if prev_index is not None and index - prev_index > 1:
364
+ if current_group:
365
+ groups.append(current_group)
366
+ current_group = []
367
+ current_group.append(index)
368
+ prev_index = index
369
+ if current_group:
370
+ groups.append(current_group)
371
+
372
+ file_results: List[str] = []
373
+ file_results.append(f"=== FILE: {os.path.basename(file_path)} ===")
374
+ for group_idx, group in enumerate(groups):
375
+ if group_idx > 0:
376
+ file_results.append("--")
377
+ for index in group:
378
+ line_number = index + 1
379
+ marker = ">" if index in match_index_set else " "
380
+ line_content = lines[index].rstrip("\r\n")
381
+ file_results.append(
382
+ f"{file_path}:{line_number}:{marker} {line_content}"
383
+ )
384
+
385
+ results.append("\n".join(file_results))
386
+
387
+ if not match_found:
388
+ return f"No matches found for '{pattern}' in {target_path}"
389
+
390
+ return "\n".join(results)
391
+
392
+ def _sleep(self, seconds: float, **kwargs) -> str:
393
+ """Pause execution for a specified number of seconds.
394
+
395
+ This is useful for:
396
+ - Waiting for page loads or animations in browser automation
397
+ - Rate limiting API calls
398
+ - Waiting for long-running operations to complete
399
+
400
+ Args:
401
+ seconds (float): Number of seconds to sleep. Can be a decimal for sub-second waits.
402
+ Maximum allowed is 300 seconds (5 minutes).
403
+
404
+ Returns:
405
+ str: Confirmation message with actual sleep duration.
406
+ """
407
+ # Validate and cap the sleep duration
408
+ if seconds < 0:
409
+ raise ValueError("seconds must be non-negative")
410
+ if seconds > 300:
411
+ seconds = 300 # Cap at 5 minutes for safety
412
+
413
+ start_time = time.time()
414
+ time.sleep(seconds)
415
+ actual_duration = time.time() - start_time
416
+
417
+ return f"Slept for {actual_duration:.2f} seconds"
418
+
419
+ def _get_result_detail(
420
+ self,
421
+ reference_id: str,
422
+ offset: int = 0,
423
+ limit: int = 2000,
424
+ **kwargs,
425
+ ) -> str:
426
+ """Get detailed content from a previous result.
427
+
428
+ When tool output is omitted, use this method to fetch full content
429
+ or a specific range.
430
+
431
+ Args:
432
+ reference_id: Result reference ID (from previous omitted output)
433
+ offset: Start position (character offset), default 0
434
+ limit: Maximum characters to return, default 2000
435
+
436
+ Returns:
437
+ Content within the specified range
438
+
439
+ Note:
440
+ This skill receives the context's skillkit_hook via the 'props' parameter
441
+ which is injected by the skill execution flow. The hook instance contains
442
+ the same cache backend where the original results are stored.
443
+ """
444
+ # Get hook from props (injected by skill execution flow)
445
+ # The props contain the context as 'gvp' (global variable pool)
446
+ props = kwargs.get("props", {})
447
+ context = props.get("gvp", None) # Note: context is passed as 'gvp' in skill_run()
448
+
449
+ hook = None
450
+ if context and hasattr(context, "skillkit_hook") and context.skillkit_hook:
451
+ hook = context.skillkit_hook
452
+ else:
453
+ # Fallback: Try to get from global reference (not recommended path)
454
+ # This may not find the result if cache is instance-based
455
+ from dolphin.lib.skill_results.skillkit_hook import SkillkitHook
456
+ hook = SkillkitHook()
457
+
458
+ raw_result = hook.get_raw_result(reference_id)
459
+
460
+ if raw_result is None:
461
+ return f"Error: reference_id '{reference_id}' not found or expired. The result may have been cleaned up or the reference ID is incorrect."
462
+
463
+ content = str(raw_result)
464
+ total_length = len(content)
465
+
466
+ # Get specified range
467
+ result = content[offset : offset + limit]
468
+
469
+ # Append meta info to help LLM understand position
470
+ if offset + limit < total_length:
471
+ remaining = total_length - offset - limit
472
+ result += f"\n... ({remaining} chars remaining, total {total_length})"
473
+
474
+ return result
475
+
476
+ def _createSkills(self) -> List[SkillFunction]:
477
+ all_skills = [
478
+ SkillFunction(self._date),
479
+ SkillFunction(self._write_file),
480
+ SkillFunction(self._write_jsonl),
481
+ SkillFunction(self._read_file),
482
+ SkillFunction(self._read_folder),
483
+ SkillFunction(self._extract_code),
484
+ SkillFunction(self._grep),
485
+ SkillFunction(self._sleep),
486
+ SkillFunction(self._get_result_detail),
487
+ ]
488
+
489
+ # If no enable function is specified, return all skills (backward compatibility)
490
+ if self.enabled_functions is None:
491
+ return all_skills
492
+
493
+ # If wildcard "*" is in enabled_functions, return all skills
494
+ if "*" in self.enabled_functions:
495
+ return all_skills
496
+
497
+ # Filter enabled skills
498
+ enabled_skills = []
499
+ for skill in all_skills:
500
+ # Get the function name and convert it to a skill name (e.g., _date -> system_date)
501
+ function_name = skill.get_function_name()
502
+ if function_name in self.enabled_functions:
503
+ enabled_skills.append(skill)
504
+
505
+ return enabled_skills
506
+
507
+
508
+
509
+ SystemFunctions = SystemFunctionsSkillKit()
@@ -0,0 +1,65 @@
1
+ from typing import List
2
+
3
+ from dolphin.core.skill.skill_function import SkillFunction
4
+ from dolphin.core.skill.skillkit import Skillkit
5
+ from dolphin.lib.vm.vm import VM
6
+ from dolphin.lib.vm.python_session_manager import PythonSessionManager
7
+
8
+
9
+ class VMSkillkit(Skillkit):
10
+ def __init__(self):
11
+ super().__init__()
12
+ self.vm: VM = None
13
+ self.session_manager = PythonSessionManager()
14
+
15
+ def getName(self) -> str:
16
+ return "vm_skillkit"
17
+
18
+ def setVM(self, vm: VM):
19
+ self.vm = vm
20
+
21
+ def _bash(self, cmd: str, **kwargs) -> str:
22
+ """Execute a bash command in the virtual machine and return the execution result.
23
+
24
+ Args:
25
+ cmd (str): The bash command to execute
26
+
27
+ Returns:
28
+ str: The execution result
29
+ """
30
+ if self.vm is None:
31
+ raise RuntimeError(
32
+ "VM is not configured. Please set VM before executing bash commands."
33
+ )
34
+ return self.vm.execBash(cmd)
35
+
36
+ def _python(self, cmd: str, **kwargs) -> str:
37
+ """Execute a Python command in the virtual machine and return the execution result.
38
+ Support session state persistence, running continuously like Jupyter Notebook.
39
+
40
+ Args:
41
+ cmd (str): The Python command to execute; result variables need to be printed
42
+ **kwargs: Additional parameters, including gvp from context
43
+
44
+ Returns:
45
+ str: Execution result
46
+ """
47
+ if self.vm is None:
48
+ raise RuntimeError(
49
+ "VM is not configured. Please set VM before executing Python commands."
50
+ )
51
+
52
+ session_id = self.getSessionId(
53
+ session_id=kwargs.get("session_id"), props=kwargs.get("props")
54
+ )
55
+ if session_id:
56
+ kwargs["session_id"] = session_id
57
+ kwargs["session_manager"] = self.session_manager
58
+
59
+ return self.vm.execPython(cmd, **kwargs)
60
+
61
+ def _createSkills(self) -> List[SkillFunction]:
62
+ return [
63
+ SkillFunction(self._bash, block_as_parameter=("bash", "cmd")),
64
+ SkillFunction(self._python, block_as_parameter=("python", "cmd")),
65
+ ]
@@ -0,0 +1,9 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Utils 模块 - 工具函数"""
3
+
4
+ from dolphin.lib.utils.data_process import *
5
+ from dolphin.lib.utils.security import *
6
+ from dolphin.lib.utils.text_retrieval import *
7
+ from dolphin.lib.utils.handle_progress import *
8
+
9
+ __all__ = []