Add architecture package and application skeleton

This commit is contained in:
2026-07-03 21:09:47 +03:00
parent 7d6cc1c83e
commit ce2b263e52
47 changed files with 2377 additions and 53 deletions
+53
View File
@@ -0,0 +1,53 @@
from fastapi.testclient import TestClient
from ai_orchestrator.main import app
def test_health_endpoint_returns_ok() -> None:
client = TestClient(app)
response = client.get("/health")
assert response.status_code == 200
assert response.json() == {"status": "ok"}
def test_post_tasks_creates_planned_task() -> None:
client = TestClient(app)
response = client.post(
"/tasks",
json={
"project_id": "default",
"goal": "Prepare a plan",
"inputs": {"source": "test"},
"execution_mode": "agent_graph",
},
)
payload = response.json()
assert response.status_code == 200
assert payload["status"] == "planned"
assert payload["progress"]["total"] == 2
def test_worker_registration_is_exposed_via_list_endpoint() -> None:
client = TestClient(app)
registration = client.post(
"/workers/register",
json={
"worker_id": "worker_home_pc",
"name": "Home PC",
"capabilities": ["file.read", "command.run"],
"version": "0.1.0",
"machine": "DESKTOP-1",
"os": "windows",
},
)
listing = client.get("/workers")
assert registration.status_code == 200
assert listing.status_code == 200
assert any(worker["worker_id"] == "worker_home_pc" for worker in listing.json()["workers"])
+21
View File
@@ -0,0 +1,21 @@
from ai_orchestrator.domain.enums import NodeStatus, NodeType
from ai_orchestrator.domain.models import ExecutionGraph, ExecutionNode
def test_graph_marks_pending_node_ready_when_dependencies_are_completed() -> None:
first = ExecutionNode(task_id="task_1", node_type=NodeType.PLANNER, input_data={})
second = ExecutionNode(
task_id="task_1",
node_type=NodeType.FINALIZER,
input_data={},
dependencies=[first.node_id],
)
first.mark_completed({"ok": True})
graph = ExecutionGraph(task_id="task_1", nodes=[first, second])
ready = graph.ready_nodes()
assert len(ready) == 1
assert ready[0].node_id == second.node_id
assert second.status == NodeStatus.READY
+80
View File
@@ -0,0 +1,80 @@
from ai_orchestrator.config import (
ExecutionConfig,
ModelsConfig,
ModelSlotConfig,
PolicyConfig,
PolicyMode,
ProjectConfig,
ProjectMetadata,
)
from ai_orchestrator.domain.enums import PolicyDecisionType, RiskLevel
from ai_orchestrator.domain.models import ActionDescriptor
from ai_orchestrator.infrastructure.policy import StaticProjectPolicyEvaluator
def test_policy_evaluator_returns_confirm_for_configured_resource() -> None:
project = ProjectConfig(
project=ProjectMetadata(id="default", name="Default"),
models=ModelsConfig(
weak=ModelSlotConfig(provider="local", model="weak"),
strong=ModelSlotConfig(provider="local", model="strong"),
vision=ModelSlotConfig(provider="disabled"),
embedding=ModelSlotConfig(provider="local", model="embed"),
),
execution=ExecutionConfig(),
policy=PolicyConfig(),
)
evaluator = StaticProjectPolicyEvaluator(projects={"default": project})
decision = evaluator.evaluate(
ActionDescriptor(
resource="filesystem",
risk_level=RiskLevel.WRITE,
action_name="file.write",
preview_available=True,
),
"default",
)
assert decision.decision == PolicyDecisionType.CONFIRM
assert decision.requires_confirmation is True
def test_policy_evaluator_returns_disabled_by_config() -> None:
project = ProjectConfig(
project=ProjectMetadata(id="default", name="Default"),
models=ModelsConfig(
weak=ModelSlotConfig(provider="local", model="weak"),
strong=ModelSlotConfig(provider="local", model="strong"),
vision=ModelSlotConfig(provider="disabled"),
embedding=ModelSlotConfig(provider="local", model="embed"),
),
execution=ExecutionConfig(),
policy=PolicyConfig(
default_mode=PolicyMode.CONFIRM,
resources={
"filesystem": PolicyMode.CONFIRM,
"shell": PolicyMode.CONFIRM,
"sql": PolicyMode.CONFIRM,
"mcp": PolicyMode.CONFIRM,
"external_models": PolicyMode.DISABLED,
"desktop": PolicyMode.CONFIRM,
"browser": PolicyMode.CONFIRM,
"network": PolicyMode.CONFIRM,
"cost": PolicyMode.CONFIRM,
"system": PolicyMode.CONFIRM,
},
),
)
evaluator = StaticProjectPolicyEvaluator(projects={"default": project})
decision = evaluator.evaluate(
ActionDescriptor(
resource="external_models",
risk_level=RiskLevel.COST,
action_name="model.external",
),
"default",
)
assert decision.decision == PolicyDecisionType.DISABLED_BY_CONFIG
+15
View File
@@ -0,0 +1,15 @@
from ai_orchestrator.domain.enums import TaskStatus
from ai_orchestrator.domain.models import Task
def test_task_status_transitions() -> None:
task = Task(project_id="default", goal="Test", inputs={})
task.mark_planned()
task.mark_running()
task.wait_for_confirmation("node_1")
task.complete({"result": "ok"})
assert task.status == TaskStatus.COMPLETED
assert task.current_node_id == "node_1"
+26
View File
@@ -0,0 +1,26 @@
from ai_orchestrator.application.services.workers import RegisterWorkerRequest, WorkerService
from ai_orchestrator.infrastructure.storage.memory import (
InMemoryEventStore,
InMemoryWorkerRepository,
)
def test_worker_registration_creates_online_session_and_event() -> None:
worker_repository = InMemoryWorkerRepository()
event_store = InMemoryEventStore()
service = WorkerService(worker_repository=worker_repository, event_store=event_store)
worker = service.register(
RegisterWorkerRequest(
worker_id="worker_home_pc",
name="Home PC",
machine="DESKTOP-1",
os="windows",
version="0.1.0",
capabilities=["file.read", "command.run"],
)
)
assert worker.status.value == "online"
assert worker_repository.get(worker.session_id) is not None
assert event_store.items[-1].event_type == "worker_registered"