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,706 @@
1
+ import os
2
+ import importlib.util
3
+ import importlib.metadata
4
+ import inspect
5
+ from typing import Dict, Optional
6
+ from rich.console import Console
7
+
8
+ from dolphin.core.config.global_config import GlobalConfig
9
+ from dolphin.core.logging.logger import get_logger
10
+ from dolphin.core.skill.skillset import Skillset
11
+ from dolphin.lib.skillkits.agent_skillkit import AgentSkillKit
12
+ from dolphin.lib.skillkits.system_skillkit import (
13
+ SystemFunctionsSkillKit,
14
+ )
15
+ from dolphin.core.agent.base_agent import BaseAgent
16
+
17
+ logger = get_logger("skill")
18
+
19
+
20
+ class GlobalSkills:
21
+ """
22
+ Global skills manager that handles both installed skills and agent skills
23
+ """
24
+
25
+ def __init__(self, globalConfig: GlobalConfig):
26
+ """
27
+ Initialize global skills manager
28
+
29
+ Args:
30
+ globalConfig (GlobalConfig): Global configuration object
31
+ """
32
+ self.globalConfig = globalConfig
33
+ self.installedSkillset = Skillset()
34
+ self.agentSkillset = Skillset()
35
+ self.agentSkills: Dict[str, BaseAgent] = {}
36
+
37
+ # Load installed skills from skill/installed directory
38
+ self._loadInstalledSkills()
39
+
40
+ # Load MCP skills if enabled
41
+ if globalConfig.mcp_config and globalConfig.mcp_config.enabled:
42
+ self._loadMCPSkills()
43
+
44
+ self._syncAllSkills()
45
+
46
+ def _loadInstalledSkills(self):
47
+ """
48
+ Load all skillkits using entry points first, fallback to file-based loading
49
+ """
50
+ # Try loading from entry points first (preferred method for pyinstaller compatibility)
51
+ if self._loadSkillkitsFromEntryPoints():
52
+ logger.debug("Successfully loaded skillkits from entry points")
53
+ else:
54
+ logger.debug(
55
+ "Entry points loading failed, falling back to file-based loading"
56
+ )
57
+ # Fallback to original file-based loading
58
+ self._loadSkillkitsFromFiles()
59
+
60
+ # Handle system function loading, following skill_config configuration
61
+ enabled_system_functions = self._get_enabled_system_functions()
62
+ # Decide how to load system functions based on the value of enabled_system_functions
63
+ system_functions = SystemFunctionsSkillKit(enabled_system_functions)
64
+ for skill in system_functions.getSkills():
65
+ self.installedSkillset.addSkill(skill)
66
+
67
+ def _loadSkillkitsFromEntryPoints(self) -> bool:
68
+ """
69
+ Load skillkits from setuptools entry points
70
+
71
+ Returns:
72
+ bool: True if loading succeeded, False if failed
73
+ """
74
+ try:
75
+ # Get all entry points for dolphin.skillkits
76
+ entry_points = importlib.metadata.entry_points(group="dolphin.skillkits")
77
+
78
+ if not entry_points:
79
+ logger.debug("No dolphin.skillkits entry points found")
80
+ return False
81
+
82
+ # Initialize VM if needed
83
+ vm = None
84
+ if (
85
+ hasattr(self.globalConfig, "vm_config")
86
+ and self.globalConfig.vm_config is not None
87
+ ):
88
+ try:
89
+ from dolphin.lib.vm.vm import VMFactory
90
+
91
+ vm = VMFactory.createVM(self.globalConfig.vm_config)
92
+ except Exception as e:
93
+ logger.warning(f"Failed to create VM: {str(e)}")
94
+
95
+ loaded_count = 0
96
+ console = Console()
97
+
98
+ with console.status("[bold green]Loading skillkits from entry points...") as status:
99
+ for entry_point in entry_points:
100
+ status.update(f"[bold blue]Loading skillkit:[/][white] {entry_point.name}[/]")
101
+ try:
102
+ # Check if this skill should be loaded based on config
103
+ if not self.globalConfig.skill_config.should_load_skill(
104
+ entry_point.name
105
+ ):
106
+ logger.debug(f"Skipping disabled skillkit: {entry_point.name}")
107
+ continue
108
+
109
+ # Load the skillkit class from entry point
110
+ skillkit_class = entry_point.load()
111
+
112
+ # Verify it's a Skillkit subclass
113
+ if not self._is_obj_hierarchy_from_class_name(
114
+ skillkit_class, "Skillkit"
115
+ ):
116
+ logger.warning(
117
+ f"Entry point {entry_point.name} is not a Skillkit subclass, skipping"
118
+ )
119
+ continue
120
+
121
+ # Create instance and configure
122
+ skillkit_instance = skillkit_class()
123
+
124
+ # Set VM if this is VMSkillkit and we have a VM configured
125
+ if hasattr(skillkit_instance, "setVM") and vm is not None:
126
+ skillkit_instance.setVM(vm)
127
+
128
+ # Set global context if the skillkit supports it
129
+ if hasattr(skillkit_instance, "setGlobalConfig"):
130
+ skillkit_instance.setGlobalConfig(self.globalConfig)
131
+
132
+ # Add skillkit to the installed skillset
133
+ # This tracks the skillkit for metadata aggregation
134
+ self.installedSkillset.addSkillkit(skillkit_instance)
135
+
136
+ loaded_count += 1
137
+ logger.debug(
138
+ f"Loaded skillkit from entry point: {entry_point.name}"
139
+ )
140
+
141
+ except Exception as e:
142
+ logger.error(
143
+ f"Failed to load skillkit from entry point {entry_point.name}: {str(e)}"
144
+ )
145
+ continue
146
+
147
+ logger.debug(
148
+ f"Successfully loaded {loaded_count} skillkits from entry points"
149
+ )
150
+ return loaded_count > 0
151
+
152
+ except Exception as e:
153
+ logger.error(f"Failed to load skillkits from entry points: {str(e)}")
154
+ return False
155
+
156
+ def _loadSkillkitsFromFiles(self):
157
+ """
158
+ Load all skillkits from skill/installed directory (fallback method)
159
+ Reuses existing code from DolphinExecutor::set_installed_skills
160
+ """
161
+ # Get the path to skill/installed directory
162
+ current_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
163
+ installed_skills_dir = os.path.join(current_dir, "skill", "installed")
164
+
165
+ if not os.path.exists(installed_skills_dir):
166
+ logger.warning(
167
+ f"Installed skills directory not found: {installed_skills_dir}"
168
+ )
169
+ return
170
+ self._loadSkillkitsFromPath(installed_skills_dir, "installed")
171
+
172
+ def _get_enabled_system_functions(self) -> Optional[list[str]]:
173
+ """Extract system function configurations from skill_config.enabled_skills"""
174
+ enabled_skills = self.globalConfig.skill_config.enabled_skills
175
+
176
+ # If enabled_skills is None, it means all skills (including system functions) will be loaded.
177
+ if enabled_skills is None:
178
+ return None
179
+
180
+ # Extract configurations in the format of system_functions.*
181
+ system_functions = []
182
+ for skill in enabled_skills:
183
+ if skill.startswith("system_functions."):
184
+ function_name = skill.replace("system_functions.", "")
185
+ system_functions.append(function_name)
186
+ # If system_functions are explicitly configured but the list is empty, return an empty list (no system functions will be loaded)
187
+ has_system_config = any(
188
+ skill.startswith("system_functions") for skill in enabled_skills
189
+ )
190
+ if has_system_config and not system_functions:
191
+ return []
192
+
193
+ # If no system_functions are configured, return [] (for backward compatibility)
194
+ if not system_functions:
195
+ return []
196
+
197
+ return system_functions
198
+
199
+ def _loadMCPSkills(self):
200
+ """Load MCP skill suite"""
201
+ # Check whether MCP skills should be loaded
202
+ if not self.globalConfig.skill_config.should_load_skill("mcp"):
203
+ logger.debug("MCP skills are disabled by configuration")
204
+ return
205
+
206
+ try:
207
+ from dolphin.lib.skillkits.mcp_skillkit import MCPSkillkit
208
+
209
+ # Create a single MCP skill suite instance
210
+ skillkit = MCPSkillkit()
211
+ skillkit.setGlobalConfig(self.globalConfig)
212
+
213
+ # Get skills and add to installed skill set
214
+ skills = skillkit.getSkills()
215
+ for skill in skills:
216
+ self.installedSkillset.addSkill(skill)
217
+
218
+ logger.debug(f"Loaded MCP skillkit: {len(skills)} skills")
219
+
220
+ except ImportError as e:
221
+ logger.warning(f"Failed to import MCP components: {str(e)}")
222
+ except Exception as e:
223
+ logger.warning(f"Error loading MCP skills: {str(e)}")
224
+
225
+ def _loadCustomSkillkitsFromPath(self, skillkitFolderPath: str):
226
+ """
227
+ Load all skillkits from custom skillkit folder
228
+
229
+ Args:
230
+ skillkitFolderPath (str): Path to the custom skillkit folder
231
+ """
232
+ # Normalize the path to handle both relative and absolute paths
233
+ if not os.path.isabs(skillkitFolderPath):
234
+ # Convert relative path to absolute path based on current working directory
235
+ skillkitFolderPath = os.path.abspath(skillkitFolderPath)
236
+
237
+ if not os.path.exists(skillkitFolderPath):
238
+ logger.warning(f"Custom skillkit folder not found: {skillkitFolderPath}")
239
+ return
240
+
241
+ logger.debug(f"Loading custom skillkits from: {skillkitFolderPath}")
242
+ self._loadSkillkitsFromPath(skillkitFolderPath, "custom")
243
+
244
+ def _loadSkillkitsFromPath(self, folderPath: str, skillkitType: str = "installed"):
245
+ """
246
+ Load skillkits from the specified folder path (only top-level directory)
247
+
248
+ Args:
249
+ folderPath (str): Path to scan for skillkits
250
+ skillkitType (str): Type of skillkits being loaded ("installed" or "custom")
251
+ """
252
+ # Initialize VM if needed
253
+ vm = None
254
+ if (
255
+ hasattr(self.globalConfig, "vm_config")
256
+ and self.globalConfig.vm_config is not None
257
+ ):
258
+ try:
259
+ from dolphin.lib.vm.vm import VMFactory
260
+
261
+ vm = VMFactory.createVM(self.globalConfig.vm_config)
262
+ except Exception as e:
263
+ logger.warning(f"Failed to create VM: {str(e)}")
264
+
265
+ # Define files to skip (not skillkits but utility modules)
266
+ SKIP_MODULES = {
267
+ "mcp_adapter", # MCP adapter utility, not a skillkit
268
+ # Add other utility modules here as needed
269
+ }
270
+
271
+ # Only scan top-level directory for both installed and custom skillkits
272
+ if not os.path.exists(folderPath):
273
+ logger.warning(f"Skillkit folder does not exist: {folderPath}")
274
+ return
275
+
276
+ for filename in os.listdir(folderPath):
277
+ # Only process .py files in the top-level directory, skip __init__.py and __pycache__
278
+ if filename.endswith(".py") and not filename.startswith("__"):
279
+ filePath = os.path.join(folderPath, filename)
280
+
281
+ # Skip directories
282
+ if os.path.isdir(filePath):
283
+ continue
284
+
285
+ moduleName = filename[:-3] # Remove .py extension
286
+
287
+ # Skip utility modules that are not skillkits
288
+ if moduleName in SKIP_MODULES:
289
+ continue
290
+
291
+ if not self.globalConfig.skill_config.should_load_skill(moduleName):
292
+ continue
293
+
294
+ try:
295
+ self._loadSkillkitFromFile(filePath, moduleName, vm, skillkitType)
296
+ except Exception as e:
297
+ # Log error but continue with other files
298
+ logger.error(
299
+ f"Failed to load {skillkitType} skillkit from {filename}: {str(e)}"
300
+ )
301
+ continue
302
+
303
+ def _loadSkillkitFromFile(
304
+ self, filePath: str, moduleName: str, vm, skillkitType: str
305
+ ):
306
+ """
307
+ Load skillkit from a single file
308
+
309
+ Args:
310
+ filePath (str): Path to the Python file
311
+ moduleName (str): Module name for import
312
+ vm: VM instance (if available)
313
+ skillkitType (str): Type of skillkit being loaded
314
+ """
315
+ import sys
316
+ import os
317
+
318
+ # Get the directory containing the file and its parent
319
+ dirPath = os.path.dirname(filePath)
320
+ parentDirPath = os.path.dirname(dirPath)
321
+ originalSysPath = sys.path.copy()
322
+
323
+ try:
324
+ # Add both the file's directory and its parent to sys.path
325
+ paths_to_add = [dirPath, parentDirPath]
326
+ for path in paths_to_add:
327
+ if path not in sys.path:
328
+ sys.path.insert(0, path)
329
+
330
+ # Ensure package structure is properly initialized
331
+ packageName = os.path.basename(dirPath)
332
+
333
+ # Create package module if it doesn't exist
334
+ if packageName not in sys.modules:
335
+ package_init_file = os.path.join(dirPath, "__init__.py")
336
+ if os.path.exists(package_init_file):
337
+ # Load the package __init__.py
338
+ package_spec = importlib.util.spec_from_file_location(
339
+ packageName, package_init_file
340
+ )
341
+ package_module = importlib.util.module_from_spec(package_spec)
342
+ package_module.__path__ = [dirPath]
343
+ sys.modules[packageName] = package_module
344
+ try:
345
+ package_spec.loader.exec_module(package_module)
346
+ except Exception as e:
347
+ logger.warning(
348
+ f"Warning: Failed to execute package __init__.py: {e}"
349
+ )
350
+ else:
351
+ # Create a minimal package module
352
+ package_module = type(sys)("package")
353
+ package_module.__path__ = [dirPath]
354
+ package_module.__package__ = packageName
355
+ sys.modules[packageName] = package_module
356
+
357
+ # Try different import strategies
358
+ module = None
359
+ import_errors = []
360
+
361
+ # Strategy 1: Direct file import with package context
362
+ try:
363
+ spec = importlib.util.spec_from_file_location(moduleName, filePath)
364
+ module = importlib.util.module_from_spec(spec)
365
+
366
+ # Set package information for relative imports
367
+ module.__package__ = packageName
368
+ module.__file__ = filePath
369
+
370
+ # Add to sys.modules temporarily to support relative imports
371
+ sys.modules[moduleName] = module
372
+ if packageName != moduleName:
373
+ sys.modules[packageName + "." + moduleName] = module
374
+
375
+ spec.loader.exec_module(module)
376
+
377
+ except Exception as e:
378
+ import_errors.append(f"Strategy 1 failed: {e}")
379
+
380
+ # Strategy 2: Try importing as part of package
381
+ try:
382
+ fullModuleName = f"{packageName}.{moduleName}"
383
+
384
+ spec = importlib.util.spec_from_file_location(
385
+ fullModuleName, filePath
386
+ )
387
+ module = importlib.util.module_from_spec(spec)
388
+ module.__package__ = packageName
389
+
390
+ sys.modules[fullModuleName] = module
391
+ spec.loader.exec_module(module)
392
+
393
+ except Exception as e2:
394
+ import_errors.append(f"Strategy 2 failed: {e2}")
395
+
396
+ # Strategy 3: Load target module only without dependencies
397
+ try:
398
+ # Just try to load our target module directly with absolute import
399
+ fullModuleName = f"{packageName}.{moduleName}"
400
+
401
+ # Create module spec
402
+ spec = importlib.util.spec_from_file_location(
403
+ fullModuleName, filePath
404
+ )
405
+ module = importlib.util.module_from_spec(spec)
406
+ module.__package__ = packageName
407
+
408
+ # Add to sys.modules
409
+ sys.modules[fullModuleName] = module
410
+
411
+ # Try to execute, if it fails due to missing dependencies,
412
+ # modify the imports in the module temporarily
413
+ original_import = __builtins__["__import__"]
414
+
415
+ def custom_import(
416
+ name, globals=None, locals=None, fromlist=(), level=0
417
+ ):
418
+ # Handle relative imports within the same package
419
+ if (
420
+ level > 0
421
+ and globals
422
+ and globals.get("__package__") == packageName
423
+ ):
424
+ if level == 1: # from .module import something
425
+ base_module = packageName
426
+ if name:
427
+ full_name = f"{base_module}.{name}"
428
+ else:
429
+ full_name = base_module
430
+ else:
431
+ full_name = name
432
+
433
+ # Try to load the referenced module if it's in the same directory
434
+ if "." in full_name:
435
+ module_name = full_name.split(".")[-1]
436
+ module_file = os.path.join(
437
+ dirPath, f"{module_name}.py"
438
+ )
439
+ if (
440
+ os.path.exists(module_file)
441
+ and full_name not in sys.modules
442
+ ):
443
+ try:
444
+ ref_spec = (
445
+ importlib.util.spec_from_file_location(
446
+ full_name, module_file
447
+ )
448
+ )
449
+ ref_module = (
450
+ importlib.util.module_from_spec(
451
+ ref_spec
452
+ )
453
+ )
454
+ ref_module.__package__ = packageName
455
+ sys.modules[full_name] = ref_module
456
+ ref_spec.loader.exec_module(ref_module)
457
+ except:
458
+ pass
459
+
460
+ return original_import(
461
+ full_name, globals, locals, fromlist, 0
462
+ )
463
+ else:
464
+ return original_import(
465
+ name, globals, locals, fromlist, level
466
+ )
467
+
468
+ # Temporarily replace __import__
469
+ __builtins__["__import__"] = custom_import
470
+
471
+ try:
472
+ spec.loader.exec_module(module)
473
+ finally:
474
+ # Restore original __import__
475
+ __builtins__["__import__"] = original_import
476
+
477
+ except Exception as e3:
478
+ import_errors.append(f"Strategy 3 failed: {e3}")
479
+ raise Exception(
480
+ f"All import strategies failed: {import_errors}"
481
+ )
482
+
483
+ if module is None:
484
+ raise Exception(f"Failed to load module: {import_errors}")
485
+
486
+ # Find all Skillkit classes in the module
487
+ for name, obj in inspect.getmembers(module, inspect.isclass):
488
+ # Check if it's a Skillkit subclass but not Skillkit itself
489
+ if self._is_obj_hierarchy_from_class_name(obj, "Skillkit"):
490
+ # Create an instance of the skillkit
491
+ skillkit_instance = obj()
492
+
493
+ # Set VM if this is VMSkillkit and we have a VM configured
494
+ if hasattr(skillkit_instance, "setVM") and vm is not None:
495
+ skillkit_instance.setVM(vm)
496
+
497
+ # Set global context if the skillkit supports it
498
+ if hasattr(skillkit_instance, "setGlobalConfig"):
499
+ skillkit_instance.setGlobalConfig(self.globalConfig)
500
+
501
+ # Add skillkit to the installed skillset
502
+ # This tracks the skillkit for metadata aggregation
503
+ self.installedSkillset.addSkillkit(skillkit_instance)
504
+
505
+ logger.debug(
506
+ f"Loaded {skillkitType} skillkit: {moduleName} from {filePath}"
507
+ )
508
+
509
+ except Exception as e:
510
+ logger.error(
511
+ f"Failed to load {skillkitType} skillkit from {filePath}: {str(e)}"
512
+ )
513
+ finally:
514
+ # Clean up sys.modules to avoid conflicts, but keep package modules
515
+ modules_to_remove = []
516
+ for mod_name in sys.modules:
517
+ if mod_name == moduleName or (
518
+ mod_name.endswith("." + moduleName) and mod_name != packageName
519
+ ):
520
+ modules_to_remove.append(mod_name)
521
+
522
+ for mod_name in modules_to_remove:
523
+ try:
524
+ del sys.modules[mod_name]
525
+ except KeyError:
526
+ pass
527
+
528
+ # Restore original sys.path
529
+ sys.path = originalSysPath
530
+
531
+ def registerAgentSkill(self, agentName: str, agent: BaseAgent):
532
+ """
533
+ Register an agent as a skill
534
+
535
+ Args:
536
+ agentName (str): Name of the agent
537
+ agent (BaseAgent): BaseAgent instance to register
538
+ """
539
+ # Store agent reference
540
+ self.agentSkills[agentName] = agent
541
+
542
+ # Create AgentSkillKit to wrap the agent
543
+ agentSkillKit = AgentSkillKit(agent, agentName)
544
+
545
+ # Add agent skills to the agent skillset
546
+ for skill in agentSkillKit.getSkills():
547
+ self.agentSkillset.addSkill(skill)
548
+
549
+ self._syncAllSkills()
550
+
551
+ logger.debug(f"Registered agent skill: {agentName}")
552
+
553
+ def unregisterAgentSkill(self, agentName: str):
554
+ """
555
+ Unregister an agent skill
556
+
557
+ Args:
558
+ agentName (str): Name of the agent to unregister
559
+ """
560
+ if agentName in self.agentSkills:
561
+ # Remove from agent skills
562
+ del self.agentSkills[agentName]
563
+
564
+ # Remove from skillset - this is tricky as we need to identify which skills belong to this agent
565
+ # We'll rebuild the agent skillset
566
+ self._rebuildAgentSkillset()
567
+
568
+ logger.debug(f"Unregistered agent skill: {agentName}")
569
+ self._syncAllSkills()
570
+
571
+ def _rebuildAgentSkillset(self):
572
+ """
573
+ Rebuild the agent skillset from current agent skills
574
+ """
575
+ self.agentSkillset = Skillset()
576
+ for agentName, agent in self.agentSkills.items():
577
+ agentSkillKit = AgentSkillKit(agent, agentName)
578
+ for skill in agentSkillKit.getSkills():
579
+ self.agentSkillset.addSkill(skill)
580
+
581
+ def clearAgentSkills(self):
582
+ """
583
+ Clear all agent skills
584
+ """
585
+ self.agentSkills.clear()
586
+ self.agentSkillset = Skillset()
587
+ self._syncAllSkills()
588
+
589
+ def getInstalledSkills(self) -> Skillset:
590
+ """
591
+ Get the installed skills skillset
592
+
593
+ Returns:
594
+ Skillset containing installed skills
595
+ """
596
+ return self.installedSkillset
597
+
598
+ def getAgentSkills(self) -> Skillset:
599
+ """
600
+ Get the agent skills skillset
601
+
602
+ Returns:
603
+ Skillset containing agent skills
604
+ """
605
+ return self.agentSkillset
606
+
607
+ def _syncAllSkills(self):
608
+ """
609
+ Sync all skills (installed + agent skills) as a combined skillset.
610
+
611
+ Note: Metadata prompt is not copied here. It is dynamically collected
612
+ via skill.owner_skillkit in ExploreStrategy._collect_metadata_prompt().
613
+ """
614
+ self.allSkills = Skillset()
615
+
616
+ # Add installed skills (owner_skillkit is already bound)
617
+ for skill in self.installedSkillset.getSkills():
618
+ self.allSkills.addSkill(skill)
619
+
620
+ # Add agent skills
621
+ for skill in self.agentSkillset.getSkills():
622
+ self.allSkills.addSkill(skill)
623
+
624
+ def getAllSkills(self) -> Skillset:
625
+ """
626
+ Get all skills (installed + agent skills) as a combined skillset
627
+
628
+ Returns:
629
+ Skillset containing all skills
630
+ """
631
+ return self.allSkills
632
+
633
+ def getSkillNames(self) -> list:
634
+ """
635
+ Get all skill names
636
+
637
+ Returns:
638
+ List of all skill names
639
+ """
640
+ return self.getAllSkills().getSkillNames()
641
+
642
+ def hasSkill(self, skillName: str) -> bool:
643
+ """
644
+ Check if a skill exists
645
+
646
+ Args:
647
+ skillName (str): Name of the skill to check
648
+
649
+ Returns:
650
+ True if skill exists, False otherwise
651
+ """
652
+ return self.getAllSkills().hasSkill(skillName)
653
+
654
+ def getSkill(self, skillName: str):
655
+ """
656
+ Get a skill by name
657
+
658
+ Args:
659
+ skillName (str): Name of the skill to get
660
+
661
+ Returns:
662
+ SkillFunction skill or None if not found
663
+ """
664
+ return self.getAllSkills().getSkill(skillName)
665
+
666
+ def getAgent(self, agentName: str) -> Optional[BaseAgent]:
667
+ """
668
+ Get an agent by name
669
+
670
+ Args:
671
+ agentName (str): Name of the agent
672
+
673
+ Returns:
674
+ BaseAgent instance or None if not found
675
+ """
676
+ return self.agentSkills.get(agentName)
677
+
678
+ def getAgentNames(self) -> list:
679
+ """
680
+ Get list of all registered agent names
681
+
682
+ Returns:
683
+ List of agent names
684
+ """
685
+ return list(self.agentSkills.keys())
686
+
687
+ def _is_obj_hierarchy_from_class_name(self, obj: object, className: str) -> bool:
688
+ """
689
+ Check if an object is a hierarchy of a given class name
690
+ """
691
+ if hasattr(obj, "__bases__"):
692
+ for base in obj.__bases__:
693
+ if base.__name__ == className:
694
+ return True
695
+ if self._is_obj_hierarchy_from_class_name(base, className):
696
+ return True
697
+ return False
698
+
699
+ def __str__(self) -> str:
700
+ """
701
+ String representation of global skills
702
+
703
+ Returns:
704
+ Description string
705
+ """
706
+ return f"GlobalSkills(installed={len(self.installedSkillset.getSkills())}, agents={len(self.agentSkills)})"