zenus-core 0.6.1__tar.gz
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.
- zenus_core-0.6.1/PKG-INFO +46 -0
- zenus_core-0.6.1/README.md +11 -0
- zenus_core-0.6.1/pyproject.toml +40 -0
- zenus_core-0.6.1/src/zenus_core/__init__.py +12 -0
- zenus_core-0.6.1/src/zenus_core/audit/__init__.py +0 -0
- zenus_core-0.6.1/src/zenus_core/audit/logger.py +116 -0
- zenus_core-0.6.1/src/zenus_core/brain/adaptive_planner.py +207 -0
- zenus_core-0.6.1/src/zenus_core/brain/dependency_analyzer.py +269 -0
- zenus_core-0.6.1/src/zenus_core/brain/failure_analyzer.py +422 -0
- zenus_core-0.6.1/src/zenus_core/brain/goal_inference.py +574 -0
- zenus_core-0.6.1/src/zenus_core/brain/goal_tracker.py +214 -0
- zenus_core-0.6.1/src/zenus_core/brain/llm/anthropic_llm.py +264 -0
- zenus_core-0.6.1/src/zenus_core/brain/llm/base.py +53 -0
- zenus_core-0.6.1/src/zenus_core/brain/llm/deepseek_llm.py +199 -0
- zenus_core-0.6.1/src/zenus_core/brain/llm/factory.py +165 -0
- zenus_core-0.6.1/src/zenus_core/brain/llm/ollama_llm.py +204 -0
- zenus_core-0.6.1/src/zenus_core/brain/llm/openai_llm.py +179 -0
- zenus_core-0.6.1/src/zenus_core/brain/llm/schemas.py +18 -0
- zenus_core-0.6.1/src/zenus_core/brain/llm/system_prompt.py +138 -0
- zenus_core-0.6.1/src/zenus_core/brain/model_router.py +471 -0
- zenus_core-0.6.1/src/zenus_core/brain/multi_agent.py +527 -0
- zenus_core-0.6.1/src/zenus_core/brain/pattern_detector.py +321 -0
- zenus_core-0.6.1/src/zenus_core/brain/pattern_memory.py +75 -0
- zenus_core-0.6.1/src/zenus_core/brain/planner.py +160 -0
- zenus_core-0.6.1/src/zenus_core/brain/prompt_evolution.py +505 -0
- zenus_core-0.6.1/src/zenus_core/brain/provider_override.py +132 -0
- zenus_core-0.6.1/src/zenus_core/brain/sandboxed_planner.py +108 -0
- zenus_core-0.6.1/src/zenus_core/brain/self_reflection.py +395 -0
- zenus_core-0.6.1/src/zenus_core/brain/suggestion_engine.py +350 -0
- zenus_core-0.6.1/src/zenus_core/brain/task_analyzer.py +200 -0
- zenus_core-0.6.1/src/zenus_core/brain/task_complexity.py +190 -0
- zenus_core-0.6.1/src/zenus_core/brain/tree_of_thoughts.py +401 -0
- zenus_core-0.6.1/src/zenus_core/config/__init__.py +25 -0
- zenus_core-0.6.1/src/zenus_core/config/loader.py +321 -0
- zenus_core-0.6.1/src/zenus_core/config/schema.py +163 -0
- zenus_core-0.6.1/src/zenus_core/config/secrets.py +182 -0
- zenus_core-0.6.1/src/zenus_core/context/__init__.py +9 -0
- zenus_core-0.6.1/src/zenus_core/context/context_manager.py +345 -0
- zenus_core-0.6.1/src/zenus_core/error/__init__.py +69 -0
- zenus_core-0.6.1/src/zenus_core/error/circuit_breaker.py +205 -0
- zenus_core-0.6.1/src/zenus_core/error/fallback.py +226 -0
- zenus_core-0.6.1/src/zenus_core/error/retry_budget.py +180 -0
- zenus_core-0.6.1/src/zenus_core/execution/__init__.py +9 -0
- zenus_core-0.6.1/src/zenus_core/execution/error_handler.py +301 -0
- zenus_core-0.6.1/src/zenus_core/execution/error_recovery.py +325 -0
- zenus_core-0.6.1/src/zenus_core/execution/intent_cache.py +311 -0
- zenus_core-0.6.1/src/zenus_core/execution/parallel_executor.py +332 -0
- zenus_core-0.6.1/src/zenus_core/execution/smart_cache.py +357 -0
- zenus_core-0.6.1/src/zenus_core/feedback/__init__.py +5 -0
- zenus_core-0.6.1/src/zenus_core/feedback/collector.py +375 -0
- zenus_core-0.6.1/src/zenus_core/memory/__init__.py +0 -0
- zenus_core-0.6.1/src/zenus_core/memory/action_tracker.py +539 -0
- zenus_core-0.6.1/src/zenus_core/memory/failure_logger.py +439 -0
- zenus_core-0.6.1/src/zenus_core/memory/intent_history.py +168 -0
- zenus_core-0.6.1/src/zenus_core/memory/semantic_search.py +204 -0
- zenus_core-0.6.1/src/zenus_core/memory/session_memory.py +105 -0
- zenus_core-0.6.1/src/zenus_core/memory/world_model.py +164 -0
- zenus_core-0.6.1/src/zenus_core/monitoring/__init__.py +21 -0
- zenus_core-0.6.1/src/zenus_core/monitoring/proactive_monitor.py +623 -0
- zenus_core-0.6.1/src/zenus_core/observability/__init__.py +5 -0
- zenus_core-0.6.1/src/zenus_core/observability/metrics.py +304 -0
- zenus_core-0.6.1/src/zenus_core/orchestrator.py +1277 -0
- zenus_core-0.6.1/src/zenus_core/output/__init__.py +20 -0
- zenus_core-0.6.1/src/zenus_core/output/console.py +246 -0
- zenus_core-0.6.1/src/zenus_core/output/formatter.py +331 -0
- zenus_core-0.6.1/src/zenus_core/output/progress.py +276 -0
- zenus_core-0.6.1/src/zenus_core/output/streaming.py +191 -0
- zenus_core-0.6.1/src/zenus_core/rollback.py +476 -0
- zenus_core-0.6.1/src/zenus_core/safety/policy.py +34 -0
- zenus_core-0.6.1/src/zenus_core/sandbox/__init__.py +21 -0
- zenus_core-0.6.1/src/zenus_core/sandbox/constraints.py +192 -0
- zenus_core-0.6.1/src/zenus_core/sandbox/executor.py +173 -0
- zenus_core-0.6.1/src/zenus_core/sandbox/tool_wrapper.py +79 -0
- zenus_core-0.6.1/src/zenus_core/shell/__init__.py +0 -0
- zenus_core-0.6.1/src/zenus_core/shell/commands.py +580 -0
- zenus_core-0.6.1/src/zenus_core/shell/enhanced_shell.py +230 -0
- zenus_core-0.6.1/src/zenus_core/shell/explain.py +669 -0
- zenus_core-0.6.1/src/zenus_core/shell/response_generator.py +69 -0
- zenus_core-0.6.1/src/zenus_core/shell/shell_helpers.py +53 -0
- zenus_core-0.6.1/src/zenus_core/tools/base.py +8 -0
- zenus_core-0.6.1/src/zenus_core/tools/browser_ops.py +345 -0
- zenus_core-0.6.1/src/zenus_core/tools/code_exec.py +160 -0
- zenus_core-0.6.1/src/zenus_core/tools/container_ops.py +149 -0
- zenus_core-0.6.1/src/zenus_core/tools/file_ops.py +71 -0
- zenus_core-0.6.1/src/zenus_core/tools/git_ops.py +402 -0
- zenus_core-0.6.1/src/zenus_core/tools/network_ops.py +225 -0
- zenus_core-0.6.1/src/zenus_core/tools/package_ops.py +261 -0
- zenus_core-0.6.1/src/zenus_core/tools/privilege.py +45 -0
- zenus_core-0.6.1/src/zenus_core/tools/process_ops.py +68 -0
- zenus_core-0.6.1/src/zenus_core/tools/registry.py +124 -0
- zenus_core-0.6.1/src/zenus_core/tools/service_ops.py +98 -0
- zenus_core-0.6.1/src/zenus_core/tools/shell_executor.py +166 -0
- zenus_core-0.6.1/src/zenus_core/tools/shell_ops.py +115 -0
- zenus_core-0.6.1/src/zenus_core/tools/system_ops.py +199 -0
- zenus_core-0.6.1/src/zenus_core/tools/text_ops.py +122 -0
- zenus_core-0.6.1/src/zenus_core/tools/vision_ops.py +364 -0
- zenus_core-0.6.1/src/zenus_core/visualization/__init__.py +40 -0
- zenus_core-0.6.1/src/zenus_core/visualization/chart_generator.py +255 -0
- zenus_core-0.6.1/src/zenus_core/visualization/diff_viewer.py +267 -0
- zenus_core-0.6.1/src/zenus_core/visualization/table_formatter.py +276 -0
- zenus_core-0.6.1/src/zenus_core/visualization/visualizer.py +242 -0
- zenus_core-0.6.1/src/zenus_core/workflows/__init__.py +7 -0
- zenus_core-0.6.1/src/zenus_core/workflows/recorder.py +333 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: zenus-core
|
|
3
|
+
Version: 0.6.1
|
|
4
|
+
Summary: Zenus Core - Intent Execution Engine
|
|
5
|
+
License: MIT
|
|
6
|
+
Author: Zenus Team
|
|
7
|
+
Requires-Python: >=3.10,<4.0
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
|
+
Provides-Extra: all
|
|
16
|
+
Provides-Extra: browser
|
|
17
|
+
Requires-Dist: anthropic (>=0.47.0,<0.48.0)
|
|
18
|
+
Requires-Dist: matplotlib (>=3.8.0,<4.0.0)
|
|
19
|
+
Requires-Dist: numpy (>=1.24.0,<2.0.0)
|
|
20
|
+
Requires-Dist: openai (>=1.0.0,<2.0.0)
|
|
21
|
+
Requires-Dist: pillow (>=12.1.1,<13.0.0)
|
|
22
|
+
Requires-Dist: playwright (>=1.40.0,<2.0.0) ; extra == "browser" or extra == "all"
|
|
23
|
+
Requires-Dist: prompt-toolkit (>=3.0.52,<4.0.0)
|
|
24
|
+
Requires-Dist: psutil (>=5.9.0,<6.0.0)
|
|
25
|
+
Requires-Dist: pyautogui (>=0.9.54,<0.10.0)
|
|
26
|
+
Requires-Dist: pydantic (>=2.0.0,<3.0.0)
|
|
27
|
+
Requires-Dist: python-dotenv (>=1.0.0,<2.0.0)
|
|
28
|
+
Requires-Dist: pyyaml (>=6.0.0,<7.0.0)
|
|
29
|
+
Requires-Dist: requests (>=2.31.0,<3.0.0)
|
|
30
|
+
Requires-Dist: rich (>=13.7.0,<14.0.0)
|
|
31
|
+
Requires-Dist: watchdog (>=3.0.0,<4.0.0)
|
|
32
|
+
Requires-Dist: zenus-visualization (>=0.6.1,<0.7.0)
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# Zenus Core
|
|
36
|
+
|
|
37
|
+
The brain of Zenus - intelligent intent execution engine.
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install zenus-core
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
See main [Zenus documentation](../../README.md) for details.
|
|
46
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "zenus-core"
|
|
3
|
+
version = "0.6.1"
|
|
4
|
+
description = "Zenus Core - Intent Execution Engine"
|
|
5
|
+
authors = ["Zenus Team"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
packages = [{include = "zenus_core", from = "src"}]
|
|
9
|
+
|
|
10
|
+
[tool.poetry.dependencies]
|
|
11
|
+
python = "^3.10"
|
|
12
|
+
pydantic = "^2.0.0"
|
|
13
|
+
python-dotenv = "^1.0.0"
|
|
14
|
+
pyyaml = "^6.0.0"
|
|
15
|
+
watchdog = "^3.0.0"
|
|
16
|
+
requests = "^2.31.0"
|
|
17
|
+
openai = "^1.0.0"
|
|
18
|
+
anthropic = "^0.47.0"
|
|
19
|
+
rich = "^13.7.0"
|
|
20
|
+
psutil = "^5.9.0"
|
|
21
|
+
playwright = {version = "^1.40.0", optional = true}
|
|
22
|
+
prompt-toolkit = "^3.0.52"
|
|
23
|
+
pyautogui = "^0.9.54"
|
|
24
|
+
pillow = "^12.1.1"
|
|
25
|
+
matplotlib = "^3.8.0"
|
|
26
|
+
numpy = "^1.24.0"
|
|
27
|
+
zenus-visualization = "^0.6.1"
|
|
28
|
+
|
|
29
|
+
[tool.poetry.extras]
|
|
30
|
+
browser = ["playwright"]
|
|
31
|
+
all = ["playwright"]
|
|
32
|
+
|
|
33
|
+
[tool.poetry.group.dev.dependencies]
|
|
34
|
+
pytest = "^8.0.0"
|
|
35
|
+
pytest-cov = "^4.1.0"
|
|
36
|
+
pytest-asyncio = "^0.23.0"
|
|
37
|
+
|
|
38
|
+
[build-system]
|
|
39
|
+
requires = ["poetry-core"]
|
|
40
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Zenus Core - Intent Execution Engine
|
|
3
|
+
|
|
4
|
+
The brain of Zenus: translates intent into validated system operations.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
__version__ = "0.3.0"
|
|
8
|
+
|
|
9
|
+
from zenus_core.orchestrator import Orchestrator, OrchestratorError
|
|
10
|
+
from zenus_core.brain.llm.schemas import IntentIR, Step
|
|
11
|
+
|
|
12
|
+
__all__ = ["Orchestrator", "OrchestratorError", "IntentIR", "Step", "__version__"]
|
|
File without changes
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Audit Logger
|
|
3
|
+
|
|
4
|
+
Records all intent translation and execution flows for review and debugging.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Optional
|
|
12
|
+
from zenus_core.brain.llm.schemas import IntentIR
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AuditLogger:
|
|
16
|
+
"""Logs all operations to structured audit files"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, log_dir: Optional[str] = None):
|
|
19
|
+
if log_dir is None:
|
|
20
|
+
log_dir = os.path.expanduser("~/.zenus/logs")
|
|
21
|
+
|
|
22
|
+
self.log_dir = Path(log_dir)
|
|
23
|
+
self.log_dir.mkdir(parents=True, exist_ok=True)
|
|
24
|
+
|
|
25
|
+
# Current session log file
|
|
26
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
27
|
+
self.session_file = self.log_dir / f"session_{timestamp}.jsonl"
|
|
28
|
+
|
|
29
|
+
def log_intent(self, user_input: str, intent: IntentIR, mode: str = "execution"):
|
|
30
|
+
"""Log intent translation"""
|
|
31
|
+
entry = {
|
|
32
|
+
"timestamp": datetime.now().isoformat(),
|
|
33
|
+
"type": "intent",
|
|
34
|
+
"mode": mode,
|
|
35
|
+
"user_input": user_input,
|
|
36
|
+
"goal": intent.goal,
|
|
37
|
+
"requires_confirmation": intent.requires_confirmation,
|
|
38
|
+
"steps": [
|
|
39
|
+
{
|
|
40
|
+
"tool": step.tool,
|
|
41
|
+
"action": step.action,
|
|
42
|
+
"args": step.args,
|
|
43
|
+
"risk": step.risk
|
|
44
|
+
}
|
|
45
|
+
for step in intent.steps
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
self._write(entry)
|
|
49
|
+
|
|
50
|
+
def log_execution_start(self, intent: IntentIR):
|
|
51
|
+
"""Log execution start"""
|
|
52
|
+
entry = {
|
|
53
|
+
"timestamp": datetime.now().isoformat(),
|
|
54
|
+
"type": "execution_start",
|
|
55
|
+
"goal": intent.goal
|
|
56
|
+
}
|
|
57
|
+
self._write(entry)
|
|
58
|
+
|
|
59
|
+
def log_step_result(self, tool: str, action: str, result: str, success: bool):
|
|
60
|
+
"""Log individual step result"""
|
|
61
|
+
entry = {
|
|
62
|
+
"timestamp": datetime.now().isoformat(),
|
|
63
|
+
"type": "step_result",
|
|
64
|
+
"tool": tool,
|
|
65
|
+
"action": action,
|
|
66
|
+
"result": result,
|
|
67
|
+
"success": success
|
|
68
|
+
}
|
|
69
|
+
self._write(entry)
|
|
70
|
+
|
|
71
|
+
def log_execution_end(self, success: bool, message: Optional[str] = None):
|
|
72
|
+
"""Log execution completion"""
|
|
73
|
+
entry = {
|
|
74
|
+
"timestamp": datetime.now().isoformat(),
|
|
75
|
+
"type": "execution_end",
|
|
76
|
+
"success": success,
|
|
77
|
+
"message": message
|
|
78
|
+
}
|
|
79
|
+
self._write(entry)
|
|
80
|
+
|
|
81
|
+
def log_error(self, error: str, context: Optional[dict] = None):
|
|
82
|
+
"""Log error"""
|
|
83
|
+
entry = {
|
|
84
|
+
"timestamp": datetime.now().isoformat(),
|
|
85
|
+
"type": "error",
|
|
86
|
+
"error": error,
|
|
87
|
+
"context": context or {}
|
|
88
|
+
}
|
|
89
|
+
self._write(entry)
|
|
90
|
+
|
|
91
|
+
def log_info(self, event_type: str, data: Optional[dict] = None):
|
|
92
|
+
"""Log informational event"""
|
|
93
|
+
entry = {
|
|
94
|
+
"timestamp": datetime.now().isoformat(),
|
|
95
|
+
"type": "info",
|
|
96
|
+
"event": event_type,
|
|
97
|
+
"data": data or {}
|
|
98
|
+
}
|
|
99
|
+
self._write(entry)
|
|
100
|
+
|
|
101
|
+
def _write(self, entry: dict):
|
|
102
|
+
"""Write entry to log file"""
|
|
103
|
+
with open(self.session_file, "a") as f:
|
|
104
|
+
f.write(json.dumps(entry) + "\n")
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
# Global logger instance
|
|
108
|
+
_logger: Optional[AuditLogger] = None
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def get_logger() -> AuditLogger:
|
|
112
|
+
"""Get or create global logger"""
|
|
113
|
+
global _logger
|
|
114
|
+
if _logger is None:
|
|
115
|
+
_logger = AuditLogger()
|
|
116
|
+
return _logger
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Adaptive Planner
|
|
3
|
+
|
|
4
|
+
Executes plans with observation and adaptation.
|
|
5
|
+
Key difference from basic planner: can observe failures and replan.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Optional, Callable
|
|
9
|
+
from zenus_core.brain.llm.schemas import IntentIR, Step
|
|
10
|
+
from zenus_core.brain.llm.factory import get_llm
|
|
11
|
+
from zenus_core.tools.registry import TOOLS
|
|
12
|
+
from zenus_core.safety.policy import check_step, SafetyError
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ExecutionResult:
|
|
16
|
+
"""Result of a single step execution"""
|
|
17
|
+
def __init__(self, success: bool, output: str, error: Optional[str] = None):
|
|
18
|
+
self.success = success
|
|
19
|
+
self.output = output
|
|
20
|
+
self.error = error
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class AdaptivePlanner:
|
|
24
|
+
"""
|
|
25
|
+
Executes plans with observation and adaptation
|
|
26
|
+
|
|
27
|
+
Unlike basic execution, this planner:
|
|
28
|
+
- Observes each step result
|
|
29
|
+
- Detects failures
|
|
30
|
+
- Can request corrective actions
|
|
31
|
+
- Maintains execution context
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self, logger=None):
|
|
35
|
+
self.logger = logger
|
|
36
|
+
self.llm = get_llm()
|
|
37
|
+
self.execution_history = []
|
|
38
|
+
|
|
39
|
+
def execute_adaptive(
|
|
40
|
+
self,
|
|
41
|
+
intent: IntentIR,
|
|
42
|
+
max_retries: int = 2,
|
|
43
|
+
on_failure: Callable = None
|
|
44
|
+
) -> bool:
|
|
45
|
+
"""
|
|
46
|
+
Execute plan with adaptive retry on failure
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
intent: The validated IntentIR to execute
|
|
50
|
+
max_retries: Maximum retry attempts per step
|
|
51
|
+
on_failure: Optional callback for failure handling
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
True if all steps succeeded, False otherwise
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
# CRITICAL: Clear execution history at the start of each execution
|
|
58
|
+
# to prevent caching results from previous commands
|
|
59
|
+
self.execution_history = []
|
|
60
|
+
|
|
61
|
+
if self.logger:
|
|
62
|
+
self.logger.log_execution_start(intent)
|
|
63
|
+
|
|
64
|
+
for step_idx, step in enumerate(intent.steps):
|
|
65
|
+
attempt = 0
|
|
66
|
+
step_succeeded = False
|
|
67
|
+
|
|
68
|
+
while attempt <= max_retries and not step_succeeded:
|
|
69
|
+
if attempt > 0:
|
|
70
|
+
print(f"\n Retry attempt {attempt} for step {step_idx + 1}...")
|
|
71
|
+
|
|
72
|
+
result = self._execute_single_step(step, step_idx + 1)
|
|
73
|
+
|
|
74
|
+
if result.success:
|
|
75
|
+
step_succeeded = True
|
|
76
|
+
self.execution_history.append({
|
|
77
|
+
"step": step,
|
|
78
|
+
"result": result,
|
|
79
|
+
"attempt": attempt
|
|
80
|
+
})
|
|
81
|
+
else:
|
|
82
|
+
attempt += 1
|
|
83
|
+
|
|
84
|
+
if attempt <= max_retries:
|
|
85
|
+
# Try to adapt the step based on failure
|
|
86
|
+
adapted_step = self._adapt_on_failure(
|
|
87
|
+
step,
|
|
88
|
+
result,
|
|
89
|
+
self.execution_history
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
if adapted_step:
|
|
93
|
+
print(f" Adapting: {adapted_step.action} with {adapted_step.args}")
|
|
94
|
+
step = adapted_step
|
|
95
|
+
else:
|
|
96
|
+
# No adaptation possible, fail
|
|
97
|
+
break
|
|
98
|
+
|
|
99
|
+
if on_failure:
|
|
100
|
+
on_failure(step, result)
|
|
101
|
+
|
|
102
|
+
if not step_succeeded:
|
|
103
|
+
error_msg = f"Step {step_idx + 1} failed after {attempt} attempts"
|
|
104
|
+
if self.logger:
|
|
105
|
+
self.logger.log_execution_end(False, error_msg)
|
|
106
|
+
return False
|
|
107
|
+
|
|
108
|
+
if self.logger:
|
|
109
|
+
self.logger.log_execution_end(True)
|
|
110
|
+
|
|
111
|
+
return True
|
|
112
|
+
|
|
113
|
+
def _execute_single_step(self, step: Step, step_num: int) -> ExecutionResult:
|
|
114
|
+
"""Execute a single step and capture result"""
|
|
115
|
+
|
|
116
|
+
try:
|
|
117
|
+
# Safety check
|
|
118
|
+
check_step(step)
|
|
119
|
+
|
|
120
|
+
# Get tool
|
|
121
|
+
tool = TOOLS.get(step.tool)
|
|
122
|
+
if not tool:
|
|
123
|
+
return ExecutionResult(
|
|
124
|
+
False,
|
|
125
|
+
"",
|
|
126
|
+
f"Tool not found: {step.tool}"
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# Get action
|
|
130
|
+
action = getattr(tool, step.action, None)
|
|
131
|
+
if not action:
|
|
132
|
+
return ExecutionResult(
|
|
133
|
+
False,
|
|
134
|
+
"",
|
|
135
|
+
f"Action not found: {step.tool}.{step.action}"
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# Execute
|
|
139
|
+
output = action(**step.args)
|
|
140
|
+
# Note: Result display handled by orchestrator via print_step()
|
|
141
|
+
|
|
142
|
+
if self.logger:
|
|
143
|
+
self.logger.log_step_result(
|
|
144
|
+
step.tool,
|
|
145
|
+
step.action,
|
|
146
|
+
str(output),
|
|
147
|
+
True
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
return ExecutionResult(True, str(output))
|
|
151
|
+
|
|
152
|
+
except SafetyError as e:
|
|
153
|
+
error_msg = f"Safety check failed: {e}"
|
|
154
|
+
if self.logger:
|
|
155
|
+
self.logger.log_step_result(
|
|
156
|
+
step.tool,
|
|
157
|
+
step.action,
|
|
158
|
+
error_msg,
|
|
159
|
+
False
|
|
160
|
+
)
|
|
161
|
+
return ExecutionResult(False, "", error_msg)
|
|
162
|
+
|
|
163
|
+
except Exception as e:
|
|
164
|
+
error_msg = f"Execution failed: {e}"
|
|
165
|
+
if self.logger:
|
|
166
|
+
self.logger.log_step_result(
|
|
167
|
+
step.tool,
|
|
168
|
+
step.action,
|
|
169
|
+
error_msg,
|
|
170
|
+
False
|
|
171
|
+
)
|
|
172
|
+
return ExecutionResult(False, "", error_msg)
|
|
173
|
+
|
|
174
|
+
def _adapt_on_failure(
|
|
175
|
+
self,
|
|
176
|
+
failed_step: Step,
|
|
177
|
+
result: ExecutionResult,
|
|
178
|
+
history: list
|
|
179
|
+
) -> Optional[Step]:
|
|
180
|
+
"""
|
|
181
|
+
Attempt to adapt a failed step
|
|
182
|
+
|
|
183
|
+
Uses LLM to suggest corrective action based on:
|
|
184
|
+
- The failed step
|
|
185
|
+
- The error
|
|
186
|
+
- Execution history
|
|
187
|
+
|
|
188
|
+
Returns adapted step or None if no adaptation possible
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
# For now, return None (no LLM-based adaptation yet)
|
|
192
|
+
# This will be enhanced in future iterations
|
|
193
|
+
return None
|
|
194
|
+
|
|
195
|
+
def get_execution_summary(self) -> dict:
|
|
196
|
+
"""Get summary of execution for reporting"""
|
|
197
|
+
|
|
198
|
+
total_steps = len(self.execution_history)
|
|
199
|
+
retried_steps = sum(
|
|
200
|
+
1 for entry in self.execution_history if entry["attempt"] > 0
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
"total_steps": total_steps,
|
|
205
|
+
"retried_steps": retried_steps,
|
|
206
|
+
"success_rate": 1.0 if total_steps > 0 else 0.0
|
|
207
|
+
}
|