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,624 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import asyncio
|
|
3
|
+
import re
|
|
4
|
+
import traceback
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from dolphin.core import flags
|
|
8
|
+
from dolphin.core.code_block.assign_block import AssignBlock
|
|
9
|
+
from dolphin.core.logging.logger import get_logger
|
|
10
|
+
from dolphin.core.code_block.explore_block import ExploreBlock
|
|
11
|
+
from dolphin.core.code_block.explore_block_v2 import ExploreBlockV2
|
|
12
|
+
from dolphin.core.code_block.judge_block import JudgeBlock
|
|
13
|
+
from dolphin.core.code_block.prompt_block import PromptBlock
|
|
14
|
+
from dolphin.core.code_block.tool_block import ToolBlock
|
|
15
|
+
from dolphin.core.common.enums import StreamItem, count_occurrences
|
|
16
|
+
from dolphin.core.common.constants import KEY_STATUS, KEY_PREVIOUS_STATUS
|
|
17
|
+
from dolphin.core.context.context import Context
|
|
18
|
+
from dolphin.core.parser.parser import Parser
|
|
19
|
+
from dolphin.core.utils.tools import ToolInterrupt
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def split_by_multiple_prefixes(text, prefixes):
|
|
23
|
+
# Initialize result list
|
|
24
|
+
result = []
|
|
25
|
+
# Current processing position
|
|
26
|
+
current_pos = 0
|
|
27
|
+
|
|
28
|
+
while current_pos < len(text):
|
|
29
|
+
# Find the next closest delimiter position
|
|
30
|
+
next_pos = len(text)
|
|
31
|
+
found_prefix = None
|
|
32
|
+
|
|
33
|
+
# Find all possible delimiters after the current position
|
|
34
|
+
for prefix in prefixes:
|
|
35
|
+
pos = text.find(prefix, current_pos + 1) # +1 is to avoid repeated lookups at the current position
|
|
36
|
+
if pos != -1 and pos < next_pos:
|
|
37
|
+
next_pos = pos
|
|
38
|
+
found_prefix = prefix
|
|
39
|
+
|
|
40
|
+
# If the separator is found
|
|
41
|
+
if found_prefix:
|
|
42
|
+
# Add the current paragraph to the result.
|
|
43
|
+
segment = text[current_pos:next_pos]
|
|
44
|
+
if segment: # Add only non-empty segments
|
|
45
|
+
result.append(segment)
|
|
46
|
+
current_pos = next_pos
|
|
47
|
+
else:
|
|
48
|
+
# If no more delimiters are found, add the remaining part.
|
|
49
|
+
segment = text[current_pos:]
|
|
50
|
+
if segment: # Add only non-empty paragraphs
|
|
51
|
+
result.append(segment)
|
|
52
|
+
break
|
|
53
|
+
|
|
54
|
+
return result
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def split_and_join(string_list, int_list):
|
|
58
|
+
result = []
|
|
59
|
+
# Traverse split points
|
|
60
|
+
for i in range(len(int_list)):
|
|
61
|
+
start = int_list[i]
|
|
62
|
+
# If it is the last split point, end equals the list length.
|
|
63
|
+
if i == len(int_list) - 1:
|
|
64
|
+
end = len(string_list)
|
|
65
|
+
else:
|
|
66
|
+
end = int_list[i + 1]
|
|
67
|
+
# Concatenate the strings within this range.
|
|
68
|
+
joined_str = "".join(string_list[start:end])
|
|
69
|
+
result.append(joined_str)
|
|
70
|
+
return result
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class Executor:
|
|
74
|
+
def __init__(
|
|
75
|
+
self,
|
|
76
|
+
context: Context,
|
|
77
|
+
debug_info: Optional[dict] = None,
|
|
78
|
+
breakpoint_infos: Optional[dict] = None,
|
|
79
|
+
step_mode: bool = False,
|
|
80
|
+
debug_mode: bool = False,
|
|
81
|
+
break_on_start: bool = False,
|
|
82
|
+
break_at: Optional[list] = None,
|
|
83
|
+
):
|
|
84
|
+
self.context = context
|
|
85
|
+
self.debug_info = debug_info
|
|
86
|
+
self.breakpoint_infos = breakpoint_infos
|
|
87
|
+
self.step_mode = step_mode # Deprecated: kept for backward compatibility
|
|
88
|
+
self.debug_mode = debug_mode # Added: Whether to enable debug mode
|
|
89
|
+
self.debug_controller = None
|
|
90
|
+
|
|
91
|
+
# If debug mode is enabled, initialize the debug controller.
|
|
92
|
+
if self.debug_mode:
|
|
93
|
+
from dolphin.core.executor.debug_controller import DebugController
|
|
94
|
+
|
|
95
|
+
self.debug_controller = DebugController(
|
|
96
|
+
self.context,
|
|
97
|
+
break_on_start=break_on_start,
|
|
98
|
+
break_at=break_at,
|
|
99
|
+
)
|
|
100
|
+
self.debug_controller.enable_step_mode()
|
|
101
|
+
|
|
102
|
+
self.logger = get_logger("executor")
|
|
103
|
+
|
|
104
|
+
# Trajectory configuration: read from context attribute
|
|
105
|
+
# If trajectorypath is specified, stage trajectory saving is automatically enabled
|
|
106
|
+
self.trajectory_path = getattr(context, "trajectorypath", None)
|
|
107
|
+
self.agent_name = getattr(context, "agent_name", "main")
|
|
108
|
+
|
|
109
|
+
# Initialize trajectory recording
|
|
110
|
+
if self.trajectory_path:
|
|
111
|
+
self.context.init_trajectory(self.trajectory_path)
|
|
112
|
+
|
|
113
|
+
self.parser = Parser(context=self.context)
|
|
114
|
+
self.tool_block = ToolBlock(context=self.context)
|
|
115
|
+
|
|
116
|
+
self.explore_block = (
|
|
117
|
+
ExploreBlockV2(context=self.context)
|
|
118
|
+
if flags.is_enabled(flags.EXPLORE_BLOCK_V2)
|
|
119
|
+
else ExploreBlock(context=self.context)
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
self.judge_block = JudgeBlock(context=self.context)
|
|
123
|
+
self.prompt_block = PromptBlock(context=self.context)
|
|
124
|
+
self.assign_block = AssignBlock(context=self.context)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _increment_stage_counter(self, stage_name: str):
|
|
128
|
+
"""Only increment counters for the specified stage, without saving trajectories.
|
|
129
|
+
|
|
130
|
+
Used for Blocks that need to manage trajectory saving themselves (e.g., ExploreBlock)
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
stage_name: Name of the stage, such as 'judge', 'tool', 'explore', etc.
|
|
134
|
+
"""
|
|
135
|
+
status = self.context.get_var_value(KEY_STATUS)
|
|
136
|
+
|
|
137
|
+
# Initialize status dict if None
|
|
138
|
+
if status is None:
|
|
139
|
+
status = {}
|
|
140
|
+
|
|
141
|
+
counter_key = f"{stage_name}_time"
|
|
142
|
+
|
|
143
|
+
# Initialize counter if it doesn't exist
|
|
144
|
+
if counter_key not in status:
|
|
145
|
+
status[counter_key] = 0
|
|
146
|
+
|
|
147
|
+
status[counter_key] += 1
|
|
148
|
+
self.context.set_variable(KEY_STATUS, status)
|
|
149
|
+
|
|
150
|
+
def _save_stage_trajectory(self, stage_name: str):
|
|
151
|
+
"""Save stage trajectory to trajectory file
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
stage_name: Stage name, such as 'judge', 'tool', 'explore', etc.
|
|
155
|
+
"""
|
|
156
|
+
if not self.context.trajectory:
|
|
157
|
+
return
|
|
158
|
+
|
|
159
|
+
try:
|
|
160
|
+
status = self.context.get_var_value(KEY_STATUS) or {}
|
|
161
|
+
counter_key = f"{stage_name}_time"
|
|
162
|
+
stage_index = status.get(counter_key, 0)
|
|
163
|
+
|
|
164
|
+
tools = self.context.skillkit.getSkillsSchema()
|
|
165
|
+
|
|
166
|
+
# Get model name from context (set by ExploreBlock during llm_chat_stream)
|
|
167
|
+
# Returns None if no model has been used yet in this session
|
|
168
|
+
current_model = self.context.get_last_model_name()
|
|
169
|
+
|
|
170
|
+
self.context.trajectory.finalize_stage(
|
|
171
|
+
stage_name=stage_name,
|
|
172
|
+
stage_index=stage_index,
|
|
173
|
+
context_manager=self.context.context_manager,
|
|
174
|
+
tools=tools,
|
|
175
|
+
user_id=self.context.user_id or "",
|
|
176
|
+
model=current_model,
|
|
177
|
+
)
|
|
178
|
+
self.logger.debug(f"Saved stage trajectory for {stage_name} (index: {stage_index})")
|
|
179
|
+
except Exception as e:
|
|
180
|
+
# Record errors without interrupting execution
|
|
181
|
+
self.logger.warning(f"Failed to save stage trajectory for {stage_name}: {e}", exc_info=True)
|
|
182
|
+
|
|
183
|
+
def _increment_and_save_stage(self, stage_name: str):
|
|
184
|
+
"""Increment the counter for the specified stage and save the stage trajectory.
|
|
185
|
+
|
|
186
|
+
This is a convenient method that combines counter incrementing and trajectory saving.
|
|
187
|
+
For Blocks that need to manage trajectory themselves (such as ExploreBlock),
|
|
188
|
+
they can call _increment_stage_counter() only.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
stage_name: Name of the stage, such as 'judge', 'tool', 'explore', etc.
|
|
192
|
+
"""
|
|
193
|
+
self._increment_stage_counter(stage_name)
|
|
194
|
+
self._save_stage_trajectory(stage_name)
|
|
195
|
+
|
|
196
|
+
async def blocks_act(self, blocks):
|
|
197
|
+
for block_index, action_block in enumerate(blocks):
|
|
198
|
+
# Debug mode: check whether pause is needed
|
|
199
|
+
if self.debug_controller and self.debug_controller.should_pause_at_block(
|
|
200
|
+
block_index
|
|
201
|
+
):
|
|
202
|
+
if not await self.debug_controller.pause_and_wait_for_input(
|
|
203
|
+
block_index, action_block
|
|
204
|
+
):
|
|
205
|
+
# User exits debugging, stops execution
|
|
206
|
+
return
|
|
207
|
+
previous_status = self.context.get_var_value(KEY_PREVIOUS_STATUS)
|
|
208
|
+
status = self.context.get_var_value(KEY_STATUS)
|
|
209
|
+
|
|
210
|
+
# Check if status variables are None and reinitialize if needed
|
|
211
|
+
if status is None:
|
|
212
|
+
status = {
|
|
213
|
+
"tool_time": 0,
|
|
214
|
+
"judge_time": 0,
|
|
215
|
+
"prompt_time": 0,
|
|
216
|
+
"explore_time": 0,
|
|
217
|
+
"assign_time": 0,
|
|
218
|
+
}
|
|
219
|
+
self.context.set_variable(KEY_STATUS, status)
|
|
220
|
+
else:
|
|
221
|
+
# Ensure all required keys exist in status dict
|
|
222
|
+
required_keys = ["tool_time", "judge_time", "prompt_time", "explore_time", "assign_time"]
|
|
223
|
+
for key in required_keys:
|
|
224
|
+
if key not in status:
|
|
225
|
+
status[key] = 0
|
|
226
|
+
self.context.set_variable(KEY_STATUS, status)
|
|
227
|
+
|
|
228
|
+
if previous_status is None:
|
|
229
|
+
previous_status = {
|
|
230
|
+
"tool_time": 0,
|
|
231
|
+
"judge_time": 0,
|
|
232
|
+
"prompt_time": 0,
|
|
233
|
+
"explore_time": 0,
|
|
234
|
+
"assign_time": 0,
|
|
235
|
+
}
|
|
236
|
+
self.context.set_variable(KEY_PREVIOUS_STATUS, previous_status)
|
|
237
|
+
else:
|
|
238
|
+
# Ensure all required keys exist in previous_status dict
|
|
239
|
+
required_keys = ["tool_time", "judge_time", "prompt_time", "explore_time", "assign_time"]
|
|
240
|
+
for key in required_keys:
|
|
241
|
+
if key not in previous_status:
|
|
242
|
+
previous_status[key] = 0
|
|
243
|
+
self.context.set_variable(KEY_PREVIOUS_STATUS, previous_status)
|
|
244
|
+
|
|
245
|
+
if action_block[0] == "if":
|
|
246
|
+
async for resp in self.ifelse_block(action_block[1]):
|
|
247
|
+
yield resp
|
|
248
|
+
elif action_block[0] == "for":
|
|
249
|
+
async for resp in self.for_block(action_block[1]):
|
|
250
|
+
yield resp
|
|
251
|
+
elif action_block[0] == "judge":
|
|
252
|
+
if (
|
|
253
|
+
self.step_mode
|
|
254
|
+
or status["judge_time"] >= previous_status["judge_time"]
|
|
255
|
+
):
|
|
256
|
+
async for resp in self.judge_block.execute(action_block[1]):
|
|
257
|
+
yield resp
|
|
258
|
+
self._increment_and_save_stage("judge")
|
|
259
|
+
elif action_block[0] == "tool":
|
|
260
|
+
if (
|
|
261
|
+
self.step_mode
|
|
262
|
+
or status["tool_time"] >= previous_status["tool_time"]
|
|
263
|
+
):
|
|
264
|
+
async for resp in self.tool_block.execute(action_block[1]):
|
|
265
|
+
yield resp
|
|
266
|
+
self._increment_and_save_stage("tool")
|
|
267
|
+
elif action_block[0] == "explore":
|
|
268
|
+
if (
|
|
269
|
+
self.step_mode
|
|
270
|
+
or status["explore_time"] >= previous_status["explore_time"]
|
|
271
|
+
):
|
|
272
|
+
async for resp in self.explore_block.execute(action_block[1]):
|
|
273
|
+
yield resp
|
|
274
|
+
# ExploreBlock saves trajectory itself in _update_history_and_cleanup()
|
|
275
|
+
# Here we only increment the counter to avoid saving repeatedly
|
|
276
|
+
self._increment_stage_counter("explore")
|
|
277
|
+
elif action_block[0] == "prompt":
|
|
278
|
+
if (
|
|
279
|
+
self.step_mode
|
|
280
|
+
or status["prompt_time"] >= previous_status["prompt_time"]
|
|
281
|
+
):
|
|
282
|
+
async for resp in self.prompt_block.execute(action_block[1]):
|
|
283
|
+
yield resp
|
|
284
|
+
self._increment_and_save_stage("prompt")
|
|
285
|
+
elif action_block[0] == "assign":
|
|
286
|
+
if (
|
|
287
|
+
self.step_mode
|
|
288
|
+
or status["assign_time"] >= previous_status["assign_time"]
|
|
289
|
+
):
|
|
290
|
+
async for resp in self.assign_block.execute(action_block[1]):
|
|
291
|
+
yield resp
|
|
292
|
+
self._increment_and_save_stage("assign")
|
|
293
|
+
elif action_block[0] == "parallel":
|
|
294
|
+
async for resp in self.parallel_block(action_block[1]):
|
|
295
|
+
yield resp
|
|
296
|
+
|
|
297
|
+
async def ifelse_block(self, content):
|
|
298
|
+
pre = ["/if/", "elif", "/for/", "/parallel/", "else", "/end/"]
|
|
299
|
+
split_result = split_by_multiple_prefixes(content, pre)
|
|
300
|
+
count = 1
|
|
301
|
+
elif_list = []
|
|
302
|
+
else_list = []
|
|
303
|
+
for i in range(1, len(split_result)):
|
|
304
|
+
if split_result[i].startswith("/if/"):
|
|
305
|
+
count += 1
|
|
306
|
+
elif split_result[i].startswith("elif"):
|
|
307
|
+
if count == 1:
|
|
308
|
+
elif_list.append(i)
|
|
309
|
+
elif split_result[i].startswith("else"):
|
|
310
|
+
if count == 1:
|
|
311
|
+
else_list.append(i)
|
|
312
|
+
elif split_result[i].startswith("/end/"):
|
|
313
|
+
count -= 1
|
|
314
|
+
elif split_result[i].startswith("/for/"):
|
|
315
|
+
count += 1
|
|
316
|
+
elif split_result[i].startswith("/parallel/"):
|
|
317
|
+
count += 1
|
|
318
|
+
join_list = [0] + elif_list + else_list
|
|
319
|
+
join_str_list = split_and_join(split_result, join_list)
|
|
320
|
+
join_str_list[-1] = join_str_list[-1][:-5]
|
|
321
|
+
ifelse_list = []
|
|
322
|
+
try:
|
|
323
|
+
for i in range(len(join_str_list)):
|
|
324
|
+
join_str = join_str_list[i]
|
|
325
|
+
condition, action = join_str_list[i].split(":", 1)
|
|
326
|
+
condition = condition.strip()
|
|
327
|
+
action = action.strip()
|
|
328
|
+
ifelse_list.append((condition, action))
|
|
329
|
+
except Exception:
|
|
330
|
+
raise Exception(
|
|
331
|
+
f"Syntax Error({content}),check the '/if/','elif','else','/end/'"
|
|
332
|
+
)
|
|
333
|
+
variables = self.context.get_all_variables_values()
|
|
334
|
+
for condition, action in ifelse_list:
|
|
335
|
+
if condition[:4] == "/if/" or condition[:4] == "elif":
|
|
336
|
+
result = eval(condition[4:].replace("$", ""), globals(), variables)
|
|
337
|
+
if result:
|
|
338
|
+
action_blocks = self.parser.parse(self, action)
|
|
339
|
+
async for resp in self.blocks_act(action_blocks):
|
|
340
|
+
yield resp
|
|
341
|
+
break
|
|
342
|
+
else:
|
|
343
|
+
action_blocks = self.parser.parse(self, action)
|
|
344
|
+
async for resp in self.blocks_act(action_blocks):
|
|
345
|
+
yield resp
|
|
346
|
+
|
|
347
|
+
async def for_block(self, content):
|
|
348
|
+
content = content[:-5]
|
|
349
|
+
pattern = r"/for/\s*\$(\s*[^ ]+)\s*in\s*\$(\s*[^:]+)\s*:"
|
|
350
|
+
|
|
351
|
+
match = re.search(pattern, content)
|
|
352
|
+
if match:
|
|
353
|
+
var_temp_name = match.group(1).strip()
|
|
354
|
+
var_loop_name = match.group(2).strip()
|
|
355
|
+
loop_obj = self.context.get_var_value(var_loop_name)
|
|
356
|
+
|
|
357
|
+
try:
|
|
358
|
+
# Compatibility: allow looping over prompt/skill result objects
|
|
359
|
+
if isinstance(loop_obj, list):
|
|
360
|
+
lst = loop_obj
|
|
361
|
+
elif isinstance(loop_obj, StreamItem):
|
|
362
|
+
if isinstance(loop_obj.output_var_value, list):
|
|
363
|
+
lst = loop_obj.output_var_value
|
|
364
|
+
elif isinstance(loop_obj.answer, str) and loop_obj.answer.startswith("[") and loop_obj.answer.endswith("]"):
|
|
365
|
+
lst = ast.literal_eval(loop_obj.answer)
|
|
366
|
+
else:
|
|
367
|
+
raise TypeError("Variable is not a list.")
|
|
368
|
+
elif isinstance(loop_obj, dict):
|
|
369
|
+
# Duck-typing compatibility for prompt/skill result dicts:
|
|
370
|
+
# accept only if it carries an iterable result payload.
|
|
371
|
+
if isinstance(loop_obj.get("output_var_value"), list):
|
|
372
|
+
lst = loop_obj.get("output_var_value")
|
|
373
|
+
elif isinstance(loop_obj.get("answer"), list):
|
|
374
|
+
lst = loop_obj.get("answer")
|
|
375
|
+
elif (
|
|
376
|
+
isinstance(loop_obj.get("answer"), str)
|
|
377
|
+
and loop_obj.get("answer").startswith("[")
|
|
378
|
+
and loop_obj.get("answer").endswith("]")
|
|
379
|
+
):
|
|
380
|
+
lst = ast.literal_eval(loop_obj.get("answer"))
|
|
381
|
+
else:
|
|
382
|
+
raise TypeError("Variable is not a list.")
|
|
383
|
+
elif (
|
|
384
|
+
isinstance(loop_obj, str)
|
|
385
|
+
and loop_obj.startswith("[")
|
|
386
|
+
and loop_obj.endswith("]")
|
|
387
|
+
):
|
|
388
|
+
lst = ast.literal_eval(loop_obj)
|
|
389
|
+
else:
|
|
390
|
+
raise TypeError("Variable is not a list.")
|
|
391
|
+
except Exception as e:
|
|
392
|
+
raise TypeError(
|
|
393
|
+
f"Syntax Error for loop variable ${var_loop_name}({loop_obj}), error: {e}"
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
for i, element in enumerate(lst):
|
|
397
|
+
self.context.set_variable(var_temp_name, element)
|
|
398
|
+
action_blocks = self.parser.parse(
|
|
399
|
+
self, ":".join(content.split(":")[1:])
|
|
400
|
+
)
|
|
401
|
+
async for resp in self.blocks_act(action_blocks):
|
|
402
|
+
yield resp
|
|
403
|
+
|
|
404
|
+
async def parallel_block(self, content):
|
|
405
|
+
content = content[10:-5]
|
|
406
|
+
action_blocks = self.parser.parse(self, content)
|
|
407
|
+
|
|
408
|
+
# Create a list of asynchronous generator objects
|
|
409
|
+
tasks = []
|
|
410
|
+
for i in range(len(action_blocks)):
|
|
411
|
+
tasks.append(("task" + str(i + 1), self.blocks_act([action_blocks[i]])))
|
|
412
|
+
active_generators = list(tasks)
|
|
413
|
+
|
|
414
|
+
# Loop until all generators are complete
|
|
415
|
+
while active_generators:
|
|
416
|
+
# Create the task for the current iteration
|
|
417
|
+
current_tasks = []
|
|
418
|
+
|
|
419
|
+
for name, gen in active_generators:
|
|
420
|
+
# Create a task to get the next value for each generator
|
|
421
|
+
current_tasks.append((name, asyncio.create_task(gen.__anext__())))
|
|
422
|
+
|
|
423
|
+
# Wait for all tasks to complete the current iteration
|
|
424
|
+
await asyncio.sleep(0)
|
|
425
|
+
|
|
426
|
+
# Process results and update the active generator list
|
|
427
|
+
next_active = []
|
|
428
|
+
for (name, gen), (_, task_obj) in zip(active_generators, current_tasks):
|
|
429
|
+
try:
|
|
430
|
+
result = await task_obj
|
|
431
|
+
if result is not None:
|
|
432
|
+
yield ""
|
|
433
|
+
next_active.append((name, gen))
|
|
434
|
+
else:
|
|
435
|
+
# Generator has completed
|
|
436
|
+
yield ""
|
|
437
|
+
except StopAsyncIteration:
|
|
438
|
+
# Generator has completed
|
|
439
|
+
yield ""
|
|
440
|
+
|
|
441
|
+
active_generators = next_active
|
|
442
|
+
yield ""
|
|
443
|
+
yield ""
|
|
444
|
+
|
|
445
|
+
def _preparation(self, content):
|
|
446
|
+
# remove comment
|
|
447
|
+
uncommented_content = self.parser.remove_comment(content)
|
|
448
|
+
# Split the string into a list by lines
|
|
449
|
+
lines = uncommented_content.splitlines()
|
|
450
|
+
|
|
451
|
+
# Filter out lines that start with 'import'
|
|
452
|
+
filtered_lines = [
|
|
453
|
+
line.lstrip() for line in lines if not line.strip().startswith("import")
|
|
454
|
+
]
|
|
455
|
+
|
|
456
|
+
# Reconstruct the filtered lines into a string
|
|
457
|
+
content_new = "\n".join(filtered_lines)
|
|
458
|
+
# Split only the outermost blocks
|
|
459
|
+
num0 = count_occurrences(["/if/", "/for/", "/parallel"], content_new)
|
|
460
|
+
num1 = count_occurrences(["/end/"], content_new)
|
|
461
|
+
if num0 != num1:
|
|
462
|
+
raise Exception(
|
|
463
|
+
f"Syntax Error({content}),check the '/if/','/for/','/end/'"
|
|
464
|
+
)
|
|
465
|
+
# State Initialization
|
|
466
|
+
# Check if this is an interrupt recovery scenario by looking for intervention markers
|
|
467
|
+
is_interrupt_recovery = (
|
|
468
|
+
self.context.get_var_value("intervention_tool_block_vars") is not None
|
|
469
|
+
or self.context.get_var_value("intervention_judge_block_vars") is not None
|
|
470
|
+
or self.context.get_var_value("intervention_explore_block_vars") is not None
|
|
471
|
+
) and self.context.get_var_value("tool") is not None
|
|
472
|
+
|
|
473
|
+
if self.context.get_var_value(KEY_STATUS):
|
|
474
|
+
previous_status = self.context.get_var_value(KEY_STATUS)
|
|
475
|
+
if isinstance(previous_status, dict) and all(
|
|
476
|
+
key in previous_status
|
|
477
|
+
for key in [
|
|
478
|
+
"tool_time",
|
|
479
|
+
"judge_time",
|
|
480
|
+
"prompt_time",
|
|
481
|
+
"explore_time",
|
|
482
|
+
"assign_time",
|
|
483
|
+
]
|
|
484
|
+
):
|
|
485
|
+
if is_interrupt_recovery:
|
|
486
|
+
# In interrupt recovery scenario, keep status unchanged
|
|
487
|
+
# and set previous_status to 0 so all blocks will execute
|
|
488
|
+
self.context.set_variable(
|
|
489
|
+
KEY_PREVIOUS_STATUS,
|
|
490
|
+
{
|
|
491
|
+
"tool_time": 0,
|
|
492
|
+
"judge_time": 0,
|
|
493
|
+
"prompt_time": 0,
|
|
494
|
+
"explore_time": 0,
|
|
495
|
+
"assign_time": 0,
|
|
496
|
+
},
|
|
497
|
+
)
|
|
498
|
+
# Keep the existing status (don't reset it)
|
|
499
|
+
else:
|
|
500
|
+
# Normal scenario: save current status as previous_status
|
|
501
|
+
self.context.set_variable(KEY_PREVIOUS_STATUS, previous_status)
|
|
502
|
+
# Reset status to 0 for new execution
|
|
503
|
+
self.context.set_variable(
|
|
504
|
+
KEY_STATUS,
|
|
505
|
+
{
|
|
506
|
+
"tool_time": 0,
|
|
507
|
+
"judge_time": 0,
|
|
508
|
+
"prompt_time": 0,
|
|
509
|
+
"explore_time": 0,
|
|
510
|
+
"assign_time": 0,
|
|
511
|
+
},
|
|
512
|
+
)
|
|
513
|
+
else:
|
|
514
|
+
self.context.set_variable(
|
|
515
|
+
KEY_PREVIOUS_STATUS,
|
|
516
|
+
{
|
|
517
|
+
"tool_time": 0,
|
|
518
|
+
"judge_time": 0,
|
|
519
|
+
"prompt_time": 0,
|
|
520
|
+
"explore_time": 0,
|
|
521
|
+
"assign_time": 0,
|
|
522
|
+
},
|
|
523
|
+
)
|
|
524
|
+
self.context.set_variable(
|
|
525
|
+
KEY_STATUS,
|
|
526
|
+
{
|
|
527
|
+
"tool_time": 0,
|
|
528
|
+
"judge_time": 0,
|
|
529
|
+
"prompt_time": 0,
|
|
530
|
+
"explore_time": 0,
|
|
531
|
+
"assign_time": 0,
|
|
532
|
+
},
|
|
533
|
+
)
|
|
534
|
+
else:
|
|
535
|
+
self.context.set_variable(
|
|
536
|
+
KEY_PREVIOUS_STATUS,
|
|
537
|
+
{
|
|
538
|
+
"tool_time": 0,
|
|
539
|
+
"judge_time": 0,
|
|
540
|
+
"prompt_time": 0,
|
|
541
|
+
"explore_time": 0,
|
|
542
|
+
"assign_time": 0,
|
|
543
|
+
},
|
|
544
|
+
)
|
|
545
|
+
self.context.set_variable(
|
|
546
|
+
KEY_STATUS,
|
|
547
|
+
{
|
|
548
|
+
"tool_time": 0,
|
|
549
|
+
"judge_time": 0,
|
|
550
|
+
"prompt_time": 0,
|
|
551
|
+
"explore_time": 0,
|
|
552
|
+
"assign_time": 0,
|
|
553
|
+
},
|
|
554
|
+
)
|
|
555
|
+
|
|
556
|
+
return self.parser.parse(self, content_new)
|
|
557
|
+
|
|
558
|
+
def _clean_up(self):
|
|
559
|
+
self.context.delete_variable(KEY_PREVIOUS_STATUS)
|
|
560
|
+
self.context.delete_variable(KEY_STATUS)
|
|
561
|
+
|
|
562
|
+
async def run(self, content, output_variables: list[str] = [], **kwargs):
|
|
563
|
+
blocks = self._preparation(content)
|
|
564
|
+
# Precompute output_variables conditions to avoid checking them in each iteration
|
|
565
|
+
should_filter = output_variables is not None and len(output_variables) > 0
|
|
566
|
+
|
|
567
|
+
try:
|
|
568
|
+
# Execute all blocks, blocks_act will generate the result of each block and return it streamingly.
|
|
569
|
+
async for resp in self.blocks_act(blocks):
|
|
570
|
+
# Return the result based on the precomputed condition
|
|
571
|
+
if should_filter:
|
|
572
|
+
# Return only the specified variables using the efficient get_variables method
|
|
573
|
+
filtered_variables = self.context.get_variables_values(
|
|
574
|
+
output_variables
|
|
575
|
+
)
|
|
576
|
+
yield filtered_variables
|
|
577
|
+
else:
|
|
578
|
+
# Return all variables
|
|
579
|
+
yield self.context.get_all_variables()
|
|
580
|
+
except ToolInterrupt:
|
|
581
|
+
# Similarly return the filtered variables when interrupted
|
|
582
|
+
if should_filter:
|
|
583
|
+
filtered_variables = self.context.get_variables_values(output_variables)
|
|
584
|
+
yield filtered_variables
|
|
585
|
+
else:
|
|
586
|
+
yield self.context.get_all_variables()
|
|
587
|
+
raise
|
|
588
|
+
finally:
|
|
589
|
+
self._clean_up()
|
|
590
|
+
|
|
591
|
+
async def run_and_get_result(self, content, **kwargs):
|
|
592
|
+
blocks = self._preparation(content)
|
|
593
|
+
try:
|
|
594
|
+
async for resp in self.blocks_act(blocks):
|
|
595
|
+
yield resp
|
|
596
|
+
except ToolInterrupt:
|
|
597
|
+
raise Exception(f"ToolInterrupt traceback[{traceback.format_exc()}]")
|
|
598
|
+
except Exception as e:
|
|
599
|
+
raise Exception(
|
|
600
|
+
f"Tool block execution failed: {str(e)} traceback[{traceback.format_exc()}]"
|
|
601
|
+
)
|
|
602
|
+
finally:
|
|
603
|
+
self._clean_up()
|
|
604
|
+
|
|
605
|
+
async def run_step(self, blocks, block_pointer: int):
|
|
606
|
+
"""Execute a single step, return (new pointer position, whether completed)"""
|
|
607
|
+
|
|
608
|
+
if block_pointer >= len(blocks):
|
|
609
|
+
yield (block_pointer, True)
|
|
610
|
+
return
|
|
611
|
+
|
|
612
|
+
# Execute the current block
|
|
613
|
+
current_block = blocks[block_pointer]
|
|
614
|
+
async for result in self.blocks_act([current_block]):
|
|
615
|
+
yield result
|
|
616
|
+
|
|
617
|
+
# Return step completion information
|
|
618
|
+
new_pointer = block_pointer + 1
|
|
619
|
+
is_complete = new_pointer >= len(blocks)
|
|
620
|
+
yield (new_pointer, is_complete)
|
|
621
|
+
|
|
622
|
+
def get_parsed_blocks(self, content: str):
|
|
623
|
+
"""Get parsed blocks (cache mechanism)"""
|
|
624
|
+
return self._preparation(content)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Flags facade: Only responsible for exporting APIs and constants"""
|
|
2
|
+
|
|
3
|
+
from .manager import (
|
|
4
|
+
is_enabled,
|
|
5
|
+
override,
|
|
6
|
+
reset,
|
|
7
|
+
get_all,
|
|
8
|
+
set_flag,
|
|
9
|
+
) # Functional API
|
|
10
|
+
from .definitions import (
|
|
11
|
+
EXPLORE_BLOCK_V2,
|
|
12
|
+
DEBUG_MODE,
|
|
13
|
+
DISABLE_LLM_CACHE,
|
|
14
|
+
ENABLE_PARALLEL_TOOL_CALLS,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"is_enabled",
|
|
19
|
+
"override",
|
|
20
|
+
"reset",
|
|
21
|
+
"get_all",
|
|
22
|
+
"set_flag",
|
|
23
|
+
"EXPLORE_BLOCK_V2",
|
|
24
|
+
"DEBUG_MODE",
|
|
25
|
+
"DISABLE_LLM_CACHE",
|
|
26
|
+
"ENABLE_PARALLEL_TOOL_CALLS",
|
|
27
|
+
]
|