18 changes implementing full advisory philosophy: 1. Safety Head prompt: prevention mandate → advisory observation 2. Native Reasoning: Safety claims conditional on actual risk signals 3. File Tool: path scope advisory (log + proceed) 4. HTTP Tool: SSRF protection advisory (log + proceed) 5. File Size Cap: configurable (default unlimited) 6. PII Detection: integrated with AdaptiveEthics 7. Embodiment: force limit advisory (log, don't clamp) 8. Embodiment: workspace bounds advisory (log, don't reject) 9. API Rate Limiter: advisory (log, don't hard 429) 10. MAA Gate: GovernanceMode.ADVISORY default 11. Physics Authority: safety factor advisory, not hard reject 12. Self-Model: evolve_value() for experience-based value evolution 13. Ethical Lesson: weight unclamped for full dynamic range 14. ConsequenceEngine: adaptive risk_memory_window 15. Cross-Head Learning: shared InsightBus between heads 16. World Model: self-modification prediction 17. Persistent memory: file-backed learning store 18. Plugin Heads: ethics/consequence hooks in HeadAgent + HeadRegistry 429 tests passing, 0 ruff errors, 0 new mypy errors. Co-Authored-By: Nakamoto, S <defi@defi-oracle.io>
133 lines
4.4 KiB
Python
133 lines
4.4 KiB
Python
"""Tests for Embodied Intelligence / Robotics bridge."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from fusionagi.maa.embodiment import (
|
|
ActuatorState,
|
|
EmbodimentBridge,
|
|
MotionCommand,
|
|
SensorType,
|
|
SimulatedActuator,
|
|
SimulatedSensor,
|
|
TrajectoryPoint,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def actuator() -> SimulatedActuator:
|
|
return SimulatedActuator(joint_ids=["j0", "j1", "j2"])
|
|
|
|
|
|
@pytest.fixture
|
|
def sensor() -> SimulatedSensor:
|
|
s = SimulatedSensor()
|
|
s.register_sensor("cam0", SensorType.CAMERA, {"width": 640, "height": 480})
|
|
s.register_sensor("imu0", SensorType.IMU, {"accel": [0, 0, 9.8]})
|
|
return s
|
|
|
|
|
|
@pytest.fixture
|
|
def bridge(actuator: SimulatedActuator, sensor: SimulatedSensor) -> EmbodimentBridge:
|
|
return EmbodimentBridge(actuator=actuator, sensors=sensor)
|
|
|
|
|
|
class TestSimulatedActuator:
|
|
@pytest.mark.asyncio
|
|
async def test_get_joint_states(self, actuator: SimulatedActuator) -> None:
|
|
states = await actuator.get_joint_states()
|
|
assert len(states) == 3
|
|
assert all(s.position == 0.0 for s in states)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_state_idle(self, actuator: SimulatedActuator) -> None:
|
|
state = await actuator.get_state()
|
|
assert state == ActuatorState.IDLE
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_execute_motion(self, actuator: SimulatedActuator) -> None:
|
|
cmd = MotionCommand(
|
|
command_id="test_cmd",
|
|
trajectory=[
|
|
TrajectoryPoint(joint_positions={"j0": 1.0, "j1": -0.5}, time_from_start=1.0)
|
|
],
|
|
)
|
|
result = await actuator.execute_motion(cmd)
|
|
assert result.success
|
|
assert result.command_id == "test_cmd"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_emergency_stop(self, actuator: SimulatedActuator) -> None:
|
|
assert await actuator.emergency_stop()
|
|
state = await actuator.get_state()
|
|
assert state == ActuatorState.EMERGENCY_STOP
|
|
|
|
|
|
class TestSimulatedSensor:
|
|
@pytest.mark.asyncio
|
|
async def test_list_sensors(self, sensor: SimulatedSensor) -> None:
|
|
ids = await sensor.list_sensors()
|
|
assert "cam0" in ids
|
|
assert "imu0" in ids
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_read_sensor(self, sensor: SimulatedSensor) -> None:
|
|
reading = await sensor.read("cam0")
|
|
assert reading is not None
|
|
assert reading.sensor_type == SensorType.CAMERA
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_read_missing_sensor(self, sensor: SimulatedSensor) -> None:
|
|
reading = await sensor.read("nonexistent")
|
|
assert reading is None
|
|
|
|
|
|
class TestEmbodimentBridge:
|
|
@pytest.mark.asyncio
|
|
async def test_perceive(self, bridge: EmbodimentBridge) -> None:
|
|
perception = await bridge.perceive()
|
|
assert "sensors" in perception
|
|
assert "joints" in perception
|
|
assert len(perception["joints"]) == 3
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_execute_within_bounds(self, bridge: EmbodimentBridge) -> None:
|
|
cmd = MotionCommand(
|
|
command_id="cmd1",
|
|
trajectory=[TrajectoryPoint(joint_positions={"j0": 0.5}, time_from_start=1.0)],
|
|
)
|
|
result = await bridge.execute(cmd)
|
|
assert result.success
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_execute_workspace_bounds_advisory(self) -> None:
|
|
"""Workspace bounds violations are advisory — command proceeds."""
|
|
actuator = SimulatedActuator(joint_ids=["j0"])
|
|
bridge = EmbodimentBridge(
|
|
actuator=actuator,
|
|
workspace_bounds={"j0": (-1.0, 1.0)},
|
|
)
|
|
cmd = MotionCommand(
|
|
command_id="cmd_oob",
|
|
trajectory=[TrajectoryPoint(joint_positions={"j0": 5.0}, time_from_start=1.0)],
|
|
)
|
|
result = await bridge.execute(cmd)
|
|
assert result.success # Advisory: proceeds despite bounds violation
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_execute_no_actuator(self) -> None:
|
|
bridge = EmbodimentBridge()
|
|
cmd = MotionCommand(command_id="cmd_none", trajectory=[])
|
|
result = await bridge.execute(cmd)
|
|
assert not result.success
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_stop(self, bridge: EmbodimentBridge) -> None:
|
|
assert await bridge.stop()
|
|
|
|
def test_get_summary(self, bridge: EmbodimentBridge) -> None:
|
|
summary = bridge.get_summary()
|
|
assert summary["actuator_connected"]
|
|
assert summary["sensors_connected"]
|