Files
BEHAVE/BEHAVE-SHELL/behave_shell/spec/event_adapter.py
anti 22b57307cf refactor: drop decnet- prefix — BEHAVE is now standalone
Rename packages and imports:
  decnet-behave-core  → behave-core
  decnet-behave-shell → behave-shell
  decnet-behave-text  → behave-text
  decnet_behave_*     → behave_*

BEHAVE is no longer a DECNET sub-project.
2026-05-10 06:20:01 -04:00

59 lines
2.2 KiB
Python

# SPDX-License-Identifier: GPL-3.0-or-later
"""DECNET bus interop. Aligns BEHAVE Observation with DECNET Event payload shape.
DECNET's Event (decnet/bus/base.py:26) carries ``(topic, payload, type, v, ts, id)``.
A BEHAVE Observation maps onto that envelope as follows:
topic = "attacker.observation." + observation.primitive
payload = observation.model_dump(exclude={"id", "ts", "v"})
type = observation.primitive
v = observation.v
ts = observation.ts
id = observation.id
The publisher must set ``topic`` from the primitive when calling ``bus.publish()``;
DECNET's bus does not trust topic from the wire (anti-spoofing, base.py:60-76).
This module does NOT import DECNET. The adapter speaks dicts; consumers wire it
to their own bus.
"""
from __future__ import annotations
from typing import Any
from .envelope import Observation
TOPIC_PREFIX: str = "attacker.observation"
def event_topic_for(primitive: str) -> str:
"""Return the canonical DECNET bus topic for a BEHAVE primitive."""
return f"{TOPIC_PREFIX}.{primitive}"
def to_event_payload(obs: Observation) -> dict[str, Any]:
"""Project an Observation into a dict suitable for ``Event.payload``.
Excludes ``id``, ``ts``, and ``v`` because those are carried at the Event
envelope level by DECNET, not in the payload body.
"""
return obs.model_dump(exclude={"id", "ts", "v"}, mode="json")
def from_event_payload(primitive: str, payload: dict[str, Any]) -> Observation:
"""Reconstruct an Observation from ``(topic-derived primitive, Event.payload)``.
The ``primitive`` argument is the trailing segment of the bus topic, NOT a
field read from the payload — relying on the wire-side ``primitive`` field
would let a misbehaving publisher spoof observations on topics they don't
actually publish to. This mirrors DECNET's ``Event.from_dict`` discipline
(decnet/bus/base.py:60-76).
"""
if "primitive" in payload and payload["primitive"] != primitive:
raise ValueError(
f"payload.primitive ({payload['primitive']!r}) does not match "
f"topic-derived primitive ({primitive!r}); refusing to reconstruct"
)
return Observation.model_validate({**payload, "primitive": primitive})