Skip to main content

bb_ir/
keys.rs

1//! Single source of truth for metadata-key string constants shared
2//! across DSL → Compiler → Runtime. Reference these constants rather
3//! than re-typing `"ai.bytesandbrains.*"` literals.
4
5// ── Wire transport classification (compiler → runtime) ─────────────
6
7/// Per-edge classification stamped by `analyze_wire_edges`. Value
8/// is [`WIRE_TRANSPORT_DATA`] or [`WIRE_TRANSPORT_TRIGGER_ONLY`].
9pub const WIRE_TRANSPORT_KEY: &str = "ai.bytesandbrains.wire_transport";
10
11/// Value of [`WIRE_TRANSPORT_KEY`] for full-payload edges.
12pub const WIRE_TRANSPORT_DATA: &str = "data";
13
14/// Value of [`WIRE_TRANSPORT_KEY`] for trigger-only edges.
15pub const WIRE_TRANSPORT_TRIGGER_ONLY: &str = "trigger_only";
16
17// ── Destination routing (compiler → runtime envelope) ──────────────
18
19/// Prefix for per-fill multiaddr suffixes stamped on `Send` nodes.
20/// Suffixes are keyed `dest_suffix.<i>` for fill index `i`.
21pub const DEST_SUFFIX_ATTR_PREFIX: &str = "ai.bytesandbrains.dest_suffix.";
22
23/// Prefix for per-fill destination site names stamped on `Send`
24/// nodes. Used by the lower-network-io pass to derive the partition
25/// name on the receive side.
26pub const DEST_SITE_NAME_PREFIX: &str = "ai.bytesandbrains.dest_site_name.";
27
28// ── DSL→Compiler binding identity ──────────────────────────────────
29
30/// Concrete-component type. Paired with [`INSTANCE_KEY`].
31pub const CONCRETE_TYPE_KEY: &str = "ai.bytesandbrains.concrete_type";
32
33/// Per-instance disambiguator paired with [`CONCRETE_TYPE_KEY`].
34pub const INSTANCE_KEY: &str = "ai.bytesandbrains.instance";
35
36/// Required-trait identifier for a generic slot. Paired with
37/// [`SLOT_ID_KEY`].
38pub const REQUIRED_TRAIT_KEY: &str = "ai.bytesandbrains.required_trait";
39
40/// Per-slot disambiguator paired with [`REQUIRED_TRAIT_KEY`].
41pub const SLOT_ID_KEY: &str = "ai.bytesandbrains.slot_id";
42
43/// Stamped on `wire.Recv` whose payload feeds a role NodeProto's
44/// `slot_id` input. Drives `decode_typed_fill`'s backend-mediated
45/// branch. Absent on framework-carrier Recv nodes.
46pub const RECV_SLOT_ID_KEY: &str = "ai.bytesandbrains.recv_slot_id";
47
48// ── Class tagging ──────────────────────────────────────────────────
49
50/// Default-class tag used by `infer_peer_classes` when a peer
51/// expression has no explicit class denotation.
52pub const DEFAULT_CLASS: &str = "ai.bytesandbrains.default_class";
53
54// ── Module phase (DSL → install → engine) ──────────────────────────
55
56/// Distinguishes a Module's body recording from its bootstrap.
57/// Drives `Engine::bootstrap_function_key` seeding on first poll.
58pub const MODULE_PHASE_KEY: &str = "ai.bytesandbrains.module_phase";
59
60/// Body-phase value of [`MODULE_PHASE_KEY`].
61pub const MODULE_PHASE_BODY: &str = "body";
62
63/// Bootstrap-phase value of [`MODULE_PHASE_KEY`]. Fired once on
64/// first poll; body parks until descendants drain.
65pub const MODULE_PHASE_BOOTSTRAP: &str = "bootstrap";
66
67// ── Backend subgraph ───────────────────────────────────────────────
68
69/// `op_type` for a carrier NodeProto wrapping a `GraphProto` body.
70/// Engine forwards these to `Backend::dispatch` for native-graph
71/// specialization.
72pub const BACKEND_SUBGRAPH_OP: &str = "BackendSubgraph";
73
74/// Attribute key for the embedded `GraphProto` body inside a
75/// `BackendSubgraph` carrier. Stamped by `collapse_backend_subgraphs`.
76pub const BACKEND_SUBGRAPH_BODY_ATTR: &str = "body";
77
78// ── Dedup ──────────────────────────────────────────────────────────
79
80/// Attribute key carrying the dedup slot identifier on a
81/// `DedupGateRx` op. Together with the inbound peer this seeds the
82/// `(peer, slot)` dedup key.
83pub const DEDUP_SLOT: &str = "ai.bytesandbrains.dedup_slot";
84
85// ── Wire pairing (DSL Graph::wire → compiler/runtime) ──────────────
86
87/// Per-edge wire-pairing token minted by `Graph::wire` and read by
88/// the compiler's `discover_wire_edges` pass.
89pub const WIRE_ID_KEY: &str = "ai.bytesandbrains.wire.wire_id";
90
91// ── Wire batching (compiler → runtime) ─────────────────────────────
92
93/// Per-edge batch grouping id stamped by `analyze_wire_edges`.
94pub const BATCH_GROUP_KEY: &str = "ai.bytesandbrains.batch_group_id";
95
96/// Trigger type denotation; matches `<Trigger as WireType>::DENOTATION`.
97pub const TRIGGER_DENOTATION: &str = "bb.trigger";
98
99// ── Wire chain (compiler → runtime deadline derivation) ────────────
100
101/// Static chain depth from this Send. Runtime multiplies by
102/// `per_hop_budget_ns` to size deadlines against the full round-trip.
103pub const CHAIN_DEPTH_KEY: &str = "ai.bytesandbrains.wire.chain_depth";
104
105/// Comma-separated targets participating in the chain (originating
106/// target excluded). Used for per-hop budget renegotiation.
107pub const CHAIN_TARGETS_KEY: &str = "ai.bytesandbrains.wire.chain_targets";
108
109// ── ATTR_PEER (re-export for ergonomic single-import) ──────────────
110
111/// Re-export of [`crate::syscall_ids::ATTR_PEER`] so a single
112/// `bb_ir::keys::*` import covers every wire-node key.
113pub use crate::syscall_ids::ATTR_PEER;
114
115/// Re-export of [`crate::version::FRAMEWORK_IR_VERSION_KEY`] for
116/// the same reason.
117pub use crate::version::FRAMEWORK_IR_VERSION_KEY;
118
119// ── Generic-slot dependency metadata (compiler → runtime) ──────────
120
121/// Prefix for per-dependency entries: `dep.<role> = "<slot>"`.
122/// Stamped by `resolve_component_dependencies`.
123pub const DEP_SLOT_KEY_PREFIX: &str = "ai.bytesandbrains.dep.";
124
125/// Build the metadata key for a dependency on the canonical role
126/// string (PascalCase - e.g. `"Backend"`, `"Index"`).
127pub fn dep_slot_key(role: &str) -> String {
128    format!("{DEP_SLOT_KEY_PREFIX}{role}")
129}
130
131/// If `key` is a `DEP_SLOT_KEY_PREFIX`-namespaced dependency entry,
132/// return the bare role string. Otherwise `None`.
133pub fn role_from_dep_slot_key(key: &str) -> Option<&str> {
134    key.strip_prefix(DEP_SLOT_KEY_PREFIX)
135}
136
137// ── Constructor helpers ────────────────────────────────────────────
138
139use crate::proto::onnx::{attribute_proto, AttributeProto};
140
141/// Per-input `dest_suffix.<name>` AttributeProto carrying the
142/// resolved destination multiaddr suffix.
143pub fn dest_suffix_attribute(input_name: &str, address_bytes: Vec<u8>) -> AttributeProto {
144    AttributeProto {
145        name: format!("{DEST_SUFFIX_ATTR_PREFIX}{input_name}"),
146        r#type: attribute_proto::AttributeType::String as i32,
147        s: address_bytes,
148        ..Default::default()
149    }
150}
151
152// ── Dependency-metadata stamping + reading helpers ─────────────────
153
154use crate::component::DependencyDecl;
155use crate::proto::onnx::{NodeProto, StringStringEntryProto};
156
157/// Stamp `deps` onto `node.metadata_props` as
158/// `dep.<role> = "<slot>"`. Idempotent on duplicates.
159pub fn stamp_dependency_metadata(node: &mut NodeProto, deps: &[DependencyDecl]) {
160    for dep in deps {
161        let key = dep_slot_key(dep.role);
162        let already = node
163            .metadata_props
164            .iter()
165            .any(|e| e.key == key && e.value == dep.slot);
166        if already {
167            continue;
168        }
169        node.metadata_props.push(StringStringEntryProto {
170            key,
171            value: dep.slot.to_string(),
172        });
173    }
174}
175
176/// Read `dep.<role> = "<slot>"` entries as borrowed `(role, slot)`.
177pub fn read_dependency_metadata(node: &NodeProto) -> impl Iterator<Item = (&str, &str)> + '_ {
178    node.metadata_props.iter().filter_map(|entry| {
179        let role = role_from_dep_slot_key(&entry.key)?;
180        Some((role, entry.value.as_str()))
181    })
182}
183
184// ── Compilation passport + slot binding (compiler → install) ───────
185
186use crate::proto::onnx::ModelProto;
187
188/// Compilation passport key. Missing → `InstallError::NotCompiled`;
189/// mismatched value → `InstallError::IncompatibleCompiledVersion`.
190pub const COMPILED_KEY: &str = "ai.bytesandbrains.compiled";
191
192/// Current compilation passport value. Bumps on IR-breaking changes.
193pub const COMPILED_CURRENT_VERSION: &str = "v1";
194
195/// Prefix for per-target per-slot binding entries:
196/// `binding.<target>.<slot> = "<role>|<TYPE_NAME>|<slot_id|-1>"`.
197pub const BINDING_KEY_PREFIX: &str = "ai.bytesandbrains.binding.";
198
199/// Build the binding key for `(target, slot)`. Splits at the FIRST
200/// dot after the prefix when parsing — slot names may contain dots.
201pub fn binding_key(target: &str, slot: &str) -> String {
202    format!("{BINDING_KEY_PREFIX}{target}.{slot}")
203}
204
205/// Parse a binding key into `(target, slot)`. `None` for non-binding.
206pub fn parse_binding_key(key: &str) -> Option<(&str, &str)> {
207    let rest = key.strip_prefix(BINDING_KEY_PREFIX)?;
208    let (target, slot) = rest.split_once('.')?;
209    Some((target, slot))
210}
211
212/// Encode a binding's `(role, TYPE_NAME, slot_id_or_neg1)` triple
213/// into the pipe-delimited value `"<role>|<TYPE_NAME>|<slot_id_or_-1>"`.
214/// Reads inversely via [`parse_binding_value`].
215pub fn encode_binding_value(role: &str, type_name: &str, slot_id_or_neg1: i64) -> String {
216    format!("{role}|{type_name}|{slot_id_or_neg1}")
217}
218
219/// Decompose a binding value into `(role, TYPE_NAME, slot_id_or_-1)`.
220/// `None` when the format doesn't match (the caller surfaces this as
221/// `InstallError::InvalidBindingTable`).
222pub fn parse_binding_value(value: &str) -> Option<(&str, &str, i64)> {
223    let mut parts = value.splitn(3, '|');
224    let role = parts.next()?;
225    let type_name = parts.next()?;
226    let slot_id: i64 = parts.next()?.parse().ok()?;
227    Some((role, type_name, slot_id))
228}
229
230/// Stamp `(key, value)` onto `model.metadata_props`, replacing any
231/// existing entry with the same key. Idempotent on re-runs.
232pub fn stamp_model_metadata(model: &mut ModelProto, key: &str, value: &str) {
233    if let Some(existing) = model.metadata_props.iter_mut().find(|e| e.key == key) {
234        existing.value = value.to_string();
235        return;
236    }
237    model.metadata_props.push(StringStringEntryProto {
238        key: key.to_string(),
239        value: value.to_string(),
240    });
241}
242
243/// Read a `ModelProto.metadata_props` entry by key. Returns the
244/// borrowed value string; `None` when the key isn't present.
245pub fn read_model_metadata<'a>(model: &'a ModelProto, key: &str) -> Option<&'a str> {
246    model
247        .metadata_props
248        .iter()
249        .find(|e| e.key == key)
250        .map(|e| e.value.as_str())
251}
252
253// ── FunctionProto metadata helpers ─────────────────────────────────
254
255use crate::proto::onnx::FunctionProto;
256
257/// Read `MODULE_PHASE_KEY` off a FunctionProto. `None` when the
258/// key is absent.
259pub fn read_function_module_phase(function: &FunctionProto) -> Option<&str> {
260    function
261        .metadata_props
262        .iter()
263        .find(|e| e.key == MODULE_PHASE_KEY)
264        .map(|e| e.value.as_str())
265}
266
267/// Stamp `MODULE_PHASE_KEY` onto a FunctionProto. Replaces existing.
268pub fn stamp_function_module_phase(function: &mut FunctionProto, phase: &str) {
269    if let Some(existing) = function
270        .metadata_props
271        .iter_mut()
272        .find(|e| e.key == MODULE_PHASE_KEY)
273    {
274        existing.value = phase.to_string();
275        return;
276    }
277    function.metadata_props.push(StringStringEntryProto {
278        key: MODULE_PHASE_KEY.to_string(),
279        value: phase.to_string(),
280    });
281}
282