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,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()