marla.messaging

The SPADE message envelope, advisory payload schemas, and parsing/validation.

Message envelope schema shared by every SPADE message MARLA sends.

Per spec section 10, every message carries this metadata regardless of payload: performative, message_type, schema_version, run_id, conversation_id, request_id, sender_alias, receiver_alias. Advisory request/response payload schemas (section 9) are added in Milestone 6/7; this module covers the envelope plus the five lifecycle message types that may travel directly between the RL Orchestrator and any participant (section 4/5): READY_CHECK, READY, START_EXPERIMENT, STOP_EXPERIMENT, EXPERIMENT_FAILED.

class marla.messaging.schemas.AdvisoryActionDescriptor(*, action_id, type, target=None, parameters=<factory>)[source]

Bases: BaseModel

The Plan Maker’s view of a legal action: public fields only.

Parameters:
action_id: str
model_config: ClassVar[ConfigDict] = {'extra': 'forbid'}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

parameters: dict[str, Any]
target: str | None
type: str
class marla.messaging.schemas.AdvisoryObjective(*, type, description)[source]

Bases: BaseModel

Parameters:
  • type (str)

  • description (str)

description: str
model_config: ClassVar[ConfigDict] = {'extra': 'forbid'}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

type: str
class marla.messaging.schemas.AdvisoryRequestPayload(*, schema_version, run_id, request_id, episode_id, step, source_observation_id, objective, observation, legal_actions)[source]

Bases: BaseModel

Parameters:
episode_id: int
legal_actions: list[AdvisoryActionDescriptor]
model_config: ClassVar[ConfigDict] = {'extra': 'forbid'}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

objective: AdvisoryObjective
observation: dict[str, Any]
request_id: str
run_id: str
schema_version: str
source_observation_id: str
step: int
class marla.messaging.schemas.AdvisoryResponsePayload(*, schema_version, run_id, request_id, scores, model_version, prompt_version, knowledge_version, inference_latency_ms, retrieved_rule_ids=<factory>)[source]

Bases: BaseModel

Parameters:
inference_latency_ms: float
knowledge_version: str
model_config: ClassVar[ConfigDict] = {'extra': 'forbid'}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_version: str
prompt_version: str
request_id: str
retrieved_rule_ids: list[str]
run_id: str
schema_version: str
scores: dict[str, float]
class marla.messaging.schemas.MessageMetadata(performative, message_type, schema_version, run_id, conversation_id, request_id, sender_alias, receiver_alias)[source]

Bases: object

The envelope fields required on every MARLA SPADE message.

Parameters:
  • performative (str)

  • message_type (MessageType)

  • schema_version (str)

  • run_id (str)

  • conversation_id (str)

  • request_id (str)

  • sender_alias (str)

  • receiver_alias (str)

conversation_id: str
message_type: MessageType
performative: str
receiver_alias: str
request_id: str
run_id: str
schema_version: str
sender_alias: str
class marla.messaging.schemas.MessageType(value)[source]

Bases: str, Enum

An enumeration.

ADVISORY_REQUEST = 'ADVISORY_REQUEST'
ADVISORY_RESPONSE = 'ADVISORY_RESPONSE'
CORRECTION_REQUEST = 'CORRECTION_REQUEST'
EXPERIMENT_FAILED = 'EXPERIMENT_FAILED'
READY = 'READY'
READY_CHECK = 'READY_CHECK'
START_EXPERIMENT = 'START_EXPERIMENT'
STOP_EXPERIMENT = 'STOP_EXPERIMENT'
exception marla.messaging.schemas.MessageValidationError[source]

Bases: Exception

Raised when a received message’s envelope is missing or malformed.

Build SPADE messages carrying the MARLA envelope (spec section 10).

marla.messaging.builders.build_message(to_jid, metadata, extra_metadata=None, payload=None)[source]

Build a SPADE Message with the full MARLA envelope as metadata.

payload, if given, is JSON-encoded into the message body; envelope fields always live in metadata, never in the body, so the Gatekeeper can validate correlation/routing without parsing JSON.

The body is never left empty, even when there is no payload: slixmpp only fires its generic "message" event (the one SPADE’s dispatcher listens on) for stanzas matching message/body – a message that is all metadata and no body is silently never delivered to any behaviour, on any server, in-process or across a real connection. "{}" is a harmless placeholder (valid, empty JSON) for messages that carry no payload, such as the READY_CHECK/READY/STOP_EXPERIMENT handshake.

Parameters:
Return type:

spade.message.Message

marla.messaging.builders.new_id(prefix)[source]
Parameters:

prefix (str)

Return type:

str

Parse the MARLA envelope and JSON payload out of a received SPADE message.

marla.messaging.parsers.parse_json_body(message)[source]
Parameters:

message (spade.message.Message)

Return type:

dict[str, Any]

marla.messaging.parsers.parse_metadata(message)[source]
Parameters:

message (spade.message.Message)

Return type:

MessageMetadata

Gatekeeper-side advisory response validation (spec sections 9-10), SPADE-free.

Separated from the schema models themselves because “exact legal action-ID coverage” and “expected sender/request correlation” checks need context (the original request’s action IDs, the expected requester alias) that a standalone Pydantic model can’t carry. Kept independent of SPADE so it is directly unit-testable.

exception marla.messaging.advisory_validation.AdvisoryValidationError(reason, detail='')[source]

Bases: Exception

A single reason an advisory response was rejected, matching spec section 10’s checklist.

Parameters:
marla.messaging.advisory_validation.validate_advisory_response_body(raw_body, expected_run_id, expected_request_id, expected_action_ids)[source]

Validate a Plan Maker response body against its originating request’s context.

Raises AdvisoryValidationError with a specific reason on the first failing check: invalid_schema, run_id_mismatch, request_id_mismatch, or action_id_coverage_mismatch. Score numeric-type and [0,1] bound checks happen inside AdvisoryResponsePayload itself and surface as invalid_schema.

Parameters:
Return type:

AdvisoryResponsePayload