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,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)}")
|