File size: 13,691 Bytes
daaa149 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 | """
ARYA-X Full Integration Test
Tests every module connection described in the spec.
"""
import sys
import traceback
PASS = []
FAIL = []
def check(name, fn):
try:
fn()
PASS.append(name)
print(f" PASS {name}")
except Exception as e:
FAIL.append((name, str(e)))
print(f" FAIL {name}")
print(f" {e}")
# ── 1. Core imports ────────────────────────────────────────────────────────────
check("env/__init__ exports SentinelEnv", lambda: __import__("env").SentinelEnv)
check("env.models all 6 models", lambda: [
getattr(__import__("env.models", fromlist=["Sensor"]), x)
for x in ["Sensor","Target","Observation","Action","Proposal","AgentObservation","ConflictRecord","EpisodeMetrics"]
])
check("env.dynamics all 4 functions", lambda: [
getattr(__import__("env.dynamics", fromlist=["initialize_sensors"]), x)
for x in ["initialize_sensors","spawn_targets","spawn_targets_stochastic","apply_correlated_failures"]
])
check("env.world_model all 3 functions", lambda: [
getattr(__import__("env.world_model", fromlist=["add_observation_noise"]), x)
for x in ["add_observation_noise","apply_mask","get_priority_mapping"]
])
check("interaction package all 4 classes", lambda: [
getattr(__import__("interaction", fromlist=["NegotiationLayer"]), x)
for x in ["NegotiationLayer","ConflictDetector","ConflictResolver","RewardEngine"]
])
check("agents package all 4 agents", lambda: [
getattr(__import__("agents", fromlist=["SatelliteAgent"]), x)
for x in ["SatelliteAgent","DroneAgent","RadarAgent","CommandAgent"]
])
check("curriculum.CurriculumEngine", lambda: __import__("curriculum").CurriculumEngine)
check("trainer.ARYAXTrainer importable", lambda: __import__("trainer").ARYAXTrainer)
check("tasks.grader all 3 functions", lambda: [
getattr(__import__("tasks.grader", fromlist=["grade_episode"]), x)
for x in ["grade_episode","grade_multi_agent_episode","run_deterministic_eval"]
])
# ── 2. env/environment.py — both AryaXEnv classes ─────────────────────────────
check("env.environment.SentinelEnv exists", lambda: __import__("env.environment", fromlist=["SentinelEnv"]).SentinelEnv)
check("env.environment.AryaXEnv (rich) exists", lambda: __import__("env.environment", fromlist=["AryaXEnv"]).AryaXEnv)
check("env.multiagent.AryaXEnv (server) exists", lambda: __import__("env.multiagent", fromlist=["AryaXEnv"]).AryaXEnv)
# ── 3. SentinelEnv full episode ────────────────────────────────────────────────
def _sentinel_episode():
from env.environment import SentinelEnv
env = SentinelEnv(max_steps=3, seed=42)
obs = env.reset()
assert obs.sensors and obs.targets
from env.models import Action
obs, r, done, info = env.step(Action(sensor_id=obs.sensors[0].id, target_id=obs.targets[0].id))
assert "step_count" in info
check("SentinelEnv reset/step/info", _sentinel_episode)
# ── 4. env.environment.AryaXEnv (rich) — partial obs + world model ────────────
def _rich_aryax():
from env.environment import AryaXEnv
env = AryaXEnv(max_steps=3, seed=42, density_factor=2.5, failure_prob=0.1)
obs_map = env.reset()
assert set(obs_map.keys()) == {"satellite","drone","radar","command"}
# Each agent gets different target counts (partial obs)
sat_t = len(obs_map["satellite"].targets)
drone_t = len(obs_map["drone"].targets)
# satellite sees all, drone sees subset — may differ
assert sat_t >= 0 and drone_t >= 0
check("env.environment.AryaXEnv partial obs + world model", _rich_aryax)
# ── 5. env.multiagent.AryaXEnv (server) — interaction/ pipeline ───────────────
def _server_aryax():
from env.multiagent import AryaXEnv, Proposal
env = AryaXEnv(max_steps=3, seed=42)
obs = env.reset()
proposals = [
Proposal(agent_id="satellite", sensor_id=obs["satellite"].sensors[0]["id"],
target_id=obs["satellite"].targets[0]["id"]),
]
obs2, rewards, done, info = env.step_multiagent(proposals)
assert "conflict_rate" in info
assert "step_rewards" in info
assert set(rewards.keys()) == {"satellite","drone","radar","command"}
check("env.multiagent.AryaXEnv step_multiagent + conflict_rate", _server_aryax)
# ── 6. interaction/ pipeline end-to-end ───────────────────────────────────────
def _interaction_pipeline():
from interaction import NegotiationLayer
from interaction.reward import RewardEngine
nl = NegotiationLayer()
re = RewardEngine()
proposals = [
{"agent_id":"satellite","sensor_id":"S1","target_id":"T0_1","capability_score":0.9},
{"agent_id":"drone", "sensor_id":"S2","target_id":"T0_1","capability_score":0.5}, # conflict
]
world_state = {
"sensors": [{"id":"S1","type":"satellite","range":500,"available":True},
{"id":"S2","type":"drone","range":100,"available":True}],
"targets": [{"id":"T0_1","priority":3,"active":True,"type":"strategic"},
{"id":"T0_2","priority":1,"active":True,"type":"airspace"}],
"idle_sensors": [],
"step": 0,
"proposals": proposals,
}
result = nl.negotiate(proposals, world_state, lambda tied: max(tied, key=lambda p: p.get("capability_score",0)))
assert len(result.conflicts_detected) > 0, "Expected conflict"
assert nl.get_conflict_rate() > 0
rewards = re.compute_step_reward(result.final_assignments, result, world_state, ["satellite","drone","radar","command"])
assert isinstance(rewards, dict)
check("interaction/ NegotiationLayer + RewardEngine pipeline", _interaction_pipeline)
# ── 7. agents/ — observe/propose interface ────────────────────────────────────
def _agents_propose():
from agents import SatelliteAgent, DroneAgent, RadarAgent, CommandAgent
from env.models import AgentObservation, Sensor, Target
sensors = [Sensor(id="S1", type="satellite", range=500, available=True)]
targets = [Target(id="T0_1", priority=3, active=True),
Target(id="T0_2", priority=2, active=True)]
obs = AgentObservation(agent_id="satellite", agent_type="satellite",
sensors=sensors, targets=targets, timestep=0)
sat = SatelliteAgent()
sat.observe(obs)
props = sat.propose()
assert isinstance(props, list)
drone_obs = AgentObservation(agent_id="drone", agent_type="drone",
sensors=[Sensor(id="S2",type="drone",range=100,available=True)],
targets=targets, timestep=0)
d = DroneAgent()
d.observe(drone_obs)
assert isinstance(d.propose(), list)
radar_obs = AgentObservation(agent_id="radar", agent_type="radar",
sensors=[Sensor(id="S3",type="radar",range=300,available=True)],
targets=targets, timestep=0)
r = RadarAgent()
r.observe(radar_obs)
assert isinstance(r.propose(), list)
cmd_obs = AgentObservation(agent_id="command", agent_type="command",
sensors=sensors, targets=targets, timestep=0)
c = CommandAgent()
c.observe(cmd_obs)
assert isinstance(c.propose(), list)
check("agents/ all 4 observe()+propose() callable", _agents_propose)
# ── 8. CommandAgent.observe() accepts proposals kwarg ─────────────────────────
def _cmd_proposals_kwarg():
from agents import CommandAgent
from env.models import AgentObservation, Sensor, Target, Proposal
sensors = [Sensor(id="S1",type="satellite",range=500,available=True)]
targets = [Target(id="T0_1",priority=3,active=True)]
obs = AgentObservation(agent_id="command",agent_type="command",
sensors=sensors,targets=targets,timestep=0)
existing = [Proposal(sensor_id="S1",target_id="T0_1",agent_id="satellite",
priority_estimate=3,confidence=0.9)]
c = CommandAgent()
c.observe(obs, proposals=existing)
props = c.propose()
assert isinstance(props, list)
check("CommandAgent.observe(obs, proposals=...) signature", _cmd_proposals_kwarg)
# ── 9. DroneAgent theory-of-mind (loss tracking) ──────────────────────────────
def _drone_tom():
from agents import DroneAgent
from env.models import AgentObservation, Sensor, Target, ConflictRecord
sensors = [Sensor(id="S2",type="drone",range=100,available=True)]
targets = [Target(id="T0_2",priority=2,active=True)]
obs = AgentObservation(agent_id="drone",agent_type="drone",
sensors=sensors,targets=targets,timestep=0)
d = DroneAgent()
d.observe(obs)
# Simulate 3 losses to radar on redundant_coverage
d.losses["redundant_coverage"] = 3
props = d.propose()
# Boundary target (even index) should have reduced confidence
boundary_props = [p for p in props if d._is_boundary_target(p.target_id)]
if boundary_props:
assert boundary_props[0].confidence <= 0.5
check("DroneAgent theory-of-mind confidence reduction", _drone_tom)
# ── 10. CurriculumEngine → AryaXEnv config connection ─────────────────────────
def _curriculum_to_env():
from curriculum import CurriculumEngine
from env.multiagent import AryaXEnv
ce = CurriculumEngine()
cfg = ce.get_scenario_config()
assert "max_steps" in cfg
assert "sensor_failure_prob" in cfg
env = AryaXEnv(
max_steps=cfg["max_steps"],
seed=42,
density_factor=1.5 + ce.difficulty_level * 2.5,
failure_prob=cfg["sensor_failure_prob"],
)
obs = env.reset()
assert obs is not None
check("CurriculumEngine config -> AryaXEnv constructor", _curriculum_to_env)
# ── 11. grader.grade_multi_agent_episode ──────────────────────────────────────
def _grader_multi():
from tasks.grader import grade_multi_agent_episode
from interaction import NegotiationLayer
nl = NegotiationLayer()
result = grade_multi_agent_episode(
per_agent_rewards={"SAT":10.0,"UAV":5.0,"RDR":3.0,"CMD":2.0},
step_count=20,
num_sensors=4,
negotiation_layer=nl,
num_agents=4,
)
assert "overall_score" in result
assert "coordination_score" in result
assert "conflict_rate" in result
check("grader.grade_multi_agent_episode", _grader_multi)
# ── 12. server.py imports cleanly ─────────────────────────────────────────────
check("server.py imports without error", lambda: __import__("server"))
# ── 13. inference.py imports + run_multi_task callable ────────────────────────
def _inference_multi():
import inference
assert callable(inference.run_multi_task)
assert callable(inference._greedy_multi_proposals)
check("inference.py run_multi_task callable", _inference_multi)
# ── 14. trainer.py ARYAXTrainer instantiates (no model) ──────────────────────
def _trainer_init():
from trainer import ARYAXTrainer
# Should not raise even without GPU/model
t = ARYAXTrainer.__new__(ARYAXTrainer)
t.curriculum = __import__("curriculum").CurriculumEngine()
from interaction import NegotiationLayer
from interaction.reward import RewardEngine
t.negotiation = NegotiationLayer()
t.reward_engine = RewardEngine()
t._grpo_trainer = None
assert t._grpo_trainer is None
check("ARYAXTrainer attributes accessible", _trainer_init)
# ── 15. tasks/ easy/medium/hard envs return SentinelEnv ───────────────────────
def _task_envs():
from tasks.easy_task import get_easy_env
from tasks.medium_task import get_medium_env
from tasks.hard_task import get_hard_env
from env.environment import SentinelEnv
for fn in [get_easy_env, get_medium_env, get_hard_env]:
e = fn()
assert isinstance(e, SentinelEnv)
obs = e.reset()
assert obs.sensors
check("tasks/ easy/medium/hard envs", _task_envs)
# ── 16. env.multiagent NegotiationLayer is interaction/ (not duplicate) ────────
def _no_duplicate_nl():
from env.multiagent import NegotiationLayer as MaNL
from interaction import NegotiationLayer as INL
assert MaNL is INL, "env.multiagent.NegotiationLayer must BE interaction.NegotiationLayer"
check("env.multiagent.NegotiationLayer IS interaction.NegotiationLayer (no duplicate)", _no_duplicate_nl)
# ── Summary ────────────────────────────────────────────────────────────────────
print(f"\n{'='*55}")
print(f" PASSED: {len(PASS)}/{len(PASS)+len(FAIL)}")
if FAIL:
print(f" FAILED: {len(FAIL)}")
for name, err in FAIL:
print(f" - {name}")
print(f" {err}")
print(f"{'='*55}")
sys.exit(0 if not FAIL else 1)
|