Import ducklm runtime
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
from app.api.server import chat, health, resolve_permission, resolve_secret
|
||||
from app.core.permission_resolution import PermissionResolutionRequest, SecretResolutionRequest
|
||||
from app.core.contracts import UserTask
|
||||
|
||||
|
||||
def test_health_handler() -> None:
|
||||
assert health() == {"status": "ok"}
|
||||
|
||||
|
||||
def test_chat_handler_returns_runtime_events() -> None:
|
||||
body = chat(UserTask(input="hello from handler test"))
|
||||
assert body["status"] == "completed"
|
||||
assert body["events"][0]["type"] == "task_received"
|
||||
|
||||
|
||||
def test_resolve_permission_handler_allows_completion() -> None:
|
||||
initial = chat(UserTask(input="запусти pwd"))
|
||||
if initial["status"] == "awaiting_permission":
|
||||
body = resolve_permission(
|
||||
PermissionResolutionRequest(task_id=initial["task_id"], decision="allow_once")
|
||||
)
|
||||
assert body["status"] in {"completed", "failed"}
|
||||
|
||||
|
||||
def test_resolve_secret_handler_requires_pending_request() -> None:
|
||||
body = resolve_secret(SecretResolutionRequest(task_id="missing", secret="x"))
|
||||
assert body["status"] == "failed"
|
||||
@@ -0,0 +1,37 @@
|
||||
from app.core.contracts import CriticScore, ExecutionDirective, PlanStep, UserTask
|
||||
|
||||
|
||||
def test_user_task_defaults() -> None:
|
||||
task = UserTask(input="hello")
|
||||
assert task.task_id
|
||||
assert task.session_id
|
||||
|
||||
|
||||
def test_plan_step_supports_dependencies() -> None:
|
||||
step = PlanStep(
|
||||
id="step-1",
|
||||
kind="tool",
|
||||
tool="shell_exec",
|
||||
description="run command",
|
||||
depends_on=[],
|
||||
)
|
||||
assert step.tool == "shell_exec"
|
||||
|
||||
|
||||
def test_critic_score_bounds() -> None:
|
||||
score = CriticScore(
|
||||
correctness=1.0,
|
||||
usefulness=0.5,
|
||||
safety=0.0,
|
||||
memory_store=False,
|
||||
weight=0.2,
|
||||
explanation="ok",
|
||||
)
|
||||
assert score.weight == 0.2
|
||||
|
||||
|
||||
def test_execution_directive_defaults() -> None:
|
||||
directive = ExecutionDirective(type="noop")
|
||||
assert directive.payload == {}
|
||||
assert directive.confidence == 0.0
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
from app.core.contracts import UserTask
|
||||
from app.runtime.runtime_controller import RuntimeController
|
||||
|
||||
|
||||
def test_runtime_loop_emits_basic_events() -> None:
|
||||
controller = RuntimeController()
|
||||
result = controller.handle_task(UserTask(input="hello runtime"))
|
||||
event_types = [event["type"] for event in result["events"]]
|
||||
assert result["status"] == "completed"
|
||||
assert "message" in result["result"]
|
||||
assert "task_received" in event_types
|
||||
assert "context_built" in event_types
|
||||
assert "task_completed" in event_types
|
||||
|
||||
|
||||
def test_runtime_loop_routes_natural_language_shell_request_to_permission_flow() -> None:
|
||||
controller = RuntimeController()
|
||||
result = controller.handle_task(UserTask(input="запусти sudo apt update"))
|
||||
event_types = [event["type"] for event in result["events"]]
|
||||
assert result["status"] == "awaiting_permission"
|
||||
assert result["directive"]["type"] == "tool"
|
||||
assert result["directive"]["payload"]["tool"] == "shell_exec"
|
||||
assert "permission_requested" in event_types
|
||||
assert "task_awaiting_permission" in event_types
|
||||
assert result["result"]["error"] == "Permission required before execution."
|
||||
@@ -0,0 +1,147 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from app.core.contracts import UserTask
|
||||
from app.runtime.runtime_controller import RuntimeController
|
||||
|
||||
|
||||
def _write_config_tree(base_dir: Path) -> None:
|
||||
(base_dir / "config").mkdir()
|
||||
(base_dir / "data" / "events").mkdir(parents=True, exist_ok=True)
|
||||
(base_dir / "data" / "state").mkdir(parents=True, exist_ok=True)
|
||||
(base_dir / "data" / "permissions").mkdir(parents=True, exist_ok=True)
|
||||
(base_dir / "models").mkdir(exist_ok=True)
|
||||
|
||||
configs = {
|
||||
"models.json": {
|
||||
"orchestrator_path": "models/llama.gguf",
|
||||
"coder_path": "models/xcoder.gguf",
|
||||
"critic_path": "models/gemma.gguf",
|
||||
"embeddings_path": "models/all-MiniLM-L6-v2",
|
||||
"inference": {},
|
||||
},
|
||||
"prompts.json": {
|
||||
"orchestration_prompt": "",
|
||||
"planning_prompt": "",
|
||||
"coder_prompt": "",
|
||||
"critic_prompt": "",
|
||||
},
|
||||
"permissions.json": {
|
||||
"dangerous_commands": {"rm": "ask_always", "sudo": "ask_always"},
|
||||
"sensitive_paths": ["/etc", "/usr", "/var"],
|
||||
"default_approval_behavior": "ask_always",
|
||||
},
|
||||
"runtime.json": {
|
||||
"step_timeout_ms": 5000,
|
||||
"task_timeout_ms": 30000,
|
||||
"planner_retry_limit": 1,
|
||||
"tool_retry_limit": 0,
|
||||
"replan_limit": 0,
|
||||
"max_execution_steps": 5,
|
||||
"retrieval_top_k": 3,
|
||||
"memory_thresholds": {},
|
||||
"critic_fallback_policy": "continue_without_critic",
|
||||
"checkpoint_policy": {"save_on_transition": True},
|
||||
"event_retention_policy": {"keep_all": True},
|
||||
"streaming_settings": {"enabled": True},
|
||||
},
|
||||
}
|
||||
for name, payload in configs.items():
|
||||
(base_dir / "config" / name).write_text(json.dumps(payload), encoding="utf-8")
|
||||
|
||||
|
||||
def test_file_write_and_read_tool_flow(tmp_path: Path) -> None:
|
||||
_write_config_tree(tmp_path)
|
||||
controller = RuntimeController(base_dir=tmp_path)
|
||||
target = tmp_path / "notes" / "test.txt"
|
||||
|
||||
write_result = controller.handle_task(
|
||||
UserTask(
|
||||
input="write a file",
|
||||
context={
|
||||
"requested_tool": "file_write",
|
||||
"tool_args": {"path": str(target), "content": "hello from ducklm"},
|
||||
},
|
||||
)
|
||||
)
|
||||
assert write_result["status"] == "completed"
|
||||
assert target.read_text(encoding="utf-8") == "hello from ducklm"
|
||||
|
||||
read_result = controller.handle_task(
|
||||
UserTask(
|
||||
input="read the file",
|
||||
context={
|
||||
"requested_tool": "file_read",
|
||||
"tool_args": {"path": str(target)},
|
||||
},
|
||||
)
|
||||
)
|
||||
assert read_result["status"] == "completed"
|
||||
assert read_result["result"]["output"] == "hello from ducklm"
|
||||
|
||||
|
||||
def test_shell_exec_requires_permission_for_dangerous_command(tmp_path: Path) -> None:
|
||||
_write_config_tree(tmp_path)
|
||||
controller = RuntimeController(base_dir=tmp_path)
|
||||
result = controller.handle_task(
|
||||
UserTask(
|
||||
input="run dangerous shell command",
|
||||
context={
|
||||
"requested_tool": "shell_exec",
|
||||
"tool_args": {"command": "rm -rf /tmp/nonexistent"},
|
||||
},
|
||||
)
|
||||
)
|
||||
assert result["status"] == "awaiting_permission"
|
||||
assert "permission_request" in result["result"]
|
||||
|
||||
|
||||
def test_shell_exec_allows_safe_command(tmp_path: Path) -> None:
|
||||
_write_config_tree(tmp_path)
|
||||
controller = RuntimeController(base_dir=tmp_path)
|
||||
result = controller.handle_task(
|
||||
UserTask(
|
||||
input="run safe shell command",
|
||||
context={
|
||||
"requested_tool": "shell_exec",
|
||||
"tool_args": {"command": "pwd"},
|
||||
},
|
||||
)
|
||||
)
|
||||
assert result["status"] == "completed"
|
||||
assert str(tmp_path) in result["result"]["output"]
|
||||
|
||||
|
||||
def test_permission_resolution_can_resume_task(tmp_path: Path) -> None:
|
||||
_write_config_tree(tmp_path)
|
||||
controller = RuntimeController(base_dir=tmp_path)
|
||||
initial = controller.handle_task(
|
||||
UserTask(
|
||||
input="запусти sudo apt update",
|
||||
)
|
||||
)
|
||||
assert initial["status"] == "awaiting_permission"
|
||||
resumed = controller.resolve_permission(task_id=initial["task_id"], decision="deny")
|
||||
assert resumed["status"] == "failed"
|
||||
assert resumed["result"]["error"] == "Permission denied by user."
|
||||
|
||||
|
||||
def test_sudo_permission_resolution_requests_secret_input(tmp_path: Path) -> None:
|
||||
_write_config_tree(tmp_path)
|
||||
controller = RuntimeController(base_dir=tmp_path)
|
||||
initial = controller.handle_task(UserTask(input="запусти sudo apt update"))
|
||||
assert initial["status"] == "awaiting_permission"
|
||||
resumed = controller.resolve_permission(task_id=initial["task_id"], decision="allow_once")
|
||||
assert resumed["status"] == "awaiting_input"
|
||||
assert resumed["result"]["secret_request"]["kind"] == "sudo_password"
|
||||
|
||||
|
||||
def test_secret_resolution_continues_after_pending_secret_saved(tmp_path: Path) -> None:
|
||||
_write_config_tree(tmp_path)
|
||||
controller = RuntimeController(base_dir=tmp_path)
|
||||
initial = controller.handle_task(UserTask(input="запусти sudo apt update"))
|
||||
resumed = controller.resolve_permission(task_id=initial["task_id"], decision="allow_once")
|
||||
assert resumed["status"] == "awaiting_input"
|
||||
final = controller.resolve_secret(task_id=initial["task_id"], secret="wrongpass")
|
||||
assert final["status"] in {"completed", "failed"}
|
||||
assert "error" in final["result"] or "output" in final["result"]
|
||||
Reference in New Issue
Block a user