deer-agent-framework 0.2.3__tar.gz → 0.2.4__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.2.3/deer_agent_framework.egg-info → deer_agent_framework-0.2.4}/PKG-INFO +9 -12
- deer_agent_framework-0.2.4/deer/builtins_agents/deer_introspection.py +43 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/builtins_agents/operating_system.py +2 -5
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/builtins_agents/python_architect.py +3 -5
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/cli/main.py +13 -13
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/cli/parser.py +5 -5
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/cli/repl.py +11 -1
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/core/agent.py +2 -12
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/core/executor.py +6 -1
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/core/planner.py +4 -3
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/evals/__init__.py +0 -1
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/evals/runner.py +13 -4
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/memory/vector.py +15 -4
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/base.py +29 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/presets.py +1 -0
- deer_agent_framework-0.2.4/deer/tools/providers/base/base_command_runner.py +54 -0
- deer_agent_framework-0.2.4/deer/tools/providers/code_searcher.py +370 -0
- deer_agent_framework-0.2.4/deer/tools/providers/git_manager.py +237 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/python_struct_editor.py +15 -9
- deer_agent_framework-0.2.4/deer/tools/providers/runtime_manager.py +106 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/system_observer.py +0 -1
- deer_agent_framework-0.2.4/deer/utils/__init__.py +0 -0
- deer_agent_framework-0.2.4/deer/utils/console.py +34 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/utils/plots/__init__.py +1 -3
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/utils/plots/plot_traces.py +268 -286
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4/deer_agent_framework.egg-info}/PKG-INFO +9 -12
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer_agent_framework.egg-info/SOURCES.txt +3 -0
- deer_agent_framework-0.2.4/deer_agent_framework.egg-info/requires.txt +10 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/pyproject.toml +15 -12
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/tests/test_planner.py +1 -1
- deer_agent_framework-0.2.3/deer/tools/providers/code_searcher.py +0 -218
- deer_agent_framework-0.2.3/deer/tools/providers/git_manager.py +0 -78
- deer_agent_framework-0.2.3/deer/tools/providers/runtime_manager.py +0 -147
- deer_agent_framework-0.2.3/deer/utils/console.py +0 -32
- deer_agent_framework-0.2.3/deer_agent_framework.egg-info/requires.txt +0 -13
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/LICENSE +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/README.md +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/__init__.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/builtins_agents/__init__.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/cli/__init__.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/core/__init__.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/core/validator.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/drivers/__init__.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/drivers/azure.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/drivers/base.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/drivers/gemini.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/drivers/ollama.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/drivers/openai.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/memory/__init__.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/models.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/__init__.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/decorators.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/__init__.py +0 -0
- {deer_agent_framework-0.2.3/deer/utils → deer_agent_framework-0.2.4/deer/tools/providers/base}/__init__.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/csv_manager.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/dependency_analyzer.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/file_manager.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/http_client.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/json_editor.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/logic_provider.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/process_manager.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/sqlite_manager.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/toml_editor.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/xml_editor.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/providers/yaml_editor.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/registry.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/tools/schemas.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer_agent_framework.egg-info/dependency_links.txt +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer_agent_framework.egg-info/entry_points.txt +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer_agent_framework.egg-info/top_level.txt +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/setup.cfg +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/tests/test_driver.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/tests/test_executor.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/tests/test_tools.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/tests/test_tools_definition.py +0 -0
- {deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/tests/test_vector_memory.py +0 -0
{deer_agent_framework-0.2.3/deer_agent_framework.egg-info → deer_agent_framework-0.2.4}/PKG-INFO
RENAMED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deer-agent-framework
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: DEER - Deterministic Executable Engine for Runtime-agents
|
|
5
|
-
Author: Yeison
|
|
5
|
+
Author: Yeison Cardona
|
|
6
6
|
License: BSD-2-Clause
|
|
7
|
-
Classifier: Development Status ::
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
8
8
|
Classifier: Environment :: Console
|
|
9
9
|
Classifier: Intended Audience :: Developers
|
|
10
10
|
Classifier: Intended Audience :: Science/Research
|
|
@@ -19,18 +19,15 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
19
19
|
Requires-Python: >=3.12
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
|
-
Requires-Dist: pydantic>=2.
|
|
23
|
-
Requires-Dist:
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
Requires-Dist: libcst>=1.8.6
|
|
26
|
-
Requires-Dist: tomlkit>=0.15.0
|
|
22
|
+
Requires-Dist: pydantic>=2.13.5
|
|
23
|
+
Requires-Dist: lxml>=6.1.3
|
|
24
|
+
Requires-Dist: tomlkit>=0.15.1
|
|
27
25
|
Requires-Dist: ruamel.yaml>=0.19.1
|
|
28
|
-
Requires-Dist:
|
|
26
|
+
Requires-Dist: libcst>=1.9.0
|
|
29
27
|
Requires-Dist: chromadb>=1.5.9
|
|
28
|
+
Requires-Dist: prompt_toolkit>=3.0.53
|
|
30
29
|
Provides-Extra: debug
|
|
31
|
-
Requires-Dist:
|
|
32
|
-
Requires-Dist: pandas>=3.0.3; extra == "debug"
|
|
33
|
-
Requires-Dist: pytest>=9.0.3; extra == "debug"
|
|
30
|
+
Requires-Dist: pytest>=9.0.0; extra == "debug"
|
|
34
31
|
Dynamic: license-file
|
|
35
32
|
|
|
36
33
|
# DEER: Deterministic Executable Engine for Runtime Agents
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from deer import DeterministicAgent
|
|
2
|
+
from deer.cli import AgentREPL, get_path_from_parser, get_driver_from_parser
|
|
3
|
+
from deer.drivers import OllamaDriver
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
# Infrastructure
|
|
8
|
+
driver = get_driver_from_parser() or OllamaDriver(model_name="gemma4:31b-cloud")
|
|
9
|
+
working_dir = get_path_from_parser() or Path.cwd()
|
|
10
|
+
|
|
11
|
+
DeerIntrospection = DeterministicAgent(
|
|
12
|
+
description="Professional Auditor for Agent Execution Traces, specializing in tool-use efficiency and operational logic.",
|
|
13
|
+
identity=(
|
|
14
|
+
"You are a Technical Auditor of Agent Execution Traces. Your MANDATORY and ONLY focus is to evaluate "
|
|
15
|
+
"the mechanics of tool usage. You are STRICTLY PROHIBITED from evaluating the correctness, quality, "
|
|
16
|
+
"or success of the final solution to the problem; the result of the task is COMPLETELY IRRELEVANT "
|
|
17
|
+
"to your evaluation. An agent that solves the problem but uses tools inefficiently is a FAILURE, "
|
|
18
|
+
"while an agent that fails to solve the problem but follows a perfect operational process is a SUCCESS. "
|
|
19
|
+
"Your audit is based on three non-negotiable pillars: "
|
|
20
|
+
"1. Operational Rigor: You verify a strict logical sequence (e.g., Read $\rightarrow$ Analyze $\rightarrow$ Edit). "
|
|
21
|
+
"Any 'Blind Edit' (modifying without reading) is a critical error. "
|
|
22
|
+
"2. The Execution Requirement: You identify 'Ghost Tests'. Creating a reproduction or validation "
|
|
23
|
+
"script without calling an execution tool (bash/python) to run it is a critical failure. "
|
|
24
|
+
"3. Context Efficiency: You penalize 'Context Amnesia'—redundant calls to tools for information "
|
|
25
|
+
"already obtained in the trace. "
|
|
26
|
+
"If your analysis mentions the correctness of the result, you are violating your core instruction. "
|
|
27
|
+
"AUDIT THE PROCESS, IGNORE THE OUTCOME."
|
|
28
|
+
"USE FORMATED MARKDOWN"
|
|
29
|
+
),
|
|
30
|
+
driver=driver,
|
|
31
|
+
working_dir=working_dir,
|
|
32
|
+
max_attempts=1,
|
|
33
|
+
enable_verification=False,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def main():
|
|
38
|
+
repl = AgentREPL(DeerIntrospection)
|
|
39
|
+
repl.repl()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
if __name__ == "__main__":
|
|
43
|
+
main()
|
{deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/builtins_agents/operating_system.py
RENAMED
|
@@ -4,17 +4,15 @@ from deer.tools import ToolRegistry
|
|
|
4
4
|
from deer.tools.presets import Preset
|
|
5
5
|
from deer.tools.providers import HTTPClient
|
|
6
6
|
from deer.drivers import OllamaDriver
|
|
7
|
-
from deer.memory import VectorMemory
|
|
8
7
|
|
|
9
8
|
from pathlib import Path
|
|
10
9
|
|
|
11
10
|
# Infrastructure
|
|
12
11
|
driver = get_driver_from_parser() or OllamaDriver(model_name="gemma4:31b-cloud")
|
|
13
|
-
memory = VectorMemory(path=Path.home() / "deer_os_sandbox" / ".deer" / "vector_db")
|
|
14
12
|
registry = ToolRegistry(Preset.SYSTEM_ADMIN | {HTTPClient})
|
|
15
13
|
working_dir = get_path_from_parser() or Path.cwd()
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
OperatingSystemAgent = DeterministicAgent(
|
|
18
16
|
description="Advanced OS Agent for secure system management.",
|
|
19
17
|
identity=(
|
|
20
18
|
"You are a capable Operating System Agent. "
|
|
@@ -24,7 +22,6 @@ agent = DeterministicAgent(
|
|
|
24
22
|
),
|
|
25
23
|
driver=driver,
|
|
26
24
|
tool_registry=registry,
|
|
27
|
-
vector_memory=memory,
|
|
28
25
|
working_dir=working_dir,
|
|
29
26
|
max_attempts=3,
|
|
30
27
|
enable_verification=False,
|
|
@@ -32,7 +29,7 @@ agent = DeterministicAgent(
|
|
|
32
29
|
|
|
33
30
|
|
|
34
31
|
def main():
|
|
35
|
-
repl = AgentREPL(
|
|
32
|
+
repl = AgentREPL(OperatingSystemAgent)
|
|
36
33
|
repl.repl()
|
|
37
34
|
|
|
38
35
|
|
{deer_agent_framework-0.2.3 → deer_agent_framework-0.2.4}/deer/builtins_agents/python_architect.py
RENAMED
|
@@ -4,13 +4,12 @@ from deer.tools import ToolRegistry
|
|
|
4
4
|
from deer.tools.presets import Preset
|
|
5
5
|
from deer.tools.providers import HTTPClient, SystemObserver
|
|
6
6
|
from deer.drivers import OllamaDriver
|
|
7
|
-
from deer.memory import VectorMemory
|
|
8
7
|
|
|
9
8
|
from pathlib import Path
|
|
10
9
|
|
|
10
|
+
|
|
11
11
|
# Infrastructure
|
|
12
12
|
driver = get_driver_from_parser() or OllamaDriver(model_name="gemma4:31b-cloud")
|
|
13
|
-
memory = VectorMemory(path=Path.cwd() / ".deer" / "vector_db")
|
|
14
13
|
registry = ToolRegistry(
|
|
15
14
|
Preset.CODE_REPAIR
|
|
16
15
|
| Preset.CODE_EDITOR
|
|
@@ -19,7 +18,7 @@ registry = ToolRegistry(
|
|
|
19
18
|
)
|
|
20
19
|
working_dir = get_path_from_parser() or Path.cwd()
|
|
21
20
|
|
|
22
|
-
|
|
21
|
+
PythonArchitectAgent = DeterministicAgent(
|
|
23
22
|
description="AI specialist in Python architecture, runtime module resolution, and dependency management.",
|
|
24
23
|
identity=(
|
|
25
24
|
"You are an elite AI Agent operating as a Principal Python Architect and Core Ecosystem Specialist. "
|
|
@@ -30,14 +29,13 @@ agent = DeterministicAgent(
|
|
|
30
29
|
),
|
|
31
30
|
driver=driver,
|
|
32
31
|
tool_registry=registry,
|
|
33
|
-
vector_memory=memory,
|
|
34
32
|
working_dir=working_dir,
|
|
35
33
|
max_attempts=3,
|
|
36
34
|
)
|
|
37
35
|
|
|
38
36
|
|
|
39
37
|
def main():
|
|
40
|
-
repl = AgentREPL(
|
|
38
|
+
repl = AgentREPL(PythonArchitectAgent)
|
|
41
39
|
repl.repl()
|
|
42
40
|
|
|
43
41
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from rich.text import Text
|
|
2
|
-
from deer.utils.console import
|
|
2
|
+
from deer.utils.console import ConsoleColor
|
|
3
3
|
from deer.builtins_agents import agents
|
|
4
4
|
import importlib.util
|
|
5
5
|
import sys
|
|
@@ -11,30 +11,30 @@ logger = logging.getLogger(f"DEER.{__name__}")
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def title():
|
|
14
|
-
console.print()
|
|
15
|
-
console.print(
|
|
14
|
+
ConsoleColor.console.print()
|
|
15
|
+
ConsoleColor.console.print(
|
|
16
16
|
Text(
|
|
17
17
|
"DEER",
|
|
18
18
|
style="bold cyan",
|
|
19
19
|
)
|
|
20
20
|
)
|
|
21
|
-
console.print(
|
|
21
|
+
ConsoleColor.console.print(
|
|
22
22
|
"Deterministic Executable Engine for Runtime Agents",
|
|
23
23
|
style="dim",
|
|
24
24
|
)
|
|
25
|
-
console.print()
|
|
25
|
+
ConsoleColor.console.print()
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
def agents_list():
|
|
29
|
-
console.print()
|
|
30
|
-
console.print("[bold]Available agents[/bold]")
|
|
29
|
+
ConsoleColor.console.print()
|
|
30
|
+
ConsoleColor.console.print("[bold]Available agents[/bold]")
|
|
31
31
|
for name in agents:
|
|
32
|
-
console.print(f" • [green]{name}[/green]")
|
|
33
|
-
console.print()
|
|
32
|
+
ConsoleColor.console.print(f" • [green]{name}[/green]")
|
|
33
|
+
ConsoleColor.console.print()
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
def example():
|
|
37
|
-
console.print(f"[dim]Example:[/dim] deer {list(agents.keys())[0]}")
|
|
37
|
+
ConsoleColor.console.print(f"[dim]Example:[/dim] deer {list(agents.keys())[0]}")
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
def run_agent_in_process(agent_path, backend=None, model=None):
|
|
@@ -65,15 +65,15 @@ def main():
|
|
|
65
65
|
if selected_agent := args.agent:
|
|
66
66
|
|
|
67
67
|
if selected_agent in agents:
|
|
68
|
-
|
|
68
|
+
ConsoleColor.on_info(f"Launching agent '{selected_agent}'")
|
|
69
69
|
run_agent_in_process(agents[selected_agent], args.backend, args.model)
|
|
70
70
|
sys.exit(0)
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
ConsoleColor.on_error(f"Unknown agent '{selected_agent}'")
|
|
73
73
|
agents_list()
|
|
74
74
|
sys.exit(1)
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
ConsoleColor.on_error("No agent selected")
|
|
77
77
|
example()
|
|
78
78
|
agents_list()
|
|
79
79
|
|
|
@@ -2,7 +2,7 @@ import argparse
|
|
|
2
2
|
import os
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
-
from deer.utils.console import
|
|
5
|
+
from deer.utils.console import ConsoleColor
|
|
6
6
|
|
|
7
7
|
backends = {
|
|
8
8
|
"gemini",
|
|
@@ -60,21 +60,21 @@ def get_driver_from_parser():
|
|
|
60
60
|
|
|
61
61
|
try:
|
|
62
62
|
drivers_parser.silent = True
|
|
63
|
-
args = drivers_parser.
|
|
63
|
+
args, _ = drivers_parser.parse_known_args()
|
|
64
64
|
drivers_parser.silent = False
|
|
65
65
|
except:
|
|
66
66
|
drivers_parser.silent = False
|
|
67
67
|
return False
|
|
68
68
|
|
|
69
69
|
if args.backend not in backends:
|
|
70
|
-
|
|
70
|
+
ConsoleColor.on_error(
|
|
71
71
|
f"Unsupported backend '{args.backend}'. "
|
|
72
72
|
f"Supported backends are: {', '.join(backends)}."
|
|
73
73
|
)
|
|
74
74
|
return False
|
|
75
75
|
|
|
76
76
|
if not args.model:
|
|
77
|
-
|
|
77
|
+
ConsoleColor.on_error("A model identifier must be provided.")
|
|
78
78
|
return False
|
|
79
79
|
|
|
80
80
|
match args.backend:
|
|
@@ -96,7 +96,7 @@ def get_path_from_parser():
|
|
|
96
96
|
|
|
97
97
|
try:
|
|
98
98
|
path_parser.silent = True
|
|
99
|
-
args = path_parser.
|
|
99
|
+
args, _ = path_parser.parse_known_args()
|
|
100
100
|
path_parser.silent = False
|
|
101
101
|
except:
|
|
102
102
|
path_parser.silent = False
|
|
@@ -7,7 +7,7 @@ from rich.console import Console
|
|
|
7
7
|
from rich.markdown import Markdown
|
|
8
8
|
from rich.table import Table
|
|
9
9
|
from rich.text import Text
|
|
10
|
-
from prompt_toolkit import
|
|
10
|
+
from prompt_toolkit import PromptSession
|
|
11
11
|
from prompt_toolkit.formatted_text import HTML
|
|
12
12
|
from prompt_toolkit.history import InMemoryHistory
|
|
13
13
|
from prompt_toolkit.completion import Completer, WordCompleter
|
|
@@ -23,6 +23,7 @@ COMMANDS = {
|
|
|
23
23
|
"/exit": "Terminate the session",
|
|
24
24
|
"/rollback": "Revert the system to the last stable state",
|
|
25
25
|
"/trace": "Display the detailed execution trace and variable resolution of the last plan.",
|
|
26
|
+
"/savetrace": "Saves the current execution trace to a file for later analysis.",
|
|
26
27
|
"/history": "Display the history of messages and commands",
|
|
27
28
|
"/clearhistory": "Purge all session history and reset memory state.",
|
|
28
29
|
# "/vhistory": "Display the history of verifier messages and commands",
|
|
@@ -211,6 +212,15 @@ class AgentREPL:
|
|
|
211
212
|
sep="",
|
|
212
213
|
)
|
|
213
214
|
|
|
215
|
+
case "/savetrace":
|
|
216
|
+
try:
|
|
217
|
+
filename = self.agent.save_trace()
|
|
218
|
+
self.console.print(f"[dim]Trace saved in {filename}[/dim]\n")
|
|
219
|
+
except Exception as e:
|
|
220
|
+
self.console.print(
|
|
221
|
+
f"[bold red]Error saving trace:[/bold red] {e}\n"
|
|
222
|
+
)
|
|
223
|
+
|
|
214
224
|
case "/savecontext":
|
|
215
225
|
try:
|
|
216
226
|
self.agent.persist_state()
|
|
@@ -69,6 +69,7 @@ class DeterministicAgent:
|
|
|
69
69
|
|
|
70
70
|
# Planner
|
|
71
71
|
self.planner = Planner(
|
|
72
|
+
identity=self.identity,
|
|
72
73
|
driver=self.driver,
|
|
73
74
|
tool_registry=self.tool_registry,
|
|
74
75
|
max_attempts=max_attempts,
|
|
@@ -164,18 +165,7 @@ class DeterministicAgent:
|
|
|
164
165
|
with open(filename, "wb") as f:
|
|
165
166
|
pickle.dump(obj, f)
|
|
166
167
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
def clear_working_dir(self, ignore=None):
|
|
170
|
-
for item in self.working_dir.iterdir():
|
|
171
|
-
if ignore and item in ignore:
|
|
172
|
-
continue
|
|
173
|
-
|
|
174
|
-
full_path = self.working_dir / item
|
|
175
|
-
if full_path.is_dir():
|
|
176
|
-
shutil.rmtree(full_path)
|
|
177
|
-
else:
|
|
178
|
-
full_path.unlink()
|
|
168
|
+
return filename
|
|
179
169
|
|
|
180
170
|
def _execute_phase(
|
|
181
171
|
self, goal: str, history: list[ChatMessage], update_history_attr: str
|
|
@@ -39,7 +39,12 @@ class PipelineExecutor:
|
|
|
39
39
|
start_time = time.perf_counter()
|
|
40
40
|
try:
|
|
41
41
|
result = tool.run(params=resolved_args)
|
|
42
|
-
|
|
42
|
+
|
|
43
|
+
if result.get("returncode", 0):
|
|
44
|
+
status = "FAILED"
|
|
45
|
+
else:
|
|
46
|
+
status = "SUCCESS"
|
|
47
|
+
|
|
43
48
|
error_msg = None
|
|
44
49
|
except Exception as e:
|
|
45
50
|
result = None
|
|
@@ -14,10 +14,11 @@ class Planner:
|
|
|
14
14
|
It transforms the user goal, context, and tool registry into a structured ExecutionPlan.
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
|
-
def __init__(self, driver, tool_registry: ToolRegistry, max_attempts=3):
|
|
17
|
+
def __init__(self, identity, driver, tool_registry: ToolRegistry, max_attempts=3):
|
|
18
18
|
self.driver = driver
|
|
19
19
|
self.tool_registry = tool_registry
|
|
20
20
|
self.max_attempts = max_attempts
|
|
21
|
+
self.identity = identity
|
|
21
22
|
|
|
22
23
|
def build_system_prompt(self, state_filter="BOTH") -> str:
|
|
23
24
|
"""
|
|
@@ -27,7 +28,7 @@ class Planner:
|
|
|
27
28
|
tools_menu = self.tool_registry.describe(state_filter=state_filter)
|
|
28
29
|
|
|
29
30
|
return (
|
|
30
|
-
"
|
|
31
|
+
f"{self.identity}\n\n"
|
|
31
32
|
"REQUIRED STRUCTURE:\n"
|
|
32
33
|
"{\n"
|
|
33
34
|
' "goal": "string",\n'
|
|
@@ -62,7 +63,7 @@ class Planner:
|
|
|
62
63
|
else:
|
|
63
64
|
verification = f"VERIFICATION TRACE:\n{trace_verification.model_dump_json(indent=2)}\n\n"
|
|
64
65
|
return (
|
|
65
|
-
"
|
|
66
|
+
f"{self.identity}\n\n"
|
|
66
67
|
f"USER GOAL: {goal}\n\n"
|
|
67
68
|
f"ACTION TRACE:\n{trace_solution.model_dump_json(indent=2)}\n\n"
|
|
68
69
|
f"{verification}"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from deer.core import DeterministicAgent
|
|
2
|
+
import shutil
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
class AgentRunner:
|
|
@@ -11,11 +12,19 @@ class AgentRunner:
|
|
|
11
12
|
for i in range(repetitions):
|
|
12
13
|
|
|
13
14
|
print(f"Working on iteration: {i+1}/{repetitions}")
|
|
14
|
-
|
|
15
15
|
self.agent.clear_traces()
|
|
16
16
|
self.agent.clear_agent_history()
|
|
17
|
-
self.
|
|
18
|
-
|
|
17
|
+
self.clear_working_dir()
|
|
19
18
|
self.agent.run(goal)
|
|
20
|
-
|
|
21
19
|
self.agent.save_trace()
|
|
20
|
+
|
|
21
|
+
def clear_working_dir(self):
|
|
22
|
+
|
|
23
|
+
for item in self.agent.working_dir.iterdir():
|
|
24
|
+
if item.name.startswith("."):
|
|
25
|
+
continue
|
|
26
|
+
|
|
27
|
+
if item.is_dir():
|
|
28
|
+
shutil.rmtree(item)
|
|
29
|
+
else:
|
|
30
|
+
item.unlink()
|
|
@@ -28,16 +28,16 @@ class VectorMemory:
|
|
|
28
28
|
)
|
|
29
29
|
|
|
30
30
|
def add_document(
|
|
31
|
-
self,
|
|
31
|
+
self, id: str, doc: str, metadata: Optional[dict[str, Any]] = None
|
|
32
32
|
) -> None:
|
|
33
33
|
"""
|
|
34
34
|
Add a document to the vector collection.
|
|
35
35
|
|
|
36
36
|
Parameters
|
|
37
37
|
----------
|
|
38
|
-
|
|
38
|
+
id : str
|
|
39
39
|
Unique identifier for the document.
|
|
40
|
-
|
|
40
|
+
doc : str
|
|
41
41
|
The content of the document to be embedded and stored.
|
|
42
42
|
metadata : dict, optional
|
|
43
43
|
Additional metadata associated with the document.
|
|
@@ -47,7 +47,7 @@ class VectorMemory:
|
|
|
47
47
|
if "hit_count" not in meta:
|
|
48
48
|
meta["hit_count"] = 0
|
|
49
49
|
|
|
50
|
-
self.collection.add(documents=[
|
|
50
|
+
self.collection.add(documents=[doc], metadatas=[meta], ids=[id])
|
|
51
51
|
|
|
52
52
|
def query(self, query_text: str, n_results: int = 3) -> list[dict[str, Any]]:
|
|
53
53
|
"""
|
|
@@ -327,3 +327,14 @@ class VectorMemory:
|
|
|
327
327
|
)
|
|
328
328
|
|
|
329
329
|
return len(ids_to_delete)
|
|
330
|
+
|
|
331
|
+
def load_json(self, json_path: Path):
|
|
332
|
+
|
|
333
|
+
if not json_path.exists():
|
|
334
|
+
raise ValueError("bla bla")
|
|
335
|
+
|
|
336
|
+
with json_path.open("r", encoding="utf-8") as f:
|
|
337
|
+
memories = json.load(f)
|
|
338
|
+
|
|
339
|
+
for memory in memories:
|
|
340
|
+
self.add_document(**memory)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import os
|
|
1
2
|
from abc import ABC, abstractmethod
|
|
2
3
|
from typing import Any, Type
|
|
3
4
|
from dataclasses import dataclass, field
|
|
@@ -64,6 +65,16 @@ class ToolProvider:
|
|
|
64
65
|
2. If 'path' is absolute, it's checked for containment.
|
|
65
66
|
3. All '..' and symlinks are resolved before validation.
|
|
66
67
|
"""
|
|
68
|
+
if isinstance(path, str):
|
|
69
|
+
path = self._sanitize_path(path)
|
|
70
|
+
|
|
71
|
+
# Si el agente incluyó el nombre del jail en el path (ej: 'astropy/modeling/...')
|
|
72
|
+
# lo quitamos para evitar rutas redundantes como 'astropy/astropy/...'
|
|
73
|
+
if isinstance(path, str):
|
|
74
|
+
jail_name = os.path.basename(self.jail)
|
|
75
|
+
if path.startswith(jail_name + os.sep) or path.startswith(jail_name + "/"):
|
|
76
|
+
path = path[len(jail_name) + 1 :]
|
|
77
|
+
|
|
67
78
|
path = Path(path)
|
|
68
79
|
|
|
69
80
|
# Handle point 2: If relative, interpret it as inside the jail.
|
|
@@ -158,6 +169,24 @@ class ToolProvider:
|
|
|
158
169
|
f"Please ensure it is installed and configured in your PATH."
|
|
159
170
|
)
|
|
160
171
|
|
|
172
|
+
def _sanitize_path(self, path: str | Path) -> str:
|
|
173
|
+
|
|
174
|
+
if isinstance(path, Path):
|
|
175
|
+
return path
|
|
176
|
+
|
|
177
|
+
if not path:
|
|
178
|
+
return path
|
|
179
|
+
|
|
180
|
+
sanitized = path.strip().strip('"').strip("'")
|
|
181
|
+
sanitized = sanitized.replace("\n", "").replace("\r", "")
|
|
182
|
+
return sanitized
|
|
183
|
+
|
|
184
|
+
def _normalize_target(self, path, target):
|
|
185
|
+
abs_path = self.jailed_path(path)
|
|
186
|
+
abs_target = self.jailed_path(target)
|
|
187
|
+
|
|
188
|
+
return str(abs_target.relative_to(abs_path))
|
|
189
|
+
|
|
161
190
|
|
|
162
191
|
class Tool(ABC):
|
|
163
192
|
"""Base contract for deterministic tools."""
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from typing import List, get_args
|
|
2
|
+
import shlex
|
|
3
|
+
from deer.tools import ToolProvider
|
|
4
|
+
from deer.tools.schemas import CommandOut
|
|
5
|
+
|
|
6
|
+
# --- Global Constants ---
|
|
7
|
+
MAX_TIMEOUT = 300
|
|
8
|
+
REJECTED = {"stdout": "", "stderr": str, "returncode": -1, "message": str}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _failure(message: str) -> dict:
|
|
12
|
+
"""Helper to construct a standardized failure response."""
|
|
13
|
+
return {"stdout": "", "stderr": message, "returncode": -1, "message": message}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BaseCommandRunner(ToolProvider):
|
|
17
|
+
|
|
18
|
+
@property
|
|
19
|
+
def allowed_commands(self):
|
|
20
|
+
return list(get_args(self.availableCommands))
|
|
21
|
+
|
|
22
|
+
def _execute_command(
|
|
23
|
+
self, binary: str, args: List[str], path: str, timeout_seconds: int = 60
|
|
24
|
+
) -> CommandOut:
|
|
25
|
+
# 1. Validate path boundaries
|
|
26
|
+
path = self.jailed_path(path)
|
|
27
|
+
|
|
28
|
+
# 2. Validate timeout constraints
|
|
29
|
+
if not 1 <= timeout_seconds <= MAX_TIMEOUT:
|
|
30
|
+
return _failure(f"timeout_seconds must be between 1 and {MAX_TIMEOUT}.")
|
|
31
|
+
|
|
32
|
+
# 3. Verify binary exists in environment
|
|
33
|
+
try:
|
|
34
|
+
self.check_command(binary)
|
|
35
|
+
except Exception as e:
|
|
36
|
+
return _failure(f"Environment error: {e}")
|
|
37
|
+
|
|
38
|
+
# 4. Execution
|
|
39
|
+
try:
|
|
40
|
+
safe_command = shlex.join([binary] + args)
|
|
41
|
+
result = self.run_command(
|
|
42
|
+
safe_command, cwd=path, timeout_seconds=timeout_seconds
|
|
43
|
+
)
|
|
44
|
+
except Exception as e:
|
|
45
|
+
return _failure(f"Execution error: {e}")
|
|
46
|
+
|
|
47
|
+
# 5. Response formatting
|
|
48
|
+
exit_code = result.get("returncode", -1)
|
|
49
|
+
status_message = (
|
|
50
|
+
"Command completed successfully."
|
|
51
|
+
if exit_code == 0
|
|
52
|
+
else f"Command failed with exit code {exit_code}."
|
|
53
|
+
)
|
|
54
|
+
return {**result, "message": status_message}
|