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,380 @@
1
+ """Hook Dispatcher Module
2
+
3
+ This module provides the core Hook dispatching logic.
4
+
5
+ Key Responsibilities:
6
+ - Dispatch to different handler types (expression, agent)
7
+ - Build evaluation context
8
+ - Standardize HookResult
9
+ - Handle errors gracefully
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import asyncio
15
+ from dataclasses import asdict
16
+ from typing import Any, Dict, Optional, TYPE_CHECKING
17
+
18
+ from dolphin.core.hook.hook_types import (
19
+ HookConfig,
20
+ HookResult,
21
+ HookError,
22
+ HookValidationError,
23
+ HookContextProtocol,
24
+ AgentRef,
25
+ )
26
+ from dolphin.core.hook.expression_evaluator import ExpressionEvaluator, ExpressionError
27
+ from dolphin.core.hook.isolated_variable_pool import IsolatedVariablePool
28
+ from dolphin.core.logging.logger import get_logger
29
+
30
+ if TYPE_CHECKING:
31
+ from dolphin.core.context.variable_pool import VariablePool
32
+
33
+ logger = get_logger("hook.dispatcher")
34
+
35
+
36
+ class FeedbackGenerator:
37
+ """Generate feedback messages based on verification results."""
38
+
39
+ def __init__(
40
+ self,
41
+ handler: str,
42
+ score: float,
43
+ threshold: float,
44
+ ):
45
+ self.handler = handler
46
+ self.score = score
47
+ self.threshold = threshold
48
+
49
+ def generate(self) -> str:
50
+ """Generate feedback message for failed verification.
51
+
52
+ Returns:
53
+ Formatted feedback string
54
+ """
55
+ feedback_parts = [
56
+ f"[Score: {self.score:.2f}, Target: {self.threshold:.2f}]",
57
+ "Please improve your answer based on the following feedback:",
58
+ ]
59
+
60
+ # Analyze expression to provide specific feedback
61
+ if isinstance(self.handler, str):
62
+ specific_feedback = self._analyze_expression()
63
+ if specific_feedback:
64
+ feedback_parts.append(specific_feedback)
65
+
66
+ return "\n".join(feedback_parts)
67
+
68
+ def _analyze_expression(self) -> str:
69
+ """Analyze expression to provide specific feedback."""
70
+ handler = self.handler
71
+
72
+ if 'len($answer)' in handler or 'len(answer)' in handler:
73
+ return "- Your answer may be too short. Please provide more detailed content."
74
+
75
+ if '$tool_calls' in handler or 'tool_calls' in handler:
76
+ return "- Please use tools to gather real data instead of making assumptions."
77
+
78
+ if '$steps' in handler or 'steps' in handler:
79
+ return "- Please show more reasoning steps in your analysis."
80
+
81
+ return ""
82
+
83
+
84
+ class HookDispatcher:
85
+ """Hook Dispatcher - handles different types of Hook handlers.
86
+
87
+ Supports:
88
+ - Expression handlers: Evaluate Python-like expressions
89
+ - Agent handlers: Execute verification agents (.dph files)
90
+
91
+ Example:
92
+ ```python
93
+ dispatcher = HookDispatcher(
94
+ config=HookConfig(handler="len($answer) > 100", threshold=0.7),
95
+ context=OnStopContext(answer="Hello", attempt=1),
96
+ variable_pool=context.variable_pool
97
+ )
98
+ result = await dispatcher.dispatch()
99
+ ```
100
+ """
101
+
102
+ def __init__(
103
+ self,
104
+ config: HookConfig,
105
+ context: HookContextProtocol,
106
+ variable_pool: VariablePool,
107
+ runtime: Any = None,
108
+ ):
109
+ """Initialize dispatcher.
110
+
111
+ Args:
112
+ config: Hook configuration
113
+ context: Hook context (OnStopContext for on_stop hooks)
114
+ variable_pool: Variable pool for variable access
115
+ runtime: Optional runtime for agent execution
116
+ """
117
+ self.config = config
118
+ self.context = context
119
+ self.variable_pool = variable_pool
120
+ self.runtime = runtime
121
+
122
+ async def dispatch(self) -> HookResult:
123
+ """Dispatch to appropriate handler and return standardized result.
124
+
125
+ Returns:
126
+ HookResult with score, passed status, and optional feedback
127
+
128
+ Raises:
129
+ HookValidationError: For configuration errors (should stop execution)
130
+ """
131
+ handler = self.config.handler
132
+
133
+ try:
134
+ # 1. Determine handler type and execute
135
+ if isinstance(handler, str):
136
+ # String expression
137
+ score = await self._eval_expression(handler)
138
+ return self._build_result(score)
139
+
140
+ elif isinstance(handler, AgentRef):
141
+ # Agent reference - execute verification agent
142
+ return await self._call_agent(handler)
143
+
144
+ else:
145
+ raise HookValidationError(f"Unknown handler type: {type(handler)}")
146
+
147
+ except asyncio.TimeoutError as e:
148
+ # Timeout error
149
+ logger.error(f"Hook handler timeout: {handler}")
150
+ return HookResult(
151
+ score=0.0,
152
+ passed=False,
153
+ feedback=None,
154
+ retry=False, # Don't retry on system error
155
+ error=f"Handler execution timeout after {self.config.agent_timeout}s",
156
+ error_type=HookError.TIMEOUT,
157
+ execution_status="timeout",
158
+ )
159
+
160
+ except ExpressionError as e:
161
+ # Expression syntax/evaluation error - raise to stop execution
162
+ raise HookValidationError(
163
+ f"Invalid hook expression: {handler}",
164
+ original_error=e,
165
+ ) from e
166
+
167
+ except HookValidationError:
168
+ # Re-raise validation errors
169
+ raise
170
+
171
+ except FileNotFoundError as e:
172
+ # Agent file not found - raise to stop execution
173
+ raise HookValidationError(
174
+ f"Verifier agent not found: {handler}",
175
+ original_error=e,
176
+ ) from e
177
+
178
+ except Exception as e:
179
+ # Other errors - degrade gracefully
180
+ logger.error(
181
+ f"Hook handler failed: {handler}",
182
+ exc_info=True,
183
+ )
184
+ return HookResult(
185
+ score=0.0,
186
+ passed=False,
187
+ feedback=None,
188
+ retry=False, # Don't retry on validator error
189
+ error=str(e),
190
+ error_type=HookError.VALIDATOR_ERROR,
191
+ execution_status="validator_error",
192
+ )
193
+
194
+ async def _eval_expression(self, expr: str) -> float:
195
+ """Evaluate expression handler.
196
+
197
+ Args:
198
+ expr: Expression string
199
+
200
+ Returns:
201
+ Score between 0.0 and 1.0
202
+ """
203
+ eval_context = self._build_eval_context()
204
+ evaluator = ExpressionEvaluator(
205
+ expr=expr,
206
+ context=eval_context,
207
+ model=self.config.model,
208
+ )
209
+ return await evaluator.evaluate()
210
+
211
+ async def _call_agent(self, agent_ref: AgentRef) -> HookResult:
212
+ """Execute verification agent.
213
+
214
+ Args:
215
+ agent_ref: Reference to the verification agent
216
+
217
+ Returns:
218
+ HookResult from agent execution
219
+ """
220
+ if self.runtime is None:
221
+ raise HookValidationError(
222
+ "Runtime is required for agent-based verification"
223
+ )
224
+
225
+ try:
226
+ # Load .dph file
227
+ agent = await self.runtime.load_agent(agent_ref.path)
228
+ except FileNotFoundError:
229
+ raise # Re-raise for dispatch() to handle
230
+ except Exception as e:
231
+ raise HookValidationError(
232
+ f"Failed to load verifier agent: {agent_ref.path}"
233
+ ) from e
234
+
235
+ try:
236
+ # Create isolated variable pool
237
+ isolated_pool = self._create_isolated_variable_pool()
238
+
239
+ # Inject hook context
240
+ isolated_pool.set('_hook_context', asdict(self.context))
241
+
242
+ # Set agent's variable pool
243
+ agent.variable_pool = isolated_pool
244
+
245
+ # Execute agent with timeout
246
+ result = await asyncio.wait_for(
247
+ agent.execute(),
248
+ timeout=self.config.agent_timeout,
249
+ )
250
+
251
+ # Parse agent result
252
+ return self._parse_agent_result(result)
253
+
254
+ except asyncio.TimeoutError:
255
+ raise # Re-raise for dispatch() to handle
256
+
257
+ except Exception as e:
258
+ logger.error(
259
+ f"Verifier agent {agent_ref.path} failed",
260
+ exc_info=True,
261
+ )
262
+ raise
263
+
264
+ finally:
265
+ # Cleanup agent resources
266
+ if 'agent' in dir() and hasattr(agent, 'cleanup'):
267
+ await agent.cleanup()
268
+
269
+ def _create_isolated_variable_pool(self) -> IsolatedVariablePool:
270
+ """Create isolated variable pool for agent execution.
271
+
272
+ Returns:
273
+ IsolatedVariablePool with read-only access
274
+ """
275
+ exposed_vars = self.config.exposed_variables
276
+
277
+ return IsolatedVariablePool(
278
+ parent=self.variable_pool,
279
+ read_only=True,
280
+ exposed_variables=exposed_vars if exposed_vars else None,
281
+ )
282
+
283
+ def _build_eval_context(self) -> Dict[str, Any]:
284
+ """Build context dictionary for expression evaluation.
285
+
286
+ Returns:
287
+ Dictionary with context fields accessible in expressions
288
+ """
289
+ # Get context as dict
290
+ ctx_dict = self.context.to_dict()
291
+
292
+ # Build evaluation context
293
+ eval_ctx = {
294
+ 'answer': ctx_dict.get('answer', ''),
295
+ 'think': ctx_dict.get('think', ''),
296
+ 'steps': ctx_dict.get('steps', 0),
297
+ 'tool_calls': len(ctx_dict.get('tool_calls', [])),
298
+ 'attempt': ctx_dict.get('attempt', 1),
299
+ }
300
+
301
+ return eval_ctx
302
+
303
+ def _build_result(self, score: float) -> HookResult:
304
+ """Build standardized HookResult from score.
305
+
306
+ Args:
307
+ score: Evaluation score (0.0 to 1.0)
308
+
309
+ Returns:
310
+ HookResult with passed status and optional feedback
311
+ """
312
+ passed = score >= self.config.threshold
313
+
314
+ # Generate feedback if not passed
315
+ feedback = None
316
+ if not passed:
317
+ generator = FeedbackGenerator(
318
+ handler=self.config.handler if isinstance(self.config.handler, str) else str(self.config.handler),
319
+ score=score,
320
+ threshold=self.config.threshold,
321
+ )
322
+ feedback = generator.generate()
323
+
324
+ return HookResult(
325
+ score=score,
326
+ passed=passed,
327
+ feedback=feedback,
328
+ retry=not passed,
329
+ breakdown=None,
330
+ )
331
+
332
+ def _parse_agent_result(self, result: Any) -> HookResult:
333
+ """Parse verification agent result into HookResult.
334
+
335
+ Supports:
336
+ - Full HookResult dict: {"score": 0.85, "passed": true, ...}
337
+ - Simple numeric: 0.85
338
+
339
+ Args:
340
+ result: Agent execution result
341
+
342
+ Returns:
343
+ Parsed HookResult
344
+ """
345
+ # Handle dict result
346
+ if isinstance(result, dict):
347
+ # Check for score field
348
+ if 'score' in result:
349
+ score = float(result['score'])
350
+ passed = result.get('passed', score >= self.config.threshold)
351
+
352
+ return HookResult(
353
+ score=score,
354
+ passed=passed,
355
+ feedback=result.get('feedback'),
356
+ retry=result.get('retry', not passed),
357
+ breakdown=result.get('breakdown'),
358
+ )
359
+
360
+ # Check for nested answer structure (from explore block)
361
+ if 'answer' in result:
362
+ answer = result['answer']
363
+ if isinstance(answer, dict) and 'score' in answer:
364
+ return self._parse_agent_result(answer)
365
+
366
+ # Handle numeric result
367
+ if isinstance(result, (int, float)):
368
+ return self._build_result(float(result))
369
+
370
+ # Invalid result format
371
+ logger.warning(f"Invalid agent result format: {type(result)}")
372
+ return HookResult(
373
+ score=0.0,
374
+ passed=False,
375
+ feedback="Verification agent returned invalid result format",
376
+ retry=False,
377
+ error="Invalid result format",
378
+ error_type=HookError.INVALID_RESULT,
379
+ execution_status="validator_error",
380
+ )
@@ -0,0 +1,248 @@
1
+ """Hook Types Module
2
+
3
+ This module defines the data structures and protocols for the Hook-based verify system.
4
+
5
+ Key Components:
6
+ - HookConfig: Configuration for Hook behavior
7
+ - OnStopContext: Context passed to on_stop Hook handlers
8
+ - HookResult: Standardized result from Hook execution
9
+ - HookContextProtocol: Protocol for extensibility
10
+ - AgentRef: Reference to a verification agent (.dph file)
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from dataclasses import dataclass, field, asdict
16
+ from typing import Optional, List, Dict, Any, Union, Protocol, runtime_checkable
17
+
18
+
19
+ @runtime_checkable
20
+ class HookContextProtocol(Protocol):
21
+ """Protocol for Hook Context, enabling future extensibility.
22
+
23
+ Any Hook Context (on_stop, on_start, on_error, etc.) must satisfy this protocol.
24
+ """
25
+ attempt: int
26
+ stage: str
27
+
28
+ def to_dict(self) -> Dict[str, Any]:
29
+ """Convert context to dictionary representation."""
30
+ ...
31
+
32
+
33
+ @dataclass
34
+ class AgentRef:
35
+ """Reference to a verification agent (.dph file).
36
+
37
+ Attributes:
38
+ path: Path to the .dph file (without extension)
39
+ """
40
+ path: str
41
+
42
+ def __post_init__(self):
43
+ # Remove @ prefix if present
44
+ if self.path.startswith('@'):
45
+ self.path = self.path[1:]
46
+ # Add .dph extension if not present
47
+ if not self.path.endswith('.dph'):
48
+ self.path = f"{self.path}.dph"
49
+
50
+
51
+ @dataclass
52
+ class HookConfig:
53
+ """Configuration for Hook behavior.
54
+
55
+ Attributes:
56
+ handler: The handler for the Hook - can be an expression string or AgentRef
57
+ threshold: Score threshold for passing (0.0 to 1.0)
58
+ max_retries: Maximum number of retry attempts
59
+ model: Optional model to use for verification
60
+ context: Optional context configuration (e.g., exposed_variables)
61
+ llm_timeout: Timeout for LLM calls in seconds
62
+ agent_timeout: Timeout for agent execution in seconds
63
+ """
64
+ handler: Union[str, AgentRef]
65
+ threshold: float = 0.5
66
+ max_retries: int = 0
67
+ model: Optional[str] = None
68
+ context: Optional[Dict[str, Any]] = None
69
+ llm_timeout: int = 30
70
+ agent_timeout: int = 60
71
+
72
+ def __post_init__(self):
73
+ # Validate threshold range
74
+ if not 0.0 <= self.threshold <= 1.0:
75
+ raise ValueError(f"threshold must be between 0.0 and 1.0, got {self.threshold}")
76
+
77
+ # Validate max_retries is non-negative
78
+ if self.max_retries < 0:
79
+ raise ValueError(f"max_retries must be non-negative, got {self.max_retries}")
80
+
81
+ # Limit max_retries to prevent infinite loops
82
+ if self.max_retries > 10:
83
+ raise ValueError(f"max_retries cannot exceed 10, got {self.max_retries}")
84
+
85
+ @property
86
+ def exposed_variables(self) -> List[str]:
87
+ """Get list of exposed variables from context config."""
88
+ if self.context and 'exposed_variables' in self.context:
89
+ return self.context['exposed_variables']
90
+ return []
91
+
92
+
93
+ @dataclass
94
+ class OnStopContext:
95
+ """Context for on_stop Hook - passed to handlers.
96
+
97
+ This is the specialized context for on_stop hooks, containing
98
+ all information about the explore block execution.
99
+
100
+ Attributes:
101
+ attempt: Current attempt number (1-indexed)
102
+ stage: Execution stage (always "explore" for on_stop)
103
+ answer: The generated answer from explore
104
+ think: The thinking/reasoning process
105
+ steps: Number of execution steps
106
+ tool_calls: List of tool calls made during exploration
107
+ """
108
+ # Common fields (satisfies HookContextProtocol)
109
+ attempt: int
110
+ stage: str = "explore"
111
+
112
+ # on_stop specific fields
113
+ answer: str = ""
114
+ think: str = ""
115
+ steps: int = 0
116
+ tool_calls: List[Dict[str, Any]] = field(default_factory=list)
117
+
118
+ def to_dict(self) -> Dict[str, Any]:
119
+ """Convert context to dictionary representation."""
120
+ return asdict(self)
121
+
122
+
123
+ @dataclass
124
+ class HookResult:
125
+ """Result from Hook execution.
126
+
127
+ Attributes:
128
+ score: Quality score between 0.0 and 1.0
129
+ passed: Whether the verification passed (score >= threshold)
130
+ feedback: Optional improvement suggestions
131
+ retry: Whether to retry (defaults to not passed)
132
+ breakdown: Optional score breakdown for debugging
133
+ error: Error message if validation failed
134
+ error_type: Type of error that occurred
135
+ execution_status: Overall execution status
136
+ """
137
+ score: float
138
+ passed: bool
139
+ feedback: Optional[str] = None
140
+ retry: bool = True
141
+ breakdown: Optional[Dict[str, Any]] = None
142
+
143
+ # Error handling fields
144
+ error: Optional[str] = None
145
+ error_type: Optional[str] = None
146
+ execution_status: str = "success"
147
+
148
+ def __post_init__(self):
149
+ # Normalize score to 0-1 range
150
+ self.score = max(0.0, min(1.0, self.score))
151
+
152
+ # Default retry to not passed
153
+ if self.retry is None:
154
+ self.retry = not self.passed
155
+
156
+ def to_dict(self) -> Dict[str, Any]:
157
+ """Convert result to dictionary representation."""
158
+ result = {
159
+ 'score': self.score,
160
+ 'passed': self.passed,
161
+ 'retry': self.retry,
162
+ 'execution_status': self.execution_status,
163
+ }
164
+ if self.feedback:
165
+ result['feedback'] = self.feedback
166
+ if self.breakdown:
167
+ result['breakdown'] = self.breakdown
168
+ if self.error:
169
+ result['error'] = self.error
170
+ result['error_type'] = self.error_type
171
+ return result
172
+
173
+
174
+ class HookError:
175
+ """Hook error type constants."""
176
+ VALIDATOR_ERROR = "validator_error"
177
+ TIMEOUT = "timeout"
178
+ INVALID_RESULT = "invalid_result"
179
+ EXPRESSION_ERROR = "expression_error"
180
+ AGENT_LOAD_ERROR = "agent_load_error"
181
+
182
+
183
+ class HookValidationError(Exception):
184
+ """Exception raised when hook validation configuration is invalid.
185
+
186
+ This error is raised for configuration errors that should stop execution,
187
+ not for validation failures during runtime.
188
+ """
189
+ def __init__(self, message: str, original_error: Optional[Exception] = None):
190
+ super().__init__(message)
191
+ self.original_error = original_error
192
+
193
+
194
+ def parse_hook_config(on_stop_value: Any) -> Optional[HookConfig]:
195
+ """Parse on_stop parameter value into HookConfig.
196
+
197
+ Supports multiple formats:
198
+ - None: No hook configured
199
+ - str: Simple expression handler
200
+ - dict with 'handler' key: Full configuration
201
+ - AgentRef: Agent reference
202
+
203
+ Args:
204
+ on_stop_value: The raw on_stop parameter value
205
+
206
+ Returns:
207
+ HookConfig if configured, None otherwise
208
+
209
+ Raises:
210
+ HookValidationError: If configuration is invalid
211
+ """
212
+ if on_stop_value is None:
213
+ return None
214
+
215
+ # Simple expression string
216
+ if isinstance(on_stop_value, str):
217
+ # Check if it's an agent reference
218
+ if on_stop_value.startswith('@'):
219
+ return HookConfig(handler=AgentRef(on_stop_value))
220
+ return HookConfig(handler=on_stop_value)
221
+
222
+ # AgentRef object
223
+ if isinstance(on_stop_value, AgentRef):
224
+ return HookConfig(handler=on_stop_value)
225
+
226
+ # Full configuration dictionary
227
+ if isinstance(on_stop_value, dict):
228
+ handler = on_stop_value.get('handler')
229
+ if handler is None:
230
+ raise HookValidationError("HookConfig requires 'handler' field")
231
+
232
+ # Parse handler
233
+ if isinstance(handler, str) and handler.startswith('@'):
234
+ handler = AgentRef(handler)
235
+ elif isinstance(handler, dict) and 'path' in handler:
236
+ handler = AgentRef(handler['path'])
237
+
238
+ return HookConfig(
239
+ handler=handler,
240
+ threshold=on_stop_value.get('threshold', 0.5),
241
+ max_retries=on_stop_value.get('max_retries', 0),
242
+ model=on_stop_value.get('model'),
243
+ context=on_stop_value.get('context'),
244
+ llm_timeout=on_stop_value.get('llm_timeout', 30),
245
+ agent_timeout=on_stop_value.get('agent_timeout', 60),
246
+ )
247
+
248
+ raise HookValidationError(f"Invalid on_stop configuration type: {type(on_stop_value)}")