marla.agents
The SPADE agents themselves, plus the shared lifecycle/advisory plumbing they’re built from. See Architecture for how these fit together.
The RL Orchestrator SPADE agent (spec section 3.1).
Owns NASimEmu, the recurrent PPO policy, and lifecycle coordination – there
is no Coordinator component. The baseline variant has no
required_participants at all, so lifecycle collapses to “send nothing,
wait for nothing, train immediately.” The assisted variant additionally
consults the Gatekeeper mid-training via a real await on the same event
loop (see learning/trainer.py and agents/advisory_client.py).
- class marla.agents.orchestrator.OrchestratorLifecycleBehaviour(*args, **kwargs)[source]
Bases:
OneShotBehaviourRuns startup -> training -> shutdown exactly once (spec section 5).
- Parameters:
args (Any)
kwargs (Any)
- Return type:
Any
- READY_CHECK_RETRY_SECONDS = 5.0
- STOP_EXPERIMENT_GRACE_SECONDS = 1.0
- class marla.agents.orchestrator.RLOrchestratorAgent(*args, **kwargs)[source]
Bases:
AgentThe only agent that owns NASimEmu and writes central experiment metrics.
- Parameters:
jid (str)
password (str)
alias (str)
run_id (str)
config (Config)
policy (RecurrentPolicy)
optimizer (torch.optim.Optimizer)
adapter (NasimEmuAdapter)
device (torch.device)
seed (int)
num_rollouts (int)
gatekeeper_alias (str | None)
gatekeeper_jid (str | None)
stop_event (asyncio.Event | None)
The Gatekeeper: authoritative schema validator for advisory traffic (spec section 3.2).
All Plan Maker advisory requests/responses route through here:
RL Orchestrator -> Gatekeeper -> Plan Maker and back. The Gatekeeper
validates communication and advisory payloads, never final NASimEmu
actions. It holds only the transient pending-request registry described in
spec section 6 – no Blackboard, no cross-run memory.
- class marla.agents.gatekeeper.AdvisoryRequestBehaviour[source]
Bases:
CyclicBehaviourRL Orchestrator -> Gatekeeper: validate the request, then forward it verbatim to the Plan Maker.
- agent
- is_running
- on_end
- on_start
- presence
- queue
- template
- web
- class marla.agents.gatekeeper.AdvisoryResponseBehaviour[source]
Bases:
CyclicBehaviourPlan Maker -> Gatekeeper: validate, request correction, or forward/reject to the RL Orchestrator.
- agent
- is_running
- on_end
- on_start
- presence
- queue
- template
- web
- class marla.agents.gatekeeper.GatekeeperAgent(jid, password, *, alias, run_id, orchestrator_alias, orchestrator_jid, plan_maker_alias, plan_maker_jid, max_schema_revisions, enable_disconnect_detection=False)[source]
Bases:
AgentValidates advisory communication; never validates final NASimEmu actions.
- Parameters:
- container
- jid
- loop
- password
- async setup()[source]
Setup agent before startup. This coroutine may be overloaded.
- Return type:
None
- traces
- verify_security
- web
- xmpp_port
- class marla.agents.gatekeeper.PendingRequest(run_id, request_id, source_observation_id, episode_id, environment_step, expected_agent_alias, legal_action_ids, schema_revision_count=0)[source]
Bases:
objectTransient correlation state for one in-flight advisory request (spec section 6).
- Parameters:
The Plan Maker SPADE agent (spec sections 3.3, 8, 9).
Frozen and advisory: no PPO gradients, no cross-run memory, no hidden simulator access. It only ever sees what the Gatekeeper forwards – the current visible observation, current legal action descriptions, the configured objective, and the static versioned knowledge base.
- class marla.agents.plan_maker.AdvisoryRequestHandler[source]
Bases:
CyclicBehaviourGatekeeper -> Plan Maker: retrieve rules, build a prompt, and respond.
- agent
- is_running
- on_end
- on_start
- presence
- queue
- template
- web
- class marla.agents.plan_maker.CorrectionRequestHandler[source]
Bases:
CyclicBehaviourGatekeeper -> Plan Maker: retry with a corrective instruction, same request ID.
- agent
- is_running
- on_end
- on_start
- presence
- queue
- template
- web
- class marla.agents.plan_maker.PlanMakerAgent(jid, password, *, alias, run_id, gatekeeper_alias, gatekeeper_jid, backend, model_version, knowledge_base, resolved_device, enable_disconnect_detection=False, debug_dir=None)[source]
Bases:
Agent- Parameters:
- container
- async generate_and_respond(behaviour, run_id, request_id, prompt, retrieved_rule_ids, legal_action_ids)[source]
- jid
- loop
- password
- async setup()[source]
Setup agent before startup. This coroutine may be overloaded.
- Return type:
None
- traces
- verify_security
- web
- xmpp_port
RL-Orchestrator-side plumbing for a single Gatekeeper consultation round-trip.
The RL Orchestrator sends an ADVISORY_REQUEST and waits synchronously
(no elapsed timeout, spec section 10/14) for the correlated
ADVISORY_RESPONSE – either an accepted payload or a
schema_rejected artifact after the Gatekeeper exhausts
max_schema_revisions. Milestone 7 wires the learned query gate around
this; this module only implements the request/await mechanics, reusable by
tests today and by the query gate later.
- class marla.agents.advisory_client.AdvisoryOutcome(status: 'AdvisoryStatus', payload: 'AdvisoryResponsePayload | None', latency_seconds: 'float', request_id: 'str')[source]
Bases:
object- Parameters:
status (Literal['accepted', 'schema_rejected'])
payload (AdvisoryResponsePayload | None)
latency_seconds (float)
request_id (str)
- payload: AdvisoryResponsePayload | None
- class marla.agents.advisory_client.AdvisoryResponseListenerBehaviour(*args, **kwargs)[source]
Bases:
CyclicBehaviourResolves the pending future matching an incoming ADVISORY_RESPONSE’s request_id.
The RL Orchestrator ignores direct Plan Maker advisory messages (spec section 10) – this behaviour is only ever registered with a template matching messages from the Gatekeeper.
- async marla.agents.advisory_client.send_advisory_request(behaviour, *, gatekeeper_jid, gatekeeper_alias, run_id, sender_alias, episode_id, step, source_observation_id, objective, observation, legal_actions, pending)[source]
Send one advisory request and await its correlated response, indefinitely.
- Parameters:
behaviour (spade.behaviour.CyclicBehaviour)
gatekeeper_jid (str)
gatekeeper_alias (str)
run_id (str)
sender_alias (str)
episode_id (int)
step (int)
source_observation_id (str)
objective (AdvisoryObjective)
observation (dict)
legal_actions (list[AdvisoryActionDescriptor])
pending (dict[str, asyncio.Future])
- Return type:
SPADE behaviour wiring for the lifecycle events in marla.runtime.lifecycle.
Reusable by any agent role: the RL Orchestrator uses
ReadyListenerBehaviour/FailureListenerBehaviour to collect
events, and non-orchestrator participants (Gatekeeper, Plan Maker –
Milestone 6) use ReadyCheckResponderBehaviour/
StopExperimentBehaviour to answer the handshake and shut down
cleanly.
- class marla.agents.lifecycle_behaviours.FailureListenerBehaviour(*args, **kwargs)[source]
Bases:
CyclicBehaviourRL-Orchestrator side: turns incoming EXPERIMENT_FAILED messages into FailureSignal events.
- Parameters:
events (asyncio.Queue)
run_id (str)
- class marla.agents.lifecycle_behaviours.ReadyCheckResponderBehaviour(*args, **kwargs)[source]
Bases:
CyclicBehaviourParticipant side: replies READY to READY_CHECK once local setup has succeeded.
Local model loading/device resolution must happen before this behaviour is added (spec section 18: an agent must not send READY after failed initialization) – if setup failed, the agent process should raise instead of ever starting this behaviour.
- class marla.agents.lifecycle_behaviours.ReadyListenerBehaviour(*args, **kwargs)[source]
Bases:
CyclicBehaviourRL-Orchestrator side: turns incoming READY messages into ReadySignal events.
- Parameters:
events (asyncio.Queue)
run_id (str)
- class marla.agents.lifecycle_behaviours.StopExperimentBehaviour(*args, **kwargs)[source]
Bases:
CyclicBehaviourAny agent: stops itself upon receiving STOP_EXPERIMENT for this run.
- Parameters:
run_id (str)
- marla.agents.lifecycle_behaviours.disable_reconnect_on_missed_ping(agent)[source]
Disable XEP-0199’s ping-triggered reconnect; see the module-level comment.
SPADE’s
XMPPClient.__init__already callsenable_keepalive()once beforesetup()ever runs, scheduling a recurring ping.disable_keepalive()cancels that scheduled event.- Return type:
None
- marla.agents.lifecycle_behaviours.make_disconnect_detector(agent, watched_jid)[source]
Build
(on_available, on_unavailable, expect_disconnect)presence callbacks that stopagentonly on a genuine, unexpected disconnect ofwatched_jid.Used by support agents (Gatekeeper, Plan Maker) to satisfy “explicit disconnect… must fail the experiment” (spec section 5) in distributed mode, where losing the process on the other end of a JID is a real, detectable event rather than a same-process assumption. Local mode’s embedded XMPP server is not used here on purpose – this only matters once processes are genuinely separate.
An “unavailable” presence stanza for
watched_jidcan arrive before the two agents’ startup ordering has settled – e.g. a presence probe answered for a peer that has simply not come online yet – and looks identical on the wire to a peer that really did drop. Only a peer previously observed available and then reported unavailable counts as a disconnect; both callbacks must be wired (on_availableas well ason_unavailable) for this distinction to work.A peer’s disconnect is also unremarkable, not a failure, once the run has reached its own STOP_EXPERIMENT – calling
expect_disconnect()(fromStopExperimentBehaviour, right before this agent stops itself) suppresses the callback for the rest of this agent’s lifetime, so a normal end-of-run teardown race doesn’t get reported as a crash.- Parameters:
watched_jid (str)
- marla.agents.lifecycle_behaviours.message_type_template(message_type)[source]
- Parameters:
message_type (MessageType)
- Return type:
spade.template.Template