deer-agent-framework 0.2a1__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.2a1/deer_agent_framework.egg-info → deer_agent_framework-0.2.4}/PKG-INFO +9 -12
- {deer_agent_framework-0.2a1/deer/builtins/agents → deer_agent_framework-0.2.4/deer/builtins_agents}/__init__.py +1 -1
- deer_agent_framework-0.2.4/deer/builtins_agents/deer_introspection.py +43 -0
- {deer_agent_framework-0.2a1/deer/builtins/agents → deer_agent_framework-0.2.4/deer/builtins_agents}/operating_system.py +3 -6
- {deer_agent_framework-0.2a1/deer/builtins/agents → deer_agent_framework-0.2.4/deer/builtins_agents}/python_architect.py +3 -5
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/cli/main.py +18 -20
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/cli/parser.py +5 -5
- deer_agent_framework-0.2.4/deer/cli/repl.py +378 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/core/agent.py +52 -21
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/core/executor.py +7 -2
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/core/planner.py +6 -5
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/core/validator.py +1 -1
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/drivers/base.py +6 -4
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/drivers/ollama.py +8 -5
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/evals/__init__.py +0 -1
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/evals/runner.py +13 -4
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/memory/vector.py +16 -5
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/models.py +3 -3
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/base.py +29 -0
- {deer_agent_framework-0.2a1 → 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.2a1 → 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.2a1 → 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.2a1 → deer_agent_framework-0.2.4}/deer/utils/plots/__init__.py +1 -3
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/utils/plots/plot_traces.py +268 -286
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4/deer_agent_framework.egg-info}/PKG-INFO +9 -12
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer_agent_framework.egg-info/SOURCES.txt +6 -3
- deer_agent_framework-0.2.4/deer_agent_framework.egg-info/requires.txt +10 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/pyproject.toml +15 -12
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/tests/test_planner.py +1 -1
- deer_agent_framework-0.2a1/deer/cli/repl.py +0 -263
- deer_agent_framework-0.2a1/deer/tools/providers/code_searcher.py +0 -218
- deer_agent_framework-0.2a1/deer/tools/providers/git_manager.py +0 -78
- deer_agent_framework-0.2a1/deer/tools/providers/runtime_manager.py +0 -147
- deer_agent_framework-0.2a1/deer/utils/console.py +0 -32
- deer_agent_framework-0.2a1/deer_agent_framework.egg-info/requires.txt +0 -13
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/LICENSE +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/README.md +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/__init__.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/cli/__init__.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/core/__init__.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/drivers/__init__.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/drivers/azure.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/drivers/gemini.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/drivers/openai.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/memory/__init__.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/__init__.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/decorators.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/providers/__init__.py +0 -0
- {deer_agent_framework-0.2a1/deer/utils → deer_agent_framework-0.2.4/deer/tools/providers/base}/__init__.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/providers/csv_manager.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/providers/dependency_analyzer.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/providers/file_manager.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/providers/http_client.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/providers/json_editor.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/providers/logic_provider.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/providers/process_manager.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/providers/sqlite_manager.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/providers/toml_editor.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/providers/xml_editor.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/providers/yaml_editor.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/registry.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer/tools/schemas.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer_agent_framework.egg-info/dependency_links.txt +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer_agent_framework.egg-info/entry_points.txt +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/deer_agent_framework.egg-info/top_level.txt +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/setup.cfg +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/tests/test_driver.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/tests/test_executor.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/tests/test_tools.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/tests/test_tools_definition.py +0 -0
- {deer_agent_framework-0.2a1 → deer_agent_framework-0.2.4}/tests/test_vector_memory.py +0 -0
{deer_agent_framework-0.2a1/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.
|
|
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()
|
|
@@ -4,18 +4,16 @@ 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
|
-
|
|
18
|
-
description="OS Agent",
|
|
15
|
+
OperatingSystemAgent = DeterministicAgent(
|
|
16
|
+
description="Advanced OS Agent for secure system management.",
|
|
19
17
|
identity=(
|
|
20
18
|
"You are a capable Operating System Agent. "
|
|
21
19
|
"Your goal is to assist the user by managing the system, executing tasks, and solving problems efficiently. "
|
|
@@ -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
|
|
|
@@ -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,40 +1,40 @@
|
|
|
1
1
|
from rich.text import Text
|
|
2
|
-
from deer.utils.console import
|
|
3
|
-
from deer.
|
|
2
|
+
from deer.utils.console import ConsoleColor
|
|
3
|
+
from deer.builtins_agents import agents
|
|
4
4
|
import importlib.util
|
|
5
5
|
import sys
|
|
6
6
|
import logging
|
|
7
7
|
|
|
8
8
|
from .parser import drivers_parser
|
|
9
9
|
|
|
10
|
-
logger = logging.getLogger("DEER")
|
|
10
|
+
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]")
|
|
31
|
-
for name in
|
|
32
|
-
console.print(f" • [green]{name}[/green]")
|
|
33
|
-
console.print()
|
|
29
|
+
ConsoleColor.console.print()
|
|
30
|
+
ConsoleColor.console.print("[bold]Available agents[/bold]")
|
|
31
|
+
for name in agents:
|
|
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(
|
|
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):
|
|
@@ -64,18 +64,16 @@ def main():
|
|
|
64
64
|
|
|
65
65
|
if selected_agent := args.agent:
|
|
66
66
|
|
|
67
|
-
if selected_agent in
|
|
68
|
-
|
|
69
|
-
run_agent_in_process(
|
|
70
|
-
available_agents[selected_agent], args.backend, args.model
|
|
71
|
-
)
|
|
67
|
+
if selected_agent in agents:
|
|
68
|
+
ConsoleColor.on_info(f"Launching agent '{selected_agent}'")
|
|
69
|
+
run_agent_in_process(agents[selected_agent], args.backend, args.model)
|
|
72
70
|
sys.exit(0)
|
|
73
71
|
|
|
74
|
-
|
|
72
|
+
ConsoleColor.on_error(f"Unknown agent '{selected_agent}'")
|
|
75
73
|
agents_list()
|
|
76
74
|
sys.exit(1)
|
|
77
75
|
|
|
78
|
-
|
|
76
|
+
ConsoleColor.on_error("No agent selected")
|
|
79
77
|
example()
|
|
80
78
|
agents_list()
|
|
81
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
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import pickle
|
|
3
|
+
import logging
|
|
4
|
+
import subprocess
|
|
5
|
+
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
from rich.markdown import Markdown
|
|
8
|
+
from rich.table import Table
|
|
9
|
+
from rich.text import Text
|
|
10
|
+
from prompt_toolkit import PromptSession
|
|
11
|
+
from prompt_toolkit.formatted_text import HTML
|
|
12
|
+
from prompt_toolkit.history import InMemoryHistory
|
|
13
|
+
from prompt_toolkit.completion import Completer, WordCompleter
|
|
14
|
+
|
|
15
|
+
from deer.core import DeterministicAgent
|
|
16
|
+
from deer import __version__
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(f"DEER.{__name__}")
|
|
19
|
+
|
|
20
|
+
COMMANDS = {
|
|
21
|
+
"/tools": "Show tools and capabilities",
|
|
22
|
+
"/clear": "Clear the console and reset the session",
|
|
23
|
+
"/exit": "Terminate the session",
|
|
24
|
+
"/rollback": "Revert the system to the last stable state",
|
|
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.",
|
|
27
|
+
"/history": "Display the history of messages and commands",
|
|
28
|
+
"/clearhistory": "Purge all session history and reset memory state.",
|
|
29
|
+
# "/vhistory": "Display the history of verifier messages and commands",
|
|
30
|
+
"/infocontext": "Displays context memory usage information.",
|
|
31
|
+
"/loadcontext": "Restore a saved session context",
|
|
32
|
+
"/savecontext": "Save the current session context",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
WELCOME_MESSAGE = """
|
|
36
|
+
# DEER - Deterministic Executable Engine for Runtime Agents - v{version}
|
|
37
|
+
|
|
38
|
+
An LLM agent orchestration framework built for:
|
|
39
|
+
- Strict runtime control
|
|
40
|
+
- Enforced tool contracts
|
|
41
|
+
- Full execution traceability
|
|
42
|
+
- Guaranteed reproducibility
|
|
43
|
+
|
|
44
|
+
## Runtime Pipeline
|
|
45
|
+
|
|
46
|
+
1. Plan → Generate a structured execution strategy
|
|
47
|
+
2. Validate → Verify contracts, dependencies, and safety
|
|
48
|
+
3. Execute → Run approved tools and workflows
|
|
49
|
+
4. Verify → Validate outputs against expected objectives
|
|
50
|
+
|
|
51
|
+
## System Guarantees
|
|
52
|
+
|
|
53
|
+
- Deterministic execution flow
|
|
54
|
+
- Typed tool interfaces
|
|
55
|
+
- Structured validation layers
|
|
56
|
+
- Full execution traceability
|
|
57
|
+
- Backend-governed orchestration
|
|
58
|
+
|
|
59
|
+
## Commands
|
|
60
|
+
|
|
61
|
+
{commands}
|
|
62
|
+
|
|
63
|
+
## System Escapes
|
|
64
|
+
|
|
65
|
+
- `!<command>` → Execute an OS shell command (e.g., `!ls -la` or `!pwd`)
|
|
66
|
+
|
|
67
|
+
## Shortcuts
|
|
68
|
+
|
|
69
|
+
- `Tab` → Auto-complete commands and navigate suggestions
|
|
70
|
+
- `Ctrl+C` → Stop processing the current task
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class SmartCommandCompleter(Completer):
|
|
75
|
+
def __init__(self, commands):
|
|
76
|
+
self.base_completer = WordCompleter(commands, ignore_case=False)
|
|
77
|
+
|
|
78
|
+
def get_completions(self, document, complete_event):
|
|
79
|
+
word_before_cursor = document.get_word_before_cursor()
|
|
80
|
+
if not word_before_cursor:
|
|
81
|
+
return []
|
|
82
|
+
return self.base_completer.get_completions(document, complete_event)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class AgentREPL:
|
|
86
|
+
|
|
87
|
+
def __init__(self, agent: DeterministicAgent):
|
|
88
|
+
self.agent = agent
|
|
89
|
+
|
|
90
|
+
self.prompt_history: InMemoryHistory
|
|
91
|
+
self.load_prompt_history()
|
|
92
|
+
|
|
93
|
+
self.console = Console()
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def completer(self):
|
|
97
|
+
internal_commands = list(COMMANDS.keys())
|
|
98
|
+
return SmartCommandCompleter(internal_commands)
|
|
99
|
+
|
|
100
|
+
def load_prompt_history(self):
|
|
101
|
+
history_file = self.agent.agent_dir / "prompt_history"
|
|
102
|
+
|
|
103
|
+
if not history_file.exists():
|
|
104
|
+
self.prompt_history = InMemoryHistory()
|
|
105
|
+
return
|
|
106
|
+
|
|
107
|
+
with open(history_file, "rb") as f:
|
|
108
|
+
self.prompt_history = pickle.load(f)
|
|
109
|
+
return
|
|
110
|
+
|
|
111
|
+
def save_prompt_history(self):
|
|
112
|
+
history_file = self.agent.agent_dir / "prompt_history"
|
|
113
|
+
with open(history_file, "wb") as f:
|
|
114
|
+
pickle.dump(self.prompt_history, f)
|
|
115
|
+
|
|
116
|
+
def clear_history(self):
|
|
117
|
+
self.history = []
|
|
118
|
+
self.trace = []
|
|
119
|
+
|
|
120
|
+
def pretty_print(self, out: str):
|
|
121
|
+
self.console.print(Markdown(out))
|
|
122
|
+
|
|
123
|
+
def show_identity(self):
|
|
124
|
+
self.pretty_print(
|
|
125
|
+
f">**Agent Profile** \n"
|
|
126
|
+
f"*{self.agent.description}* \n"
|
|
127
|
+
f"root: {self.agent.tool_registry.jail_path} \n"
|
|
128
|
+
f"{self.agent.driver}: {self.agent.driver.model_name} \n"
|
|
129
|
+
)
|
|
130
|
+
print("")
|
|
131
|
+
|
|
132
|
+
def show_welcome(self):
|
|
133
|
+
commands_formated = ""
|
|
134
|
+
for command in COMMANDS:
|
|
135
|
+
commands_formated += (
|
|
136
|
+
f"- {('`'+command+'`').ljust(12, ' ')} → {COMMANDS[command]}\n "
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
self.console.clear()
|
|
140
|
+
self.pretty_print(
|
|
141
|
+
WELCOME_MESSAGE.format(version=__version__, commands=commands_formated)
|
|
142
|
+
)
|
|
143
|
+
self.show_identity()
|
|
144
|
+
|
|
145
|
+
def run_command(self, command):
|
|
146
|
+
match command:
|
|
147
|
+
case "/exit":
|
|
148
|
+
self.agent.persist_state()
|
|
149
|
+
self.console.print(
|
|
150
|
+
"[dim]Context state persisted to disk.[/dim]\n",
|
|
151
|
+
Markdown("----"),
|
|
152
|
+
)
|
|
153
|
+
sys.exit(0)
|
|
154
|
+
|
|
155
|
+
case "/clear":
|
|
156
|
+
self.console.clear()
|
|
157
|
+
self.show_identity()
|
|
158
|
+
self.console.print(
|
|
159
|
+
"[dim]Console buffer cleared. Session state persists. Use /clearhistory for a full purge.[/dim]\n",
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
case "/clearhistory":
|
|
163
|
+
self.clear_history()
|
|
164
|
+
self.console.clear()
|
|
165
|
+
self.agent.clear_agent_history()
|
|
166
|
+
self.agent.clear_traces()
|
|
167
|
+
self.agent.persist_state()
|
|
168
|
+
self.show_identity()
|
|
169
|
+
self.console.print(
|
|
170
|
+
"[dim]Session history purged. Memory state reset.[/dim]\n",
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
case "/tools":
|
|
174
|
+
tools_description = self.agent.tool_registry.describe()
|
|
175
|
+
self.console.print(
|
|
176
|
+
Markdown(tools_description),
|
|
177
|
+
"\n",
|
|
178
|
+
f"[dim]Tool registry: {len(self.agent.tool_registry._tools)} modules active.[/dim]\n",
|
|
179
|
+
f"[dim]Registry description payload: {self.agent.format_bytes(len(tools_description))}[/dim]\n",
|
|
180
|
+
sep="",
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
case "/rollback":
|
|
184
|
+
self.console.print(
|
|
185
|
+
"[dim][bold]Rollback executed.[/bold] System reverted to the last stable state.[/dim]\n",
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
case "/trace":
|
|
189
|
+
trace = self.agent.traces["solution"]
|
|
190
|
+
for i, step in enumerate(trace):
|
|
191
|
+
self.console.print(f"[bold yellow]Trace {i+1}:[/bold yellow]")
|
|
192
|
+
print(f"{step}\n")
|
|
193
|
+
|
|
194
|
+
case "/history":
|
|
195
|
+
for i, chat in enumerate(self.agent.agent_history[1:]):
|
|
196
|
+
self.console.print(f"[bold yellow]Chat {i+1}:[/bold yellow]")
|
|
197
|
+
print(f"{chat}\n")
|
|
198
|
+
self.console.print(
|
|
199
|
+
f"[dim]Session history payload: {self.agent.format_bytes(len(str(self.agent.agent_history)))}[/dim]\n",
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
# case "/vhistory":
|
|
203
|
+
# for i, chat in enumerate(self.agent.verificator_history[1:]):
|
|
204
|
+
# self.console.print(f"[bold yellow]Chat {i+1}:[/bold yellow]")
|
|
205
|
+
# print(f"{chat}\n")
|
|
206
|
+
|
|
207
|
+
case "/infocontext":
|
|
208
|
+
bytes = sum(len(str(item)) for item in self.agent.agent_history)
|
|
209
|
+
self.console.print(
|
|
210
|
+
f"[dim][bold]History:[/bold] {len(self.agent.agent_history)} messages[/dim]\n",
|
|
211
|
+
f"[dim][bold]Context size:[/bold] ~{self.agent.format_bytes(bytes)}[/dim]\n",
|
|
212
|
+
sep="",
|
|
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
|
+
|
|
224
|
+
case "/savecontext":
|
|
225
|
+
try:
|
|
226
|
+
self.agent.persist_state()
|
|
227
|
+
self.console.print("[dim]Context state persisted to disk.[/dim]\n")
|
|
228
|
+
except Exception as e:
|
|
229
|
+
self.console.print(
|
|
230
|
+
f"[bold red]Context persistence error:[/bold red] {e}\n"
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
case "/loadcontext":
|
|
234
|
+
try:
|
|
235
|
+
self.agent.restore_state()
|
|
236
|
+
self.console.print(
|
|
237
|
+
"[dim]Context recovered. Session state synchronized.[/dim]\n"
|
|
238
|
+
)
|
|
239
|
+
except Exception as e:
|
|
240
|
+
self.console.print(
|
|
241
|
+
f"[bold red]Context restoration error:[/bold red] {e}\n"
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
self.console.print(
|
|
245
|
+
Markdown("----"),
|
|
246
|
+
end="",
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
def repl(self):
|
|
250
|
+
logger.setLevel(logging.CRITICAL)
|
|
251
|
+
self.show_welcome()
|
|
252
|
+
self.pretty_print(f"----")
|
|
253
|
+
|
|
254
|
+
session = PromptSession(
|
|
255
|
+
erase_when_done=True,
|
|
256
|
+
history=self.prompt_history,
|
|
257
|
+
completer=self.completer,
|
|
258
|
+
mouse_support=True,
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
def as_table(prompt, message="", color="cyan"):
|
|
262
|
+
table = Table.grid(expand=True)
|
|
263
|
+
table.add_column(width=len(prompt) + 1)
|
|
264
|
+
table.add_column()
|
|
265
|
+
|
|
266
|
+
table.add_row(
|
|
267
|
+
Text(prompt, style=f"bold {color}"),
|
|
268
|
+
Markdown(message),
|
|
269
|
+
)
|
|
270
|
+
return table
|
|
271
|
+
|
|
272
|
+
input_prompt = ">>> " # >>>
|
|
273
|
+
input_prompt_history = "USER |"
|
|
274
|
+
output_prompt_history = "DEER |"
|
|
275
|
+
|
|
276
|
+
while True:
|
|
277
|
+
try:
|
|
278
|
+
msg = session.prompt(
|
|
279
|
+
HTML(f"<ansicyan><b>{input_prompt}</b></ansicyan>")
|
|
280
|
+
)
|
|
281
|
+
self.save_prompt_history()
|
|
282
|
+
|
|
283
|
+
if self.agent.load_context:
|
|
284
|
+
self.agent.persist_state()
|
|
285
|
+
|
|
286
|
+
except KeyboardInterrupt:
|
|
287
|
+
continue
|
|
288
|
+
except EOFError:
|
|
289
|
+
break
|
|
290
|
+
|
|
291
|
+
msg = msg.strip()
|
|
292
|
+
if not msg:
|
|
293
|
+
continue
|
|
294
|
+
|
|
295
|
+
match msg:
|
|
296
|
+
case command if command in COMMANDS:
|
|
297
|
+
self.console.print(
|
|
298
|
+
as_table(f"[{command}]", color="cyan"),
|
|
299
|
+
Text("\n"),
|
|
300
|
+
end="",
|
|
301
|
+
)
|
|
302
|
+
self.run_command(command)
|
|
303
|
+
|
|
304
|
+
case command if command.startswith("!"):
|
|
305
|
+
# Remove the '!' from the start to get only the command
|
|
306
|
+
cmd_to_run = command[1:].strip()
|
|
307
|
+
|
|
308
|
+
self.console.print(
|
|
309
|
+
as_table(f"[{cmd_to_run}]", color="cyan"),
|
|
310
|
+
Text("\n"),
|
|
311
|
+
end="",
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
try:
|
|
315
|
+
# Execute the command
|
|
316
|
+
result = subprocess.run(
|
|
317
|
+
cmd_to_run,
|
|
318
|
+
shell=True,
|
|
319
|
+
capture_output=True,
|
|
320
|
+
text=True,
|
|
321
|
+
check=True,
|
|
322
|
+
)
|
|
323
|
+
self.console.print(
|
|
324
|
+
Text(result.stdout),
|
|
325
|
+
Markdown("----"),
|
|
326
|
+
sep="",
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
except subprocess.CalledProcessError as e:
|
|
330
|
+
self.console.print(
|
|
331
|
+
f"[dim]Error executing command: {e.stderr}[/dim]",
|
|
332
|
+
Markdown("----"),
|
|
333
|
+
sep="",
|
|
334
|
+
)
|
|
335
|
+
except Exception as e:
|
|
336
|
+
self.console.print(
|
|
337
|
+
f"[dim]An unexpected error occurred: {e}[/dim]",
|
|
338
|
+
Markdown("----"),
|
|
339
|
+
sep="",
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
case _:
|
|
343
|
+
if msg.startswith("/"):
|
|
344
|
+
self.pretty_print(
|
|
345
|
+
f"Command `{msg}` not recognized. Did you mean one of these: {list(COMMANDS)}"
|
|
346
|
+
)
|
|
347
|
+
continue
|
|
348
|
+
with self.console.status(
|
|
349
|
+
"[dim]processing request[/]",
|
|
350
|
+
spinner="point",
|
|
351
|
+
spinner_style="dim",
|
|
352
|
+
):
|
|
353
|
+
try:
|
|
354
|
+
self.console.print(
|
|
355
|
+
as_table(input_prompt_history, msg),
|
|
356
|
+
Text("\n"),
|
|
357
|
+
end="",
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
# Call the LLM model
|
|
361
|
+
response = self.agent.run(msg)
|
|
362
|
+
|
|
363
|
+
self.console.print(
|
|
364
|
+
as_table(output_prompt_history, response),
|
|
365
|
+
Text("\n"),
|
|
366
|
+
Markdown("----"),
|
|
367
|
+
end="",
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
except KeyboardInterrupt:
|
|
371
|
+
self.console.print(
|
|
372
|
+
"[dim]Command aborted by user[/dim]\n",
|
|
373
|
+
Markdown("----"),
|
|
374
|
+
sep="",
|
|
375
|
+
)
|
|
376
|
+
continue
|
|
377
|
+
except EOFError:
|
|
378
|
+
break
|