deer-agent-framework 0.1a2__tar.gz → 0.1a4__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.
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/PKG-INFO +20 -8
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/README.md +13 -5
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/__init__.py +4 -2
- deer_agent_framework-0.1a4/deer/builtins/python_manager/agent.py +31 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/core/agent.py +220 -79
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/core/ui.py +11 -5
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/drivers/base_driver.py +82 -14
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/drivers/gemini_driver.py +27 -7
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/drivers/ollama_driver.py +9 -13
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/executor/executor.py +4 -4
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/main.py +28 -17
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/planner/planner.py +5 -3
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/prompts/planner.py +2 -2
- deer_agent_framework-0.1a4/deer/states/__init__.py +2 -0
- deer_agent_framework-0.1a4/deer/states/base.py +50 -0
- deer_agent_framework-0.1a4/deer/states/parallel_state_manager.py +129 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/tools/__init__.py +1 -1
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/tools/base.py +8 -2
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/tools/builtin/__init__.py +6 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/tools/builtin/file_manager.py +45 -17
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/tools/builtin/git_manager.py +11 -10
- deer_agent_framework-0.1a4/deer/tools/builtin/json_editor.py +150 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/tools/builtin/memory_manager.py +6 -5
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/tools/builtin/network_manager.py +2 -1
- deer_agent_framework-0.1a4/deer/tools/builtin/python_editor.py +552 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/tools/builtin/runtime_manager.py +8 -7
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/tools/builtin/search_manager.py +14 -13
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/tools/builtin/structured_data_inspector.py +6 -6
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/tools/builtin/system_inspector.py +5 -4
- deer_agent_framework-0.1a4/deer/tools/builtin/toml_editor.py +122 -0
- deer_agent_framework-0.1a4/deer/tools/builtin/xml_editor.py +126 -0
- deer_agent_framework-0.1a4/deer/tools/builtin/yaml_editor.py +150 -0
- deer_agent_framework-0.1a4/deer/tools/presets.py +71 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/tools/registry.py +2 -1
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/validator/plan_validator.py +4 -4
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/validator/rules.py +4 -4
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer_agent_framework.egg-info/PKG-INFO +20 -8
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer_agent_framework.egg-info/SOURCES.txt +11 -4
- deer_agent_framework-0.1a4/deer_agent_framework.egg-info/requires.txt +12 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/pyproject.toml +7 -3
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/tests/test_plan_validator.py +3 -3
- deer_agent_framework-0.1a4/tests/test_python_editor.py +68 -0
- deer_agent_framework-0.1a2/deer/builtins/python_manager/agent.py +0 -29
- deer_agent_framework-0.1a2/deer/builtins/python_manager/tools.py +0 -112
- deer_agent_framework-0.1a2/deer_agent_framework.egg-info/requires.txt +0 -8
- deer_agent_framework-0.1a2/tests/test_azure_driver.py +0 -56
- deer_agent_framework-0.1a2/tests/test_openai_driver.py +0 -69
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/LICENSE +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/builtins/__init__.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/core/__init__.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/drivers/__init__.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/drivers/azure_driver.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/drivers/openai_driver.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/executor/__init__.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/executor/logic.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/executor/logic_secure.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/planner/__init__.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/prompts/__init__.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/prompts/error_explain.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/prompts/goal_improvement.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/prompts/goal_validation.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/prompts/humanizer.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/prompts/response_improvement.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/schema/__init__.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/schema/io.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/schema/plan.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/tools/decorators.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/tracing/__init__.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/tracing/logging_config.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/tracing/store.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/utils/__init__.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/utils/console.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/utils/plots/__init__.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/utils/plots/plot_traces.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer/validator/__init__.py +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer_agent_framework.egg-info/dependency_links.txt +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer_agent_framework.egg-info/entry_points.txt +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/deer_agent_framework.egg-info/top_level.txt +0 -0
- {deer_agent_framework-0.1a2 → deer_agent_framework-0.1a4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deer-agent-framework
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.1a4
|
|
4
4
|
Summary: DEER - Deterministic Executable Engine for Runtime-agents
|
|
5
5
|
Author: Yeison
|
|
6
6
|
License: BSD-2-Clause
|
|
@@ -20,8 +20,12 @@ Requires-Python: >=3.12
|
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
22
|
Requires-Dist: pydantic>=2.0
|
|
23
|
-
Requires-Dist: rich
|
|
24
|
-
Requires-Dist: prompt_toolkit
|
|
23
|
+
Requires-Dist: rich>=15.0.0
|
|
24
|
+
Requires-Dist: prompt_toolkit>=3.0.52
|
|
25
|
+
Requires-Dist: libcst>=1.8.6
|
|
26
|
+
Requires-Dist: tomlkit>=0.15.0
|
|
27
|
+
Requires-Dist: ruamel.yaml>=0.19.1
|
|
28
|
+
Requires-Dist: lxml>=6.1.1
|
|
25
29
|
Provides-Extra: debug
|
|
26
30
|
Requires-Dist: matplotlib>=3.10.9; extra == "debug"
|
|
27
31
|
Requires-Dist: pandas>=3.0.3; extra == "debug"
|
|
@@ -68,8 +72,9 @@ Forget about telling the AI to "act like a specialist." Define a high-level `Det
|
|
|
68
72
|
```python
|
|
69
73
|
from pathlib import Path
|
|
70
74
|
from deer.core.agent import DeterministicAgent
|
|
75
|
+
from deer.states import ParallelGitStateManager
|
|
71
76
|
from deer.drivers import get_driver_from_parser
|
|
72
|
-
from .tools import
|
|
77
|
+
from deer.tools import Preset
|
|
73
78
|
|
|
74
79
|
agent = DeterministicAgent(
|
|
75
80
|
description="Python Architecture Specialist",
|
|
@@ -78,10 +83,11 @@ agent = DeterministicAgent(
|
|
|
78
83
|
"in advanced module resolution and dependency management."
|
|
79
84
|
),
|
|
80
85
|
driver=get_driver_from_parser(),
|
|
81
|
-
|
|
86
|
+
tool_registry=Preset.CODE_REPAIR | Preset.CODE_EDITOR | Preset.DATA_ANALYST,
|
|
82
87
|
jail_path=Path.cwd() / "sandbox", # Strict security boundary
|
|
83
88
|
format_response="markdown",
|
|
84
|
-
max_retries=5
|
|
89
|
+
max_retries=5,
|
|
90
|
+
state_manager=ParallelGitStateManager(),
|
|
85
91
|
)
|
|
86
92
|
|
|
87
93
|
if __name__ == "__main__":
|
|
@@ -95,10 +101,10 @@ Combine built-in managers (File, Git, Search) into a single, cohesive unit of ex
|
|
|
95
101
|
from deer.tools.registry import ToolRegistry
|
|
96
102
|
from deer.tools.builtin import FileManager, GitManager, SearchManager
|
|
97
103
|
|
|
98
|
-
|
|
104
|
+
tool_registry = ToolRegistry()
|
|
99
105
|
|
|
100
106
|
# Add specialized capabilities with zero-config
|
|
101
|
-
|
|
107
|
+
tool_registry.register(
|
|
102
108
|
FileManager(),
|
|
103
109
|
GitManager(),
|
|
104
110
|
SearchManager(),
|
|
@@ -171,6 +177,12 @@ Every decision, tool call, and validation step is recorded. This allows for deep
|
|
|
171
177
|
|
|
172
178
|
## Installation
|
|
173
179
|
|
|
180
|
+
From PyPI:
|
|
181
|
+
```bash
|
|
182
|
+
pip install deer-agent-framework
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
From GitHub (development version):
|
|
174
186
|
```bash
|
|
175
187
|
pip install git+https://github.com/dunderlab/deer-agent-framework.git
|
|
176
188
|
```
|
|
@@ -38,8 +38,9 @@ Forget about telling the AI to "act like a specialist." Define a high-level `Det
|
|
|
38
38
|
```python
|
|
39
39
|
from pathlib import Path
|
|
40
40
|
from deer.core.agent import DeterministicAgent
|
|
41
|
+
from deer.states import ParallelGitStateManager
|
|
41
42
|
from deer.drivers import get_driver_from_parser
|
|
42
|
-
from .tools import
|
|
43
|
+
from deer.tools import Preset
|
|
43
44
|
|
|
44
45
|
agent = DeterministicAgent(
|
|
45
46
|
description="Python Architecture Specialist",
|
|
@@ -48,10 +49,11 @@ agent = DeterministicAgent(
|
|
|
48
49
|
"in advanced module resolution and dependency management."
|
|
49
50
|
),
|
|
50
51
|
driver=get_driver_from_parser(),
|
|
51
|
-
|
|
52
|
+
tool_registry=Preset.CODE_REPAIR | Preset.CODE_EDITOR | Preset.DATA_ANALYST,
|
|
52
53
|
jail_path=Path.cwd() / "sandbox", # Strict security boundary
|
|
53
54
|
format_response="markdown",
|
|
54
|
-
max_retries=5
|
|
55
|
+
max_retries=5,
|
|
56
|
+
state_manager=ParallelGitStateManager(),
|
|
55
57
|
)
|
|
56
58
|
|
|
57
59
|
if __name__ == "__main__":
|
|
@@ -65,10 +67,10 @@ Combine built-in managers (File, Git, Search) into a single, cohesive unit of ex
|
|
|
65
67
|
from deer.tools.registry import ToolRegistry
|
|
66
68
|
from deer.tools.builtin import FileManager, GitManager, SearchManager
|
|
67
69
|
|
|
68
|
-
|
|
70
|
+
tool_registry = ToolRegistry()
|
|
69
71
|
|
|
70
72
|
# Add specialized capabilities with zero-config
|
|
71
|
-
|
|
73
|
+
tool_registry.register(
|
|
72
74
|
FileManager(),
|
|
73
75
|
GitManager(),
|
|
74
76
|
SearchManager(),
|
|
@@ -141,6 +143,12 @@ Every decision, tool call, and validation step is recorded. This allows for deep
|
|
|
141
143
|
|
|
142
144
|
## Installation
|
|
143
145
|
|
|
146
|
+
From PyPI:
|
|
147
|
+
```bash
|
|
148
|
+
pip install deer-agent-framework
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
From GitHub (development version):
|
|
144
152
|
```bash
|
|
145
153
|
pip install git+https://github.com/dunderlab/deer-agent-framework.git
|
|
146
154
|
```
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
|
|
4
|
+
__version__ = "0.1.9"
|
|
5
|
+
|
|
4
6
|
|
|
5
7
|
def load_env_file(env_path: Path) -> None:
|
|
6
8
|
"""Reads a .env file and loads its variables into os.environ natively."""
|
|
@@ -28,9 +30,9 @@ def load_env_file(env_path: Path) -> None:
|
|
|
28
30
|
|
|
29
31
|
# Your configuration logic remains clean and dependency-free
|
|
30
32
|
local_env = Path(".env")
|
|
31
|
-
user_env = Path.home() / "deer.env"
|
|
33
|
+
user_env = Path.home() / ".deer.env"
|
|
32
34
|
|
|
33
35
|
if local_env.exists():
|
|
34
36
|
load_env_file(local_env)
|
|
35
37
|
elif user_env.exists():
|
|
36
|
-
load_env_file(user_env)
|
|
38
|
+
load_env_file(user_env)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from deer.core.agent import DeterministicAgent
|
|
4
|
+
from deer.states import ParallelGitStateManager
|
|
5
|
+
from deer.drivers import get_driver_from_parser
|
|
6
|
+
from deer.tools import Preset
|
|
7
|
+
|
|
8
|
+
agent = DeterministicAgent(
|
|
9
|
+
description="AI specialist in Python architecture, runtime module resolution, and dependency management.",
|
|
10
|
+
identity=(
|
|
11
|
+
"You are an elite AI Agent operating as a Principal Python Architect and Core Ecosystem Specialist. "
|
|
12
|
+
"You provide authoritative, deterministic guidance on advanced module resolution, runtime execution, "
|
|
13
|
+
"dependency isolation, and package distribution. Your expertise spans the entire Python lifecycle: "
|
|
14
|
+
"from engineering scalable code structures to resolving complex import mechanisms and optimizing "
|
|
15
|
+
"deployment pipelines across public or internal repositories."
|
|
16
|
+
),
|
|
17
|
+
driver=get_driver_from_parser(),
|
|
18
|
+
tool_registry=Preset.CODE_REPAIR | Preset.CODE_EDITOR | Preset.DATA_ANALYST,
|
|
19
|
+
jail_path=Path.cwd(),
|
|
20
|
+
format_response="markdown",
|
|
21
|
+
max_retries=5,
|
|
22
|
+
state_manager=ParallelGitStateManager(),
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def main():
|
|
27
|
+
agent.repl()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
if __name__ == "__main__":
|
|
31
|
+
main()
|
|
@@ -5,10 +5,16 @@ import pickle
|
|
|
5
5
|
import sys
|
|
6
6
|
from typing import Callable
|
|
7
7
|
from datetime import datetime
|
|
8
|
+
import os
|
|
9
|
+
import subprocess
|
|
10
|
+
from pathlib import Path
|
|
8
11
|
|
|
9
12
|
from prompt_toolkit import prompt
|
|
10
13
|
from prompt_toolkit.formatted_text import HTML
|
|
11
14
|
from prompt_toolkit.history import InMemoryHistory
|
|
15
|
+
from prompt_toolkit.completion import WordCompleter
|
|
16
|
+
from rich.console import Console
|
|
17
|
+
from rich.markdown import Markdown
|
|
12
18
|
|
|
13
19
|
from deer.prompts import (
|
|
14
20
|
RESPONSE_IMPROVEMENT_PROMPT,
|
|
@@ -17,6 +23,7 @@ from deer.prompts import (
|
|
|
17
23
|
GOAL_VERIFIER_PROMPT,
|
|
18
24
|
VERIFIER_JUDGE_PROMPT,
|
|
19
25
|
)
|
|
26
|
+
from deer import __version__
|
|
20
27
|
from deer.drivers.base_driver import LLMDriver
|
|
21
28
|
from deer.planner.planner import Planner
|
|
22
29
|
from deer.validator.plan_validator import PlanValidator
|
|
@@ -24,11 +31,19 @@ from deer.executor.executor import Executor
|
|
|
24
31
|
from deer.core.ui import WELCOME_MESSAGE
|
|
25
32
|
from deer.tools.registry import ToolRegistry, default_registry
|
|
26
33
|
from deer.schema.io import AgentInput, AgentOutput
|
|
27
|
-
from
|
|
28
|
-
from rich.markdown import Markdown
|
|
34
|
+
from deer.states import BaseStateManager
|
|
29
35
|
|
|
30
36
|
logger = logging.getLogger("DEER")
|
|
31
|
-
prompt_history = InMemoryHistory()
|
|
37
|
+
# prompt_history = InMemoryHistory()
|
|
38
|
+
# prompt_history = '.deer.history'
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
COMMANDS = {
|
|
42
|
+
"/tools": "Show tools and capabilities",
|
|
43
|
+
"/clear": "Clear the console and reset the session",
|
|
44
|
+
"/exit": "Terminate the session",
|
|
45
|
+
"/rollback": "Revert the system to the last stable state",
|
|
46
|
+
}
|
|
32
47
|
|
|
33
48
|
|
|
34
49
|
class DeterministicAgent:
|
|
@@ -38,42 +53,53 @@ class DeterministicAgent:
|
|
|
38
53
|
identity: str = "DeterministicAgent",
|
|
39
54
|
max_retries: int = 3,
|
|
40
55
|
driver: LLMDriver | None = None,
|
|
41
|
-
|
|
56
|
+
tool_registry: ToolRegistry | None = None,
|
|
42
57
|
format_response: str = "plaintext",
|
|
43
|
-
|
|
58
|
+
state_manager: BaseStateManager = None,
|
|
44
59
|
jail_path: str = None,
|
|
45
60
|
) -> None:
|
|
46
61
|
assert format_response in {"markdown", "plaintext"}
|
|
47
62
|
|
|
63
|
+
self.history_filename = ".deer.history"
|
|
64
|
+
self.prompt_history = self.load_history()
|
|
65
|
+
|
|
48
66
|
self.identity = identity
|
|
49
67
|
self.description = description
|
|
50
68
|
self.driver = driver
|
|
51
|
-
|
|
69
|
+
|
|
70
|
+
if isinstance(tool_registry, set):
|
|
71
|
+
tr = ToolRegistry()
|
|
72
|
+
tr.register(*[tool() for tool in tool_registry])
|
|
73
|
+
tool_registry = tr
|
|
74
|
+
|
|
75
|
+
self.tool_registry = tool_registry or default_registry()
|
|
76
|
+
|
|
52
77
|
self.console = Console()
|
|
53
78
|
|
|
54
79
|
self.executor = Executor(
|
|
55
|
-
|
|
80
|
+
tool_registry=self.tool_registry,
|
|
56
81
|
)
|
|
57
82
|
|
|
58
83
|
self.validator = PlanValidator(
|
|
59
|
-
|
|
84
|
+
tool_registry=self.tool_registry,
|
|
60
85
|
)
|
|
61
86
|
|
|
62
87
|
self.planner = Planner(
|
|
63
88
|
identity=identity,
|
|
64
89
|
driver=driver,
|
|
65
|
-
|
|
90
|
+
tool_registry=self.tool_registry,
|
|
66
91
|
format_response=format_response,
|
|
67
92
|
)
|
|
68
93
|
|
|
94
|
+
self.state_manager = state_manager
|
|
95
|
+
|
|
69
96
|
assert (
|
|
70
|
-
|
|
97
|
+
max_retries > 0
|
|
71
98
|
), f"max_tries_for_plan must be positive, got {max_retries}"
|
|
72
99
|
self.max_retries = max_retries
|
|
73
100
|
|
|
74
101
|
self.history = []
|
|
75
102
|
self.trace = []
|
|
76
|
-
self.rollback_function = rollback
|
|
77
103
|
|
|
78
104
|
if jail_path:
|
|
79
105
|
self.set_jail(jail_path)
|
|
@@ -84,17 +110,17 @@ class DeterministicAgent:
|
|
|
84
110
|
)
|
|
85
111
|
logger.info(f"Identity: {self.identity}")
|
|
86
112
|
logger.info(
|
|
87
|
-
f"Available tools:\n{self.
|
|
113
|
+
f"Available tools:\n{self.tool_registry.describe(include_state_modifying=True)}"
|
|
88
114
|
)
|
|
89
115
|
logger.info(f"Authorized filesystem scope is restricted to: {jail_path}")
|
|
90
116
|
|
|
91
117
|
def set_jail(self, jail_path):
|
|
92
|
-
self.
|
|
118
|
+
self.tool_registry.set_jail(jail_path)
|
|
93
119
|
|
|
94
120
|
def should_verify(self, plan) -> bool:
|
|
95
121
|
return any(step.tool for step in plan.steps)
|
|
96
122
|
|
|
97
|
-
def
|
|
123
|
+
def generate_and_execute_plan(
|
|
98
124
|
self,
|
|
99
125
|
agent_input: AgentInput,
|
|
100
126
|
feedback: {},
|
|
@@ -113,7 +139,7 @@ class DeterministicAgent:
|
|
|
113
139
|
feedback = {"Planning error": str(planning_error)}
|
|
114
140
|
last_error_message = str(planning_error)
|
|
115
141
|
return None, feedback, last_error_message, None
|
|
116
|
-
logger.info(f"Plan generated")
|
|
142
|
+
logger.info(f"1. Plan generated")
|
|
117
143
|
logger.debug(f"Plan steps:")
|
|
118
144
|
for step in plan.steps:
|
|
119
145
|
logger.debug(f"\tstep: {step.id}")
|
|
@@ -129,7 +155,7 @@ class DeterministicAgent:
|
|
|
129
155
|
feedback = {"verification error": str(validation_error)}
|
|
130
156
|
last_error_message = str(validation_error)
|
|
131
157
|
return None, feedback, last_error_message, plan
|
|
132
|
-
logger.info(f"Plan verified")
|
|
158
|
+
logger.info(f"2. Plan verified")
|
|
133
159
|
|
|
134
160
|
try:
|
|
135
161
|
response = self.executor.execute(
|
|
@@ -141,22 +167,28 @@ class DeterministicAgent:
|
|
|
141
167
|
feedback = {"execution error": str(execution_error)}
|
|
142
168
|
last_error_message = str(execution_error)
|
|
143
169
|
return None, feedback, last_error_message, plan
|
|
144
|
-
logger.info(f"Plan executed")
|
|
170
|
+
logger.info(f"3. Plan executed")
|
|
145
171
|
|
|
146
172
|
return response, feedback, last_error_message, plan
|
|
147
173
|
|
|
148
174
|
def run(self, agent_input: AgentInput) -> AgentOutput:
|
|
149
175
|
feedback = {}
|
|
150
176
|
run_trace = {}
|
|
151
|
-
|
|
152
177
|
response_validation = None
|
|
178
|
+
last_error_message = None
|
|
179
|
+
|
|
180
|
+
self.state_snapshot()
|
|
153
181
|
|
|
154
182
|
for attempt_idx in range(self.max_retries):
|
|
183
|
+
self.state_restore()
|
|
184
|
+
|
|
155
185
|
logger.debug(f"Attempt {attempt_idx + 1} of {self.max_retries}")
|
|
156
186
|
|
|
157
187
|
run_trace[f"Attempt-{attempt_idx + 1}"] = {}
|
|
158
188
|
|
|
159
189
|
for solution_idx in range(self.max_retries):
|
|
190
|
+
self.state_restore()
|
|
191
|
+
|
|
160
192
|
logger.debug(
|
|
161
193
|
f"Solution planning {solution_idx + 1} of {self.max_retries}"
|
|
162
194
|
)
|
|
@@ -164,8 +196,8 @@ class DeterministicAgent:
|
|
|
164
196
|
f"Solution-{solution_idx + 1}"
|
|
165
197
|
] = datetime.now()
|
|
166
198
|
|
|
167
|
-
response, feedback, last_error_message,
|
|
168
|
-
agent_input, feedback
|
|
199
|
+
response, feedback, last_error_message, solution_steps = (
|
|
200
|
+
self.generate_and_execute_plan(agent_input, feedback)
|
|
169
201
|
)
|
|
170
202
|
if response is None:
|
|
171
203
|
continue
|
|
@@ -176,15 +208,10 @@ class DeterministicAgent:
|
|
|
176
208
|
f"Agent failed to produce a response after {self.max_retries} attempts"
|
|
177
209
|
)
|
|
178
210
|
if last_error_message:
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
)
|
|
182
|
-
return AgentOutput(
|
|
183
|
-
result=last_error_message_explained,
|
|
184
|
-
trace=self.executor.trace_store.get_trace(),
|
|
185
|
-
)
|
|
211
|
+
feedback = {"Previous Error": last_error_message}
|
|
212
|
+
continue
|
|
186
213
|
|
|
187
|
-
if not self.should_verify(
|
|
214
|
+
if not self.should_verify(solution_steps):
|
|
188
215
|
break
|
|
189
216
|
|
|
190
217
|
feedback_ = {}
|
|
@@ -209,7 +236,7 @@ class DeterministicAgent:
|
|
|
209
236
|
agent_verifier_input = AgentInput(
|
|
210
237
|
goal=goal_verifier_prompt, payload=payload_verifier
|
|
211
238
|
)
|
|
212
|
-
response_validation, feedback_, _,
|
|
239
|
+
response_validation, feedback_, _, _ = self.generate_and_execute_plan(
|
|
213
240
|
agent_verifier_input, feedback_, include_state_modifying=False
|
|
214
241
|
)
|
|
215
242
|
|
|
@@ -235,7 +262,16 @@ class DeterministicAgent:
|
|
|
235
262
|
if response.validated:
|
|
236
263
|
break
|
|
237
264
|
|
|
238
|
-
|
|
265
|
+
if response is None:
|
|
266
|
+
logger.warning(
|
|
267
|
+
f"Agent failed to produce a response after {self.max_retries} attempts"
|
|
268
|
+
)
|
|
269
|
+
if last_error_message:
|
|
270
|
+
last_error_message_explained = self.explain_error(last_error_message)
|
|
271
|
+
return AgentOutput(
|
|
272
|
+
result=last_error_message_explained,
|
|
273
|
+
trace=self.executor.trace_store.get_trace(),
|
|
274
|
+
)
|
|
239
275
|
|
|
240
276
|
self.history.extend(
|
|
241
277
|
[
|
|
@@ -261,21 +297,29 @@ class DeterministicAgent:
|
|
|
261
297
|
)
|
|
262
298
|
|
|
263
299
|
self.planner.goal = agent_input.goal
|
|
300
|
+
self.state_purge_engine()
|
|
264
301
|
return response
|
|
265
302
|
|
|
266
|
-
def send(self,
|
|
303
|
+
def send(self, message, print_chat: bool = False):
|
|
267
304
|
user_input = AgentInput(
|
|
268
|
-
goal=
|
|
305
|
+
goal=message,
|
|
269
306
|
payload={
|
|
270
307
|
"chat_history": self.history,
|
|
271
308
|
},
|
|
272
309
|
)
|
|
273
|
-
response = self.run(user_input)
|
|
274
310
|
|
|
311
|
+
if print_chat:
|
|
312
|
+
print(f">>> {message}")
|
|
313
|
+
|
|
314
|
+
response = self.run(user_input)
|
|
275
315
|
if isinstance(response.result, dict):
|
|
276
316
|
response.result = self.humanize_result(response)
|
|
277
317
|
else:
|
|
278
318
|
response.result = self.improve_result(response)
|
|
319
|
+
|
|
320
|
+
if print_chat:
|
|
321
|
+
print(f" {response.result}")
|
|
322
|
+
|
|
279
323
|
return response
|
|
280
324
|
|
|
281
325
|
def pretty_print(self, out: str):
|
|
@@ -285,12 +329,17 @@ class DeterministicAgent:
|
|
|
285
329
|
self.history = []
|
|
286
330
|
self.trace = []
|
|
287
331
|
|
|
288
|
-
def
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
332
|
+
def state_snapshot(self):
|
|
333
|
+
if self.state_manager:
|
|
334
|
+
self.state_manager.set_reference_state(self.tool_registry.jail_path)
|
|
335
|
+
|
|
336
|
+
def state_restore(self):
|
|
337
|
+
if self.state_manager:
|
|
338
|
+
self.state_manager.rollback_to_previous_state()
|
|
339
|
+
|
|
340
|
+
def state_purge_engine(self):
|
|
341
|
+
if self.state_manager:
|
|
342
|
+
self.state_manager.purge_engine()
|
|
294
343
|
|
|
295
344
|
def humanize_result(self, output: AgentOutput) -> str:
|
|
296
345
|
trace_str = ""
|
|
@@ -318,7 +367,7 @@ class DeterministicAgent:
|
|
|
318
367
|
|
|
319
368
|
def explain_error(self, error: str):
|
|
320
369
|
error_prompt = ERROR_EXPLAIN_PROMPT.format(
|
|
321
|
-
error=error, tools=self.
|
|
370
|
+
error=error, tools=self.tool_registry.describe()
|
|
322
371
|
)
|
|
323
372
|
error_explained = self.driver.generate_text(error_prompt)
|
|
324
373
|
return error_explained
|
|
@@ -376,7 +425,7 @@ class DeterministicAgent:
|
|
|
376
425
|
|
|
377
426
|
def save_trace(self, filename: str):
|
|
378
427
|
obj = {
|
|
379
|
-
"tools": list(self.
|
|
428
|
+
"tools": list(self.tool_registry.list_tools()),
|
|
380
429
|
"trace": self.trace,
|
|
381
430
|
"history": self.history,
|
|
382
431
|
}
|
|
@@ -388,75 +437,167 @@ class DeterministicAgent:
|
|
|
388
437
|
pickle.dump(obj, f)
|
|
389
438
|
|
|
390
439
|
def show_welcome(self):
|
|
440
|
+
|
|
441
|
+
commands_formated = ""
|
|
442
|
+
for command in COMMANDS:
|
|
443
|
+
commands_formated += (
|
|
444
|
+
f"- {('`'+command+'`').ljust(12, ' ')} → {COMMANDS[command]}\n "
|
|
445
|
+
)
|
|
446
|
+
|
|
391
447
|
self.console.clear()
|
|
392
|
-
self.pretty_print(
|
|
448
|
+
self.pretty_print(
|
|
449
|
+
WELCOME_MESSAGE.format(version=__version__, commands=commands_formated)
|
|
450
|
+
)
|
|
393
451
|
self.pretty_print(
|
|
394
452
|
f">**Agent Profile** \n"
|
|
395
453
|
f"*{self.description}* \n"
|
|
396
|
-
f"root: {self.
|
|
454
|
+
f"root: {self.tool_registry.jail_path} \n"
|
|
397
455
|
f"{self.driver}: {self.driver.model_name} \n"
|
|
398
456
|
)
|
|
399
457
|
print("\n")
|
|
400
458
|
|
|
459
|
+
def run_command(self, command):
|
|
460
|
+
match command:
|
|
461
|
+
case "/exit":
|
|
462
|
+
sys.exit(0)
|
|
463
|
+
|
|
464
|
+
case "/clear":
|
|
465
|
+
self.clear_history()
|
|
466
|
+
self.console.clear()
|
|
467
|
+
self.pretty_print(f">**Agent Profile** \n*{self.description}*")
|
|
468
|
+
print("\n")
|
|
469
|
+
|
|
470
|
+
case "/tools":
|
|
471
|
+
self.pretty_print(
|
|
472
|
+
self.tool_registry.describe(
|
|
473
|
+
include_state_modifying=True, markdown=True
|
|
474
|
+
)
|
|
475
|
+
)
|
|
476
|
+
print("\n")
|
|
477
|
+
|
|
478
|
+
case "/rollback":
|
|
479
|
+
self.state_restore()
|
|
480
|
+
self.pretty_print(
|
|
481
|
+
"**Rollback executed.** System reverted to the last stable state."
|
|
482
|
+
)
|
|
483
|
+
print("\n")
|
|
484
|
+
|
|
401
485
|
def repl(self):
|
|
402
486
|
logger.setLevel(logging.CRITICAL)
|
|
403
487
|
self.show_welcome()
|
|
404
488
|
|
|
405
489
|
while True:
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
490
|
+
try:
|
|
491
|
+
msg = prompt(
|
|
492
|
+
HTML("<ansicyan><b>>>> </b></ansicyan>"),
|
|
493
|
+
history=self.prompt_history,
|
|
494
|
+
completer=self.completer,
|
|
495
|
+
)
|
|
496
|
+
self.save_history()
|
|
497
|
+
except KeyboardInterrupt:
|
|
498
|
+
continue
|
|
499
|
+
except EOFError:
|
|
500
|
+
break
|
|
416
501
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
self.pretty_print(f">**Agent Profile** \n*{self.description}*")
|
|
421
|
-
print("\n")
|
|
502
|
+
msg = msg.strip()
|
|
503
|
+
if not msg:
|
|
504
|
+
continue
|
|
422
505
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
506
|
+
match msg:
|
|
507
|
+
case command if command in COMMANDS:
|
|
508
|
+
self.run_command(command)
|
|
509
|
+
|
|
510
|
+
case command if command.startswith("!"):
|
|
511
|
+
# Remove the '!' from the start to get only the command
|
|
512
|
+
cmd_to_run = command[1:].strip()
|
|
513
|
+
|
|
514
|
+
try:
|
|
515
|
+
# Execute the command
|
|
516
|
+
result = subprocess.run(
|
|
517
|
+
cmd_to_run,
|
|
518
|
+
shell=True,
|
|
519
|
+
capture_output=True,
|
|
520
|
+
text=True,
|
|
521
|
+
check=True,
|
|
427
522
|
)
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
self.pretty_print(
|
|
434
|
-
"**Rollback executed.** System reverted to the last stable state."
|
|
435
|
-
)
|
|
436
|
-
print("\n")
|
|
523
|
+
print(result.stdout)
|
|
524
|
+
except subprocess.CalledProcessError as e:
|
|
525
|
+
print(f"Error executing command: {e.stderr}")
|
|
526
|
+
except Exception as e:
|
|
527
|
+
print(f"An unexpected error occurred: {e}")
|
|
437
528
|
|
|
438
529
|
case _:
|
|
439
|
-
if
|
|
530
|
+
if msg.startswith("/"):
|
|
531
|
+
logging.warning(
|
|
532
|
+
f"Command `{msg}` not recognized. Did you mean one of these: {list(COMMANDS)}"
|
|
533
|
+
)
|
|
440
534
|
continue
|
|
441
|
-
|
|
442
535
|
with self.console.status(
|
|
443
536
|
"[dim]processing request[/]",
|
|
444
537
|
spinner="point",
|
|
445
538
|
spinner_style="dim",
|
|
446
539
|
):
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
540
|
+
try:
|
|
541
|
+
output = self.send(msg)
|
|
542
|
+
self.pretty_print(f" {output.text}")
|
|
543
|
+
|
|
544
|
+
print("\n")
|
|
545
|
+
except KeyboardInterrupt:
|
|
546
|
+
self.console.print("[dim]Command aborted by user[/]\n")
|
|
547
|
+
self.state_restore()
|
|
548
|
+
continue
|
|
549
|
+
except EOFError:
|
|
550
|
+
break
|
|
551
|
+
|
|
552
|
+
def iterate_debug(
|
|
553
|
+
self,
|
|
554
|
+
chain_messages: list[str],
|
|
555
|
+
repetitions: int,
|
|
556
|
+
path: str,
|
|
557
|
+
callback: Callable = None,
|
|
558
|
+
):
|
|
452
559
|
logger.setLevel(logging.DEBUG)
|
|
560
|
+
os.makedirs(path, exist_ok=True)
|
|
453
561
|
for i in range(repetitions):
|
|
454
562
|
self.clear_history()
|
|
455
|
-
self.rollback()
|
|
456
563
|
name = f"{self.driver.model_name}-{datetime.now().timestamp()}"
|
|
564
|
+
|
|
565
|
+
formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")
|
|
566
|
+
file_handler = logging.FileHandler(f"{path}/{name}.log")
|
|
567
|
+
file_handler.setFormatter(formatter)
|
|
568
|
+
logger.addHandler(file_handler)
|
|
569
|
+
logger.info("Starting debug mode")
|
|
570
|
+
logger.info(f"Model: {self.driver.model_name}")
|
|
571
|
+
logger.info("Tools:")
|
|
572
|
+
logger.info(self.tool_registry.describe(include_state_modifying=True))
|
|
573
|
+
logger.removeHandler(file_handler)
|
|
574
|
+
file_handler.close()
|
|
575
|
+
|
|
457
576
|
self.generate_chat_log(
|
|
458
577
|
chain_messages,
|
|
459
578
|
print_chat=True,
|
|
460
579
|
save_log=f"{path}/{name}.log",
|
|
461
580
|
)
|
|
462
581
|
self.save_trace(f"{path}/{name}.trace")
|
|
582
|
+
if callback:
|
|
583
|
+
callback()
|
|
584
|
+
|
|
585
|
+
def load_history(self):
|
|
586
|
+
history_file = Path.home() / self.history_filename
|
|
587
|
+
if not history_file.exists():
|
|
588
|
+
self.prompt_history = InMemoryHistory()
|
|
589
|
+
return self.prompt_history
|
|
590
|
+
|
|
591
|
+
with open(history_file, "rb") as f:
|
|
592
|
+
self.prompt_history = pickle.load(f)
|
|
593
|
+
return self.prompt_history
|
|
594
|
+
|
|
595
|
+
def save_history(self):
|
|
596
|
+
history_file = Path.home() / self.history_filename
|
|
597
|
+
with open(history_file, "wb") as f:
|
|
598
|
+
pickle.dump(self.prompt_history, f)
|
|
599
|
+
|
|
600
|
+
@property
|
|
601
|
+
def completer(self):
|
|
602
|
+
internal_commands = ["/tools", "/clear", "/exit", "/rollback"]
|
|
603
|
+
return WordCompleter(internal_commands, ignore_case=False)
|