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,181 @@
|
|
|
1
|
+
"""Result Processor Module
|
|
2
|
+
Responsible for processing tool execution results, including caching, reference management, and policy application
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import uuid
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from typing import Any, Dict, Optional
|
|
8
|
+
|
|
9
|
+
from dolphin.lib.skill_results.cache_backend import (
|
|
10
|
+
CacheBackend,
|
|
11
|
+
CacheEntry,
|
|
12
|
+
)
|
|
13
|
+
from dolphin.lib.skill_results.result_reference import ResultReference
|
|
14
|
+
from dolphin.lib.skill_results.strategy_registry import StrategyRegistry
|
|
15
|
+
|
|
16
|
+
from dolphin.core.logging.logger import get_logger
|
|
17
|
+
|
|
18
|
+
logger = get_logger("skill_results")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ResultProcessor:
|
|
22
|
+
"""Result Processor
|
|
23
|
+
Responsible for receiving raw tool results, writing to cache, and returning references
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(
|
|
27
|
+
self, cache_backend: CacheBackend, strategy_registry: StrategyRegistry
|
|
28
|
+
):
|
|
29
|
+
self.cache_backend = cache_backend
|
|
30
|
+
self.strategy_registry = strategy_registry
|
|
31
|
+
|
|
32
|
+
def process_result(
|
|
33
|
+
self,
|
|
34
|
+
tool_name: str,
|
|
35
|
+
result: Any,
|
|
36
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
37
|
+
) -> ResultReference:
|
|
38
|
+
"""Process tool execution results
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
tool_name: Tool name
|
|
42
|
+
result: Tool execution result
|
|
43
|
+
metadata: Metadata
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
ResultReference: Result reference object
|
|
47
|
+
"""
|
|
48
|
+
try:
|
|
49
|
+
# Generate unique reference ID
|
|
50
|
+
reference_id = str(uuid.uuid4())
|
|
51
|
+
|
|
52
|
+
# Preparing metadata
|
|
53
|
+
if metadata is None:
|
|
54
|
+
metadata = {}
|
|
55
|
+
|
|
56
|
+
metadata.update(
|
|
57
|
+
{
|
|
58
|
+
"tool_name": tool_name,
|
|
59
|
+
"processed_at": datetime.now().isoformat(),
|
|
60
|
+
"result_type": type(result).__name__,
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Calculate result size
|
|
65
|
+
result_size = len(str(result)) if result else 0
|
|
66
|
+
|
|
67
|
+
# Create cache entry
|
|
68
|
+
cache_entry = CacheEntry(
|
|
69
|
+
reference_id=reference_id,
|
|
70
|
+
full_result=result,
|
|
71
|
+
metadata=metadata,
|
|
72
|
+
created_at=datetime.now(),
|
|
73
|
+
tool_name=tool_name,
|
|
74
|
+
size=result_size,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Store in cache
|
|
78
|
+
self.cache_backend.store(cache_entry)
|
|
79
|
+
|
|
80
|
+
logger.debug(
|
|
81
|
+
f"结果处理完成: tool={tool_name}, id={reference_id}, size={result_size}"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# Return result reference
|
|
85
|
+
return ResultReference(
|
|
86
|
+
reference_id=reference_id,
|
|
87
|
+
cache_backend=self.cache_backend,
|
|
88
|
+
strategy_registry=self.strategy_registry,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
except Exception as e:
|
|
92
|
+
logger.error(f"结果处理failed: tool={tool_name}, error={e}")
|
|
93
|
+
raise
|
|
94
|
+
|
|
95
|
+
def get_result_reference(self, reference_id: str) -> Optional[ResultReference]:
|
|
96
|
+
"""Get result reference object by reference ID
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
reference_id: Reference ID
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
ResultReference: Result reference object, returns None if not exists
|
|
103
|
+
"""
|
|
104
|
+
try:
|
|
105
|
+
if not self.cache_backend.exists(reference_id):
|
|
106
|
+
return None
|
|
107
|
+
|
|
108
|
+
return ResultReference(
|
|
109
|
+
reference_id=reference_id,
|
|
110
|
+
cache_backend=self.cache_backend,
|
|
111
|
+
strategy_registry=self.strategy_registry,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
except Exception as e:
|
|
115
|
+
logger.error(f"Get结果引用failed: id={reference_id}, error={e}")
|
|
116
|
+
return None
|
|
117
|
+
|
|
118
|
+
def delete_result(self, reference_id: str) -> bool:
|
|
119
|
+
"""Delete the cached result of the specified reference
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
reference_id: Reference ID
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
bool: Whether the deletion was successful
|
|
126
|
+
"""
|
|
127
|
+
try:
|
|
128
|
+
success = self.cache_backend.delete(reference_id)
|
|
129
|
+
if success:
|
|
130
|
+
logger.info(f"结果删除successful: id={reference_id}")
|
|
131
|
+
else:
|
|
132
|
+
logger.warning(f"结果删除failed: id={reference_id}")
|
|
133
|
+
return success
|
|
134
|
+
|
|
135
|
+
except Exception as e:
|
|
136
|
+
logger.error(f"结果删除异常: id={reference_id}, error={e}")
|
|
137
|
+
return False
|
|
138
|
+
|
|
139
|
+
def cleanup_expired(self, max_age_hours: int = 24) -> int:
|
|
140
|
+
"""Clean expired cache
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
max_age_hours: Maximum retention time (in hours)
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
int: Number of items cleaned
|
|
147
|
+
"""
|
|
148
|
+
try:
|
|
149
|
+
cleaned_count = self.cache_backend.cleanup(max_age_hours)
|
|
150
|
+
logger.info(
|
|
151
|
+
f"过期缓存清理完成: count={cleaned_count}, max_age={max_age_hours}h"
|
|
152
|
+
)
|
|
153
|
+
return cleaned_count
|
|
154
|
+
|
|
155
|
+
except Exception as e:
|
|
156
|
+
logger.error(f"过期缓存清理failed: error={e}")
|
|
157
|
+
return 0
|
|
158
|
+
|
|
159
|
+
def get_stats(self) -> Dict[str, Any]:
|
|
160
|
+
"""Get cache statistics
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
Dict: Dictionary of statistics
|
|
164
|
+
"""
|
|
165
|
+
try:
|
|
166
|
+
cache_stats = self.cache_backend.get_stats()
|
|
167
|
+
strategy_stats = self.strategy_registry.get_stats()
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
"cache": cache_stats,
|
|
171
|
+
"strategies": strategy_stats,
|
|
172
|
+
"processor": {
|
|
173
|
+
"type": "ResultProcessor",
|
|
174
|
+
"cache_backend": type(self.cache_backend).__name__,
|
|
175
|
+
"strategy_registry": type(self.strategy_registry).__name__,
|
|
176
|
+
},
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
except Exception as e:
|
|
180
|
+
logger.error(f"Get统计信息failed: error={e}")
|
|
181
|
+
return {"error": str(e)}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""Result reference module
|
|
2
|
+
Provides functions for referencing, caching, and handling strategies for result data
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict, Optional, TYPE_CHECKING
|
|
6
|
+
from datetime import datetime, timedelta
|
|
7
|
+
|
|
8
|
+
from dolphin.lib.skill_results.cache_backend import CacheBackend, CacheEntry
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from dolphin.lib.skill_results.strategy_registry import StrategyRegistry
|
|
12
|
+
|
|
13
|
+
from dolphin.core.logging.logger import get_logger
|
|
14
|
+
|
|
15
|
+
logger = get_logger("skill_results")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ResultReference:
|
|
19
|
+
"""Result reference class, providing reference access to cached results"""
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
reference_id: str,
|
|
24
|
+
cache_backend: CacheBackend,
|
|
25
|
+
strategy_registry: "StrategyRegistry",
|
|
26
|
+
):
|
|
27
|
+
self.reference_id = reference_id
|
|
28
|
+
self.cache_backend = cache_backend
|
|
29
|
+
self._strategy_registry = strategy_registry
|
|
30
|
+
self._cached_entry: Optional[CacheEntry] = None
|
|
31
|
+
|
|
32
|
+
def get_full_result(self) -> Optional[Any]:
|
|
33
|
+
"""Get complete results"""
|
|
34
|
+
try:
|
|
35
|
+
if self._cached_entry is None:
|
|
36
|
+
self._cached_entry = self.cache_backend.get(self.reference_id)
|
|
37
|
+
|
|
38
|
+
if self._cached_entry:
|
|
39
|
+
return self._cached_entry.full_result
|
|
40
|
+
return None
|
|
41
|
+
except Exception as e:
|
|
42
|
+
logger.error(f"Failed to get full result for {self.reference_id}: {e}")
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
def get_metadata(self) -> Optional[Dict[str, Any]]:
|
|
46
|
+
"""Get metadata"""
|
|
47
|
+
try:
|
|
48
|
+
if self._cached_entry is None:
|
|
49
|
+
self._cached_entry = self.cache_backend.get(self.reference_id)
|
|
50
|
+
|
|
51
|
+
if self._cached_entry:
|
|
52
|
+
return self._cached_entry.metadata
|
|
53
|
+
return None
|
|
54
|
+
except Exception as e:
|
|
55
|
+
logger.error(f"Failed to get metadata for {self.reference_id}: {e}")
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
# === Unified Entry: Retrieval Based on Category ===
|
|
59
|
+
def get_for_category(
|
|
60
|
+
self, category: str, strategy_name: str = "default", **kwargs
|
|
61
|
+
) -> Optional[Any]:
|
|
62
|
+
"""Get processed data based on category (e.g., category='llm' or 'frontend')"""
|
|
63
|
+
try:
|
|
64
|
+
strategy = self._strategy_registry.get_strategy(strategy_name, category)
|
|
65
|
+
if strategy is None:
|
|
66
|
+
logger.warning(f"策略不存在: {category}:{strategy_name}")
|
|
67
|
+
return None
|
|
68
|
+
|
|
69
|
+
if not strategy.supports(category):
|
|
70
|
+
logger.warning(f"策略不支持 category: {category}")
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
return strategy.process(self, **kwargs)
|
|
74
|
+
except Exception as e:
|
|
75
|
+
logger.error(f"策略处理failed: {category}:{strategy_name}, 错误: {e}")
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
def get(self, strategy_spec: str = "", **kwargs) -> Any:
|
|
79
|
+
"""Unified acquisition: supports 'llm:summary' / 'app/pagination' / 'summary' (auto-detected)."""
|
|
80
|
+
if not strategy_spec:
|
|
81
|
+
# Channel not specified:优先尝试 llm,再尝试 app
|
|
82
|
+
result = self.get_for_category("llm", "default", **kwargs)
|
|
83
|
+
if result is not None:
|
|
84
|
+
return result
|
|
85
|
+
return self.get_for_category("app", strategy_name="default", **kwargs)
|
|
86
|
+
|
|
87
|
+
# Parsing strategy specifications
|
|
88
|
+
if ":" in strategy_spec:
|
|
89
|
+
category, name = strategy_spec.split(":", 1)
|
|
90
|
+
return self.get_for_category(category, name, **kwargs)
|
|
91
|
+
elif "/" in strategy_spec:
|
|
92
|
+
category, name = strategy_spec.split("/", 1)
|
|
93
|
+
return self.get_for_category(category, name, **kwargs)
|
|
94
|
+
else:
|
|
95
|
+
# Unknown channel: try common categories
|
|
96
|
+
for k in ("llm", "app"):
|
|
97
|
+
result = self.get_for_category(k, strategy_spec, **kwargs)
|
|
98
|
+
if result is not None:
|
|
99
|
+
return result
|
|
100
|
+
# Try another category
|
|
101
|
+
for k in self._strategy_registry.list_strategies().keys():
|
|
102
|
+
if k in ("llm", "app"):
|
|
103
|
+
continue
|
|
104
|
+
result = self.get_for_category(k, strategy_spec, **kwargs)
|
|
105
|
+
if result is not None:
|
|
106
|
+
return result
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
def exists(self) -> bool:
|
|
110
|
+
"""Check if reference exists"""
|
|
111
|
+
return self.cache_backend.exists(self.reference_id)
|
|
112
|
+
|
|
113
|
+
def get_info(self) -> Dict[str, Any]:
|
|
114
|
+
"""Get reference information"""
|
|
115
|
+
try:
|
|
116
|
+
if self._cached_entry is None:
|
|
117
|
+
self._cached_entry = self.cache_backend.get(self.reference_id)
|
|
118
|
+
|
|
119
|
+
if self._cached_entry:
|
|
120
|
+
return {
|
|
121
|
+
"reference_id": self.reference_id,
|
|
122
|
+
"tool_name": self._cached_entry.tool_name,
|
|
123
|
+
"size": self._cached_entry.size,
|
|
124
|
+
"created_at": self._cached_entry.created_at.isoformat(),
|
|
125
|
+
"metadata": self._cached_entry.metadata,
|
|
126
|
+
}
|
|
127
|
+
return {"reference_id": self.reference_id, "exists": False}
|
|
128
|
+
except Exception as e:
|
|
129
|
+
logger.error(f"Failed to get info for {self.reference_id}: {e}")
|
|
130
|
+
return {"reference_id": self.reference_id, "error": str(e)}
|
|
131
|
+
|
|
132
|
+
def delete(self) -> bool:
|
|
133
|
+
"""Delete reference"""
|
|
134
|
+
try:
|
|
135
|
+
success = self.cache_backend.delete(self.reference_id)
|
|
136
|
+
if success:
|
|
137
|
+
self._cached_entry = None
|
|
138
|
+
return success
|
|
139
|
+
except Exception as e:
|
|
140
|
+
logger.error(f"Failed to delete reference {self.reference_id}: {e}")
|
|
141
|
+
return False
|
|
142
|
+
|
|
143
|
+
def set_data(self, data: Any) -> None:
|
|
144
|
+
"""Set result data"""
|
|
145
|
+
self._data = data
|
|
146
|
+
|
|
147
|
+
def set_metadata(self, metadata: Dict[str, Any]) -> None:
|
|
148
|
+
"""Set metadata"""
|
|
149
|
+
self._metadata = metadata.copy()
|
|
150
|
+
|
|
151
|
+
def update_metadata(self, updates: Dict[str, Any]) -> None:
|
|
152
|
+
"""Update metadata"""
|
|
153
|
+
self._metadata.update(updates)
|
|
154
|
+
|
|
155
|
+
def is_expired(self) -> bool:
|
|
156
|
+
"""Check if expired"""
|
|
157
|
+
if self._cache_ttl is None:
|
|
158
|
+
return False
|
|
159
|
+
return datetime.now() - self._created_at > timedelta(seconds=self._cache_ttl)
|
|
160
|
+
|
|
161
|
+
def get_age(self) -> float:
|
|
162
|
+
"""Get data age (seconds)"""
|
|
163
|
+
return (datetime.now() - self._created_at).total_seconds()
|
|
164
|
+
|
|
165
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
166
|
+
"""Convert to dictionary"""
|
|
167
|
+
return {
|
|
168
|
+
"reference_id": self.reference_id,
|
|
169
|
+
"data": self._data,
|
|
170
|
+
"metadata": self._metadata,
|
|
171
|
+
"created_at": self._created_at.isoformat(),
|
|
172
|
+
"cache_ttl": self._cache_ttl,
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
def __str__(self) -> str:
|
|
176
|
+
return f"ResultReference(id={self.reference_id}, data_type={type(self._data).__name__})"
|
|
177
|
+
|
|
178
|
+
def __repr__(self) -> str:
|
|
179
|
+
return self.__str__()
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
"""SkillKit Hook Module
|
|
2
|
+
Provides hook functions and utility methods for processing skill results.
|
|
3
|
+
|
|
4
|
+
Main Features:
|
|
5
|
+
- Before returning to LLM: get_for_llm -> generates understandable data based on strategy
|
|
6
|
+
- Before returning to APP: get_for_app -> generates displayable data based on strategy
|
|
7
|
+
- Result caching: unified management of result lifecycle
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import Dict, Any, Optional
|
|
11
|
+
|
|
12
|
+
from dolphin.lib.skill_results.result_processor import ResultProcessor
|
|
13
|
+
from dolphin.lib.skill_results.result_reference import ResultReference
|
|
14
|
+
from dolphin.lib.skill_results.strategy_registry import StrategyRegistry
|
|
15
|
+
from dolphin.lib.skill_results.cache_backend import (
|
|
16
|
+
CacheBackend,
|
|
17
|
+
MemoryCacheBackend,
|
|
18
|
+
)
|
|
19
|
+
from dolphin.core.skill.skill_function import SkillFunction, DynamicAPISkillFunction
|
|
20
|
+
|
|
21
|
+
from dolphin.core.logging.logger import get_logger
|
|
22
|
+
|
|
23
|
+
logger = get_logger("skill_results")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class SkillkitHook:
|
|
27
|
+
"""Skillkit Result Processing Hooks.
|
|
28
|
+
|
|
29
|
+
- Provides a unified interface for result retrieval and processing
|
|
30
|
+
- Supports data transformation with different strategies
|
|
31
|
+
- Manages result caching and lifecycle
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
cache_backend: Optional[CacheBackend] = None,
|
|
37
|
+
strategy_registry: Optional[StrategyRegistry] = None,
|
|
38
|
+
) -> None:
|
|
39
|
+
self._processor = ResultProcessor(
|
|
40
|
+
cache_backend=cache_backend or MemoryCacheBackend(),
|
|
41
|
+
strategy_registry=strategy_registry or StrategyRegistry(),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# === Hook: After tool execution ===
|
|
45
|
+
def on_tool_after_execute(
|
|
46
|
+
self, tool_name: str, result: Any, metadata: Optional[Dict[str, Any]] = None
|
|
47
|
+
) -> ResultReference:
|
|
48
|
+
"""Alias: equivalent to process_result."""
|
|
49
|
+
return self.process_result(
|
|
50
|
+
tool_name=tool_name, result=result, metadata=metadata
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# === Hook: After tool execution ===
|
|
54
|
+
def process_result(
|
|
55
|
+
self,
|
|
56
|
+
tool_name: str,
|
|
57
|
+
result: Any,
|
|
58
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
59
|
+
) -> ResultReference:
|
|
60
|
+
"""Called after the tool execution is completed, saves the result and returns a reference.
|
|
61
|
+
The returned reference ID can be recorded in the context for subsequent frontend/LLM to retrieve data as needed.
|
|
62
|
+
"""
|
|
63
|
+
return self._processor.process_result(
|
|
64
|
+
tool_name=tool_name, result=result, metadata=metadata
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# === Hook: Before returning LLM ===
|
|
68
|
+
def on_before_send_to_llm(
|
|
69
|
+
self,
|
|
70
|
+
reference_id: str,
|
|
71
|
+
strategy_name: str = None,
|
|
72
|
+
skill: SkillFunction = None,
|
|
73
|
+
**kwargs,
|
|
74
|
+
) -> Optional[str]:
|
|
75
|
+
"""Alias: equivalent to get_for_llm. Uses the default strategy by default.
|
|
76
|
+
|
|
77
|
+
Special handling for dynamic tool responses:
|
|
78
|
+
- If the result contains _dynamic_tools marker, returns a user-friendly message
|
|
79
|
+
- Otherwise, processes the result using the normal strategy
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
# Check if this is a dynamic tool response
|
|
83
|
+
ref = self._get_result_reference(reference_id)
|
|
84
|
+
|
|
85
|
+
if ref:
|
|
86
|
+
full_result = ref.get_full_result()
|
|
87
|
+
|
|
88
|
+
if isinstance(full_result, dict):
|
|
89
|
+
# Check for _dynamic_tools at multiple levels
|
|
90
|
+
dynamic_tools = None
|
|
91
|
+
message = ""
|
|
92
|
+
|
|
93
|
+
# Level 1: Check if _dynamic_tools is directly in full_result
|
|
94
|
+
if "_dynamic_tools" in full_result:
|
|
95
|
+
dynamic_tools = full_result.get("_dynamic_tools", [])
|
|
96
|
+
message = full_result.get("message", "") or full_result.get("answer", "")
|
|
97
|
+
if isinstance(message, dict):
|
|
98
|
+
message = message.get("message", "") or message.get("answer", "")
|
|
99
|
+
|
|
100
|
+
# Level 2: Check if _dynamic_tools is in full_result["answer"]
|
|
101
|
+
elif "answer" in full_result:
|
|
102
|
+
answer = full_result["answer"]
|
|
103
|
+
if isinstance(answer, dict) and "_dynamic_tools" in answer:
|
|
104
|
+
dynamic_tools = answer.get("_dynamic_tools", [])
|
|
105
|
+
message = answer.get("message", "")
|
|
106
|
+
|
|
107
|
+
# If we found dynamic tools, process them
|
|
108
|
+
if dynamic_tools is not None:
|
|
109
|
+
tool_names = []
|
|
110
|
+
if isinstance(dynamic_tools, list):
|
|
111
|
+
for tool in dynamic_tools:
|
|
112
|
+
if isinstance(tool, dict) and "name" in tool:
|
|
113
|
+
tool_names.append(tool["name"])
|
|
114
|
+
|
|
115
|
+
# Build the response message
|
|
116
|
+
if not message and tool_names:
|
|
117
|
+
message = f"Successfully loaded {len(tool_names)} tools: {', '.join(tool_names)}"
|
|
118
|
+
elif not message:
|
|
119
|
+
message = "Successfully loaded dynamic tools"
|
|
120
|
+
|
|
121
|
+
logger.debug(
|
|
122
|
+
f"[Dynamic Tool] Returning for LLM - message: {message}, tools: {tool_names}"
|
|
123
|
+
)
|
|
124
|
+
return message
|
|
125
|
+
|
|
126
|
+
# Normal processing
|
|
127
|
+
if strategy_name is None and skill is not None:
|
|
128
|
+
strategy_name = skill.get_first_valid_llm_strategy()
|
|
129
|
+
|
|
130
|
+
strategy_name = strategy_name if strategy_name else "default"
|
|
131
|
+
|
|
132
|
+
return self.get_for_llm(
|
|
133
|
+
reference_id=reference_id, strategy_name=strategy_name, **kwargs
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
def on_before_reply_app(
|
|
137
|
+
self,
|
|
138
|
+
reference_id: str,
|
|
139
|
+
strategy_name: str = "default",
|
|
140
|
+
skill: SkillFunction = None,
|
|
141
|
+
**kwargs,
|
|
142
|
+
) -> Dict[str, Any]:
|
|
143
|
+
"""Alias: equivalent to get_for_app. Uses the default strategy by default.
|
|
144
|
+
|
|
145
|
+
Special handling for dynamic tool responses:
|
|
146
|
+
- If the result contains _dynamic_tools marker, returns structured data with message
|
|
147
|
+
- Otherwise, processes the result using the normal strategy
|
|
148
|
+
"""
|
|
149
|
+
# Check if this is a dynamic API tool response
|
|
150
|
+
ref = self._get_result_reference(reference_id)
|
|
151
|
+
|
|
152
|
+
if ref and skill and isinstance(skill, DynamicAPISkillFunction):
|
|
153
|
+
full_result = ref.get_full_result()
|
|
154
|
+
return {"answer": full_result}
|
|
155
|
+
|
|
156
|
+
# Normal processing
|
|
157
|
+
try:
|
|
158
|
+
strategy_name = skill.get_first_valid_app_strategy()
|
|
159
|
+
if not strategy_name:
|
|
160
|
+
return {"error": "未找到有效的APP策略"}
|
|
161
|
+
|
|
162
|
+
return self.get_for_app(reference_id, strategy_name=strategy_name, **kwargs)
|
|
163
|
+
except Exception as e:
|
|
164
|
+
logger.error(f"APP回复前处理failed: {e}")
|
|
165
|
+
return {"error": f"数据处理failed: {str(e)}"}
|
|
166
|
+
|
|
167
|
+
def on_before_send_to_context(
|
|
168
|
+
self,
|
|
169
|
+
reference_id: str,
|
|
170
|
+
skill: SkillFunction,
|
|
171
|
+
skillkit_name: str,
|
|
172
|
+
resource_skill_path: Optional[str] = None,
|
|
173
|
+
) -> tuple[str, Dict[str, Any]]:
|
|
174
|
+
"""Hook called before sending result to context (SCRATCHPAD message).
|
|
175
|
+
|
|
176
|
+
This hook applies context retention strategies to optimize how much
|
|
177
|
+
content is stored in the context window.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
reference_id: The result reference ID
|
|
181
|
+
skill: The executed skill function
|
|
182
|
+
skillkit_name: Name of the skillkit
|
|
183
|
+
resource_skill_path: Optional path to resource skill
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
tuple[str, dict]: (processed_content, metadata)
|
|
187
|
+
"""
|
|
188
|
+
from dolphin.core.skill.context_retention import (
|
|
189
|
+
SkillContextRetention,
|
|
190
|
+
get_context_retention_strategy,
|
|
191
|
+
ContextRetentionMode,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
ref = self._get_result_reference(reference_id)
|
|
195
|
+
if not ref:
|
|
196
|
+
return "", {}
|
|
197
|
+
|
|
198
|
+
full_result = ref.get_full_result()
|
|
199
|
+
if not isinstance(full_result, str):
|
|
200
|
+
full_result = str(full_result)
|
|
201
|
+
|
|
202
|
+
# Get decorator config or default
|
|
203
|
+
config = getattr(skill.func, '_context_retention', None)
|
|
204
|
+
if not config:
|
|
205
|
+
config = SkillContextRetention() # Default FULL mode
|
|
206
|
+
|
|
207
|
+
# Apply strategy
|
|
208
|
+
strategy = get_context_retention_strategy(config.mode)
|
|
209
|
+
processed_result = strategy.process(full_result, config, reference_id)
|
|
210
|
+
|
|
211
|
+
# Build metadata
|
|
212
|
+
metadata = {
|
|
213
|
+
"original_length": len(full_result),
|
|
214
|
+
"processed_length": len(processed_result),
|
|
215
|
+
"retention_mode": config.mode.value,
|
|
216
|
+
"pinned": config.mode == ContextRetentionMode.PIN,
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
# Use update() to merge with existing metadata
|
|
220
|
+
existing_metadata = ref.get_metadata()
|
|
221
|
+
if existing_metadata:
|
|
222
|
+
metadata.update(existing_metadata)
|
|
223
|
+
|
|
224
|
+
return processed_result, metadata
|
|
225
|
+
|
|
226
|
+
def get_for_llm(
|
|
227
|
+
self, reference_id: str, strategy_name: str = "default", **kwargs
|
|
228
|
+
) -> str:
|
|
229
|
+
"""Get data suitable for LLM
|
|
230
|
+
- Generate data in a format understandable by LLM according to the strategy
|
|
231
|
+
- Support strategies such as 'llm:summary', 'llm:truncate', etc.
|
|
232
|
+
"""
|
|
233
|
+
try:
|
|
234
|
+
ref = self._get_result_reference(reference_id)
|
|
235
|
+
if ref is None:
|
|
236
|
+
return "结果引用不存在"
|
|
237
|
+
|
|
238
|
+
return ref.get_for_category("llm", strategy_name, **kwargs)
|
|
239
|
+
except Exception as e:
|
|
240
|
+
logger.error(f"GetLLM数据failed: {e}")
|
|
241
|
+
return f"数据Getfailed: {str(e)}"
|
|
242
|
+
|
|
243
|
+
def get_for_app(
|
|
244
|
+
self, reference_id: str, strategy_name: str = "default", **kwargs
|
|
245
|
+
) -> Dict[str, Any]:
|
|
246
|
+
"""Get data suitable for APP
|
|
247
|
+
- Generate a displayable data format according to the strategy
|
|
248
|
+
- Support strategies such as 'app:pagination', 'app:preview', etc.
|
|
249
|
+
"""
|
|
250
|
+
try:
|
|
251
|
+
ref = self._get_result_reference(reference_id)
|
|
252
|
+
if ref is None:
|
|
253
|
+
return {"error": "结果引用不存在"}
|
|
254
|
+
|
|
255
|
+
return ref.get_for_category("app", strategy_name=strategy_name, **kwargs)
|
|
256
|
+
except Exception as e:
|
|
257
|
+
logger.error(f"GetAPP数据failed: {e}")
|
|
258
|
+
return {"error": f"数据Getfailed: {str(e)}"}
|
|
259
|
+
|
|
260
|
+
def get_for_category(
|
|
261
|
+
self, reference_id: str, category: str, strategy_name: str = "default", **kwargs
|
|
262
|
+
) -> Any:
|
|
263
|
+
"""Get data based on category
|
|
264
|
+
- Supports strategy processing for any category
|
|
265
|
+
- Provides unified error handling
|
|
266
|
+
"""
|
|
267
|
+
try:
|
|
268
|
+
ref = self._get_result_reference(reference_id)
|
|
269
|
+
if ref is None:
|
|
270
|
+
if category == "llm":
|
|
271
|
+
return "结果引用不存在"
|
|
272
|
+
else:
|
|
273
|
+
return {"error": "结果引用不存在"}
|
|
274
|
+
|
|
275
|
+
return ref.get_for_category(category, strategy_name=strategy_name, **kwargs)
|
|
276
|
+
except Exception as e:
|
|
277
|
+
logger.error(f"Get{category}数据failed: {e}")
|
|
278
|
+
if category == "llm":
|
|
279
|
+
return f"数据Getfailed: {str(e)}"
|
|
280
|
+
else:
|
|
281
|
+
return {"error": f"数据Getfailed: {str(e)}"}
|
|
282
|
+
|
|
283
|
+
def get_raw_result(self, reference_id: str) -> Any:
|
|
284
|
+
"""Get original result data (not processed by strategy)"""
|
|
285
|
+
try:
|
|
286
|
+
ref = self._get_result_reference(reference_id)
|
|
287
|
+
if ref is None:
|
|
288
|
+
return None
|
|
289
|
+
return ref.get_full_result()
|
|
290
|
+
except Exception as e:
|
|
291
|
+
logger.error(f"Get原始结果failed: {e}")
|
|
292
|
+
return None
|
|
293
|
+
|
|
294
|
+
def get_result_metadata(self, reference_id: str) -> Optional[Dict[str, Any]]:
|
|
295
|
+
"""Get result metadata"""
|
|
296
|
+
try:
|
|
297
|
+
ref = self._get_result_reference(reference_id)
|
|
298
|
+
if ref is None:
|
|
299
|
+
return None
|
|
300
|
+
return ref.get_metadata()
|
|
301
|
+
except Exception as e:
|
|
302
|
+
logger.error(f"Get结果元数据failed: {e}")
|
|
303
|
+
return None
|
|
304
|
+
|
|
305
|
+
def _get_result_reference(self, reference_id: str) -> Optional[ResultReference]:
|
|
306
|
+
"""Get result reference"""
|
|
307
|
+
try:
|
|
308
|
+
"""Get ResultReference by reference ID."""
|
|
309
|
+
return self._processor.get_result_reference(reference_id)
|
|
310
|
+
except Exception as e:
|
|
311
|
+
logger.error(f"Get结果引用failed: {e}")
|
|
312
|
+
return None
|
|
313
|
+
|
|
314
|
+
def delete_result(self, reference_id: str) -> bool:
|
|
315
|
+
"""Delete the cached result of the specified reference."""
|
|
316
|
+
return self._processor.delete_result(reference_id)
|
|
317
|
+
|
|
318
|
+
def cleanup_expired(self, max_age_hours: int = 24) -> int:
|
|
319
|
+
"""Clean up expired cache and return the number of items cleaned."""
|
|
320
|
+
return self._processor.cleanup_expired(max_age_hours=max_age_hours)
|
|
321
|
+
|
|
322
|
+
def get_stats(self) -> Dict[str, Any]:
|
|
323
|
+
"""Get the statistics information of the cache and policy system."""
|
|
324
|
+
return self._processor.get_stats()
|