use bb_ir::proto::onnx::NodeProto;
use bb_ir::wire_shape;
use bb_runtime::atomic::DispatchResult;
use bb_runtime::bus::OpError;
use bb_runtime::framework::{BlockReason, Decision};
use bb_runtime::ids::PeerId;
use bb_runtime::runtime::RuntimeResourceRef;
use bb_runtime::slot_value::SlotValue;
use bb_runtime::syscall::values::TriggerValue;
pub struct PeerHealthGateTxOp;
pub const DOMAIN: &str = "ai.bytesandbrains.syscall";
pub const OP_TYPE: &str = "PeerHealthGateTx";
pub const ATTR_PEER: &str = wire_shape::ATTR_PEER;
pub fn invoke(
node: &NodeProto,
_inputs: &[(&str, &dyn SlotValue)],
ctx: &mut RuntimeResourceRef<'_>,
) -> Result<DispatchResult, OpError> {
let peer = read_peer_attr(node).ok_or_else(|| OpError {
detail: format!("PeerHealthGateTx missing required `{ATTR_PEER}` attribute"),
..Default::default()
})?;
let now_ns = ctx.time.scheduler.now_ns();
match ctx
.peers
.governor
.check_outbound(peer, ctx.peers.backoff, now_ns)
{
Decision::Allow => Ok(DispatchResult::Immediate(vec![(
"trigger".to_string(),
Box::new(TriggerValue),
)])),
Decision::Deny(reason) => Err(OpError {
detail: format!(
"PeerHealthGateTx denied send to peer {peer:?}: reason={}",
reason_label(&reason),
),
..Default::default()
}),
}
}
pub fn reason_label(reason: &BlockReason) -> &'static str {
match reason {
BlockReason::Blocklisted => "blocklisted",
BlockReason::NotAllowlisted => "not_allowlisted",
BlockReason::Cooldown { .. } => "cooldown",
}
}
fn read_peer_attr(node: &NodeProto) -> Option<PeerId> {
wire_shape::read_peer_bytes(node).and_then(|bytes| PeerId::from_bytes(bytes).ok())
}
use bb_runtime::registry::OpRegistration as _BbOpsSyscallReg;
inventory::submit! {
_BbOpsSyscallReg {
domain: DOMAIN,
op_type: OP_TYPE,
invoke,
kind: bb_runtime::registry::RegistrationKind::Syscall,
}
}