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.
- DolphinLanguageSDK/__init__.py +58 -0
- dolphin/__init__.py +62 -0
- dolphin/cli/__init__.py +20 -0
- dolphin/cli/args/__init__.py +9 -0
- dolphin/cli/args/parser.py +567 -0
- dolphin/cli/builtin_agents/__init__.py +22 -0
- dolphin/cli/commands/__init__.py +4 -0
- dolphin/cli/interrupt/__init__.py +8 -0
- dolphin/cli/interrupt/handler.py +205 -0
- dolphin/cli/interrupt/keyboard.py +82 -0
- dolphin/cli/main.py +49 -0
- dolphin/cli/multimodal/__init__.py +34 -0
- dolphin/cli/multimodal/clipboard.py +327 -0
- dolphin/cli/multimodal/handler.py +249 -0
- dolphin/cli/multimodal/image_processor.py +214 -0
- dolphin/cli/multimodal/input_parser.py +149 -0
- dolphin/cli/runner/__init__.py +8 -0
- dolphin/cli/runner/runner.py +989 -0
- dolphin/cli/ui/__init__.py +10 -0
- dolphin/cli/ui/console.py +2795 -0
- dolphin/cli/ui/input.py +340 -0
- dolphin/cli/ui/layout.py +425 -0
- dolphin/cli/ui/stream_renderer.py +302 -0
- dolphin/cli/utils/__init__.py +8 -0
- dolphin/cli/utils/helpers.py +135 -0
- dolphin/cli/utils/version.py +49 -0
- dolphin/core/__init__.py +107 -0
- dolphin/core/agent/__init__.py +10 -0
- dolphin/core/agent/agent_state.py +69 -0
- dolphin/core/agent/base_agent.py +970 -0
- dolphin/core/code_block/__init__.py +0 -0
- dolphin/core/code_block/agent_init_block.py +0 -0
- dolphin/core/code_block/assign_block.py +98 -0
- dolphin/core/code_block/basic_code_block.py +1865 -0
- dolphin/core/code_block/explore_block.py +1327 -0
- dolphin/core/code_block/explore_block_v2.py +712 -0
- dolphin/core/code_block/explore_strategy.py +672 -0
- dolphin/core/code_block/judge_block.py +220 -0
- dolphin/core/code_block/prompt_block.py +32 -0
- dolphin/core/code_block/skill_call_deduplicator.py +291 -0
- dolphin/core/code_block/tool_block.py +129 -0
- dolphin/core/common/__init__.py +17 -0
- dolphin/core/common/constants.py +176 -0
- dolphin/core/common/enums.py +1173 -0
- dolphin/core/common/exceptions.py +133 -0
- dolphin/core/common/multimodal.py +539 -0
- dolphin/core/common/object_type.py +165 -0
- dolphin/core/common/output_format.py +432 -0
- dolphin/core/common/types.py +36 -0
- dolphin/core/config/__init__.py +16 -0
- dolphin/core/config/global_config.py +1289 -0
- dolphin/core/config/ontology_config.py +133 -0
- dolphin/core/context/__init__.py +12 -0
- dolphin/core/context/context.py +1580 -0
- dolphin/core/context/context_manager.py +161 -0
- dolphin/core/context/var_output.py +82 -0
- dolphin/core/context/variable_pool.py +356 -0
- dolphin/core/context_engineer/__init__.py +41 -0
- dolphin/core/context_engineer/config/__init__.py +5 -0
- dolphin/core/context_engineer/config/settings.py +402 -0
- dolphin/core/context_engineer/core/__init__.py +7 -0
- dolphin/core/context_engineer/core/budget_manager.py +327 -0
- dolphin/core/context_engineer/core/context_assembler.py +583 -0
- dolphin/core/context_engineer/core/context_manager.py +637 -0
- dolphin/core/context_engineer/core/tokenizer_service.py +260 -0
- dolphin/core/context_engineer/example/incremental_example.py +267 -0
- dolphin/core/context_engineer/example/traditional_example.py +334 -0
- dolphin/core/context_engineer/services/__init__.py +5 -0
- dolphin/core/context_engineer/services/compressor.py +399 -0
- dolphin/core/context_engineer/utils/__init__.py +6 -0
- dolphin/core/context_engineer/utils/context_utils.py +441 -0
- dolphin/core/context_engineer/utils/message_formatter.py +270 -0
- dolphin/core/context_engineer/utils/token_utils.py +139 -0
- dolphin/core/coroutine/__init__.py +15 -0
- dolphin/core/coroutine/context_snapshot.py +154 -0
- dolphin/core/coroutine/context_snapshot_profile.py +922 -0
- dolphin/core/coroutine/context_snapshot_store.py +268 -0
- dolphin/core/coroutine/execution_frame.py +145 -0
- dolphin/core/coroutine/execution_state_registry.py +161 -0
- dolphin/core/coroutine/resume_handle.py +101 -0
- dolphin/core/coroutine/step_result.py +101 -0
- dolphin/core/executor/__init__.py +18 -0
- dolphin/core/executor/debug_controller.py +630 -0
- dolphin/core/executor/dolphin_executor.py +1063 -0
- dolphin/core/executor/executor.py +624 -0
- dolphin/core/flags/__init__.py +27 -0
- dolphin/core/flags/definitions.py +49 -0
- dolphin/core/flags/manager.py +113 -0
- dolphin/core/hook/__init__.py +95 -0
- dolphin/core/hook/expression_evaluator.py +499 -0
- dolphin/core/hook/hook_dispatcher.py +380 -0
- dolphin/core/hook/hook_types.py +248 -0
- dolphin/core/hook/isolated_variable_pool.py +284 -0
- dolphin/core/interfaces.py +53 -0
- dolphin/core/llm/__init__.py +0 -0
- dolphin/core/llm/llm.py +495 -0
- dolphin/core/llm/llm_call.py +100 -0
- dolphin/core/llm/llm_client.py +1285 -0
- dolphin/core/llm/message_sanitizer.py +120 -0
- dolphin/core/logging/__init__.py +20 -0
- dolphin/core/logging/logger.py +526 -0
- dolphin/core/message/__init__.py +8 -0
- dolphin/core/message/compressor.py +749 -0
- dolphin/core/parser/__init__.py +8 -0
- dolphin/core/parser/parser.py +405 -0
- dolphin/core/runtime/__init__.py +10 -0
- dolphin/core/runtime/runtime_graph.py +926 -0
- dolphin/core/runtime/runtime_instance.py +446 -0
- dolphin/core/skill/__init__.py +14 -0
- dolphin/core/skill/context_retention.py +157 -0
- dolphin/core/skill/skill_function.py +686 -0
- dolphin/core/skill/skill_matcher.py +282 -0
- dolphin/core/skill/skillkit.py +700 -0
- dolphin/core/skill/skillset.py +72 -0
- dolphin/core/trajectory/__init__.py +10 -0
- dolphin/core/trajectory/recorder.py +189 -0
- dolphin/core/trajectory/trajectory.py +522 -0
- dolphin/core/utils/__init__.py +9 -0
- dolphin/core/utils/cache_kv.py +212 -0
- dolphin/core/utils/tools.py +340 -0
- dolphin/lib/__init__.py +93 -0
- dolphin/lib/debug/__init__.py +8 -0
- dolphin/lib/debug/visualizer.py +409 -0
- dolphin/lib/memory/__init__.py +28 -0
- dolphin/lib/memory/async_processor.py +220 -0
- dolphin/lib/memory/llm_calls.py +195 -0
- dolphin/lib/memory/manager.py +78 -0
- dolphin/lib/memory/sandbox.py +46 -0
- dolphin/lib/memory/storage.py +245 -0
- dolphin/lib/memory/utils.py +51 -0
- dolphin/lib/ontology/__init__.py +12 -0
- dolphin/lib/ontology/basic/__init__.py +0 -0
- dolphin/lib/ontology/basic/base.py +102 -0
- dolphin/lib/ontology/basic/concept.py +130 -0
- dolphin/lib/ontology/basic/object.py +11 -0
- dolphin/lib/ontology/basic/relation.py +63 -0
- dolphin/lib/ontology/datasource/__init__.py +27 -0
- dolphin/lib/ontology/datasource/datasource.py +66 -0
- dolphin/lib/ontology/datasource/oracle_datasource.py +338 -0
- dolphin/lib/ontology/datasource/sql.py +845 -0
- dolphin/lib/ontology/mapping.py +177 -0
- dolphin/lib/ontology/ontology.py +733 -0
- dolphin/lib/ontology/ontology_context.py +16 -0
- dolphin/lib/ontology/ontology_manager.py +107 -0
- dolphin/lib/skill_results/__init__.py +31 -0
- dolphin/lib/skill_results/cache_backend.py +559 -0
- dolphin/lib/skill_results/result_processor.py +181 -0
- dolphin/lib/skill_results/result_reference.py +179 -0
- dolphin/lib/skill_results/skillkit_hook.py +324 -0
- dolphin/lib/skill_results/strategies.py +328 -0
- dolphin/lib/skill_results/strategy_registry.py +150 -0
- dolphin/lib/skillkits/__init__.py +44 -0
- dolphin/lib/skillkits/agent_skillkit.py +155 -0
- dolphin/lib/skillkits/cognitive_skillkit.py +82 -0
- dolphin/lib/skillkits/env_skillkit.py +250 -0
- dolphin/lib/skillkits/mcp_adapter.py +616 -0
- dolphin/lib/skillkits/mcp_skillkit.py +771 -0
- dolphin/lib/skillkits/memory_skillkit.py +650 -0
- dolphin/lib/skillkits/noop_skillkit.py +31 -0
- dolphin/lib/skillkits/ontology_skillkit.py +89 -0
- dolphin/lib/skillkits/plan_act_skillkit.py +452 -0
- dolphin/lib/skillkits/resource/__init__.py +52 -0
- dolphin/lib/skillkits/resource/models/__init__.py +6 -0
- dolphin/lib/skillkits/resource/models/skill_config.py +109 -0
- dolphin/lib/skillkits/resource/models/skill_meta.py +127 -0
- dolphin/lib/skillkits/resource/resource_skillkit.py +393 -0
- dolphin/lib/skillkits/resource/skill_cache.py +215 -0
- dolphin/lib/skillkits/resource/skill_loader.py +395 -0
- dolphin/lib/skillkits/resource/skill_validator.py +406 -0
- dolphin/lib/skillkits/resource_skillkit.py +11 -0
- dolphin/lib/skillkits/search_skillkit.py +163 -0
- dolphin/lib/skillkits/sql_skillkit.py +274 -0
- dolphin/lib/skillkits/system_skillkit.py +509 -0
- dolphin/lib/skillkits/vm_skillkit.py +65 -0
- dolphin/lib/utils/__init__.py +9 -0
- dolphin/lib/utils/data_process.py +207 -0
- dolphin/lib/utils/handle_progress.py +178 -0
- dolphin/lib/utils/security.py +139 -0
- dolphin/lib/utils/text_retrieval.py +462 -0
- dolphin/lib/vm/__init__.py +11 -0
- dolphin/lib/vm/env_executor.py +895 -0
- dolphin/lib/vm/python_session_manager.py +453 -0
- dolphin/lib/vm/vm.py +610 -0
- dolphin/sdk/__init__.py +60 -0
- dolphin/sdk/agent/__init__.py +12 -0
- dolphin/sdk/agent/agent_factory.py +236 -0
- dolphin/sdk/agent/dolphin_agent.py +1106 -0
- dolphin/sdk/api/__init__.py +4 -0
- dolphin/sdk/runtime/__init__.py +8 -0
- dolphin/sdk/runtime/env.py +363 -0
- dolphin/sdk/skill/__init__.py +10 -0
- dolphin/sdk/skill/global_skills.py +706 -0
- dolphin/sdk/skill/traditional_toolkit.py +260 -0
- kweaver_dolphin-0.1.0.dist-info/METADATA +521 -0
- kweaver_dolphin-0.1.0.dist-info/RECORD +199 -0
- kweaver_dolphin-0.1.0.dist-info/WHEEL +5 -0
- kweaver_dolphin-0.1.0.dist-info/entry_points.txt +27 -0
- kweaver_dolphin-0.1.0.dist-info/licenses/LICENSE.txt +201 -0
- 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)})"
|