Add architecture package and application skeleton
This commit is contained in:
@@ -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"])
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
Reference in New Issue
Block a user