corpus_ipc/models/message.rs
1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3use serde::{Deserialize, Serialize};
4
5use crate::validation::{ProtocolLimits, Validate, ValidationError, check_finite};
6
7use super::de::de_loss;
8use super::{
9 ConfigPayload, EmbeddingBatch, GradientBatch, NeuromodulatorSnapshot, SpikeBatch,
10 StimulusBatch, TraceBatch,
11};
12
13/// Core message enum for cross-process IPC.
14///
15/// Messages are separated into:
16/// - Input messages (spikes, embeddings, stimuli, neuromodulators, config)
17/// - Output messages (gradients, traces, training status)
18/// - Control messages (shutdown, ping)
19///
20/// Variant names (`Spikes`, `EligibilityTraces`, …) are serde identifiers and
21/// must stay stable. The payloads they wrap are IPC transport types, not
22/// in-memory training structs (see the module docs).
23///
24/// Versioned ingress should go through
25/// [`crate::decode_ipc_message_json`]: that entry point classifies
26/// [`crate::WireCompatibility`] before the payload is used. Direct
27/// `Deserialize` on this enum is the unversioned (legacy) encoding.
28///
29/// Unknown object fields on payloads are ignored (forward compatible).
30/// Unknown variant names fail to deserialize; this enum has no `Default`
31/// and no serde `other` catch-all, so they never become a valid message.
32#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
33pub enum IpcMessage {
34 // Input messages
35 /// Wire payload for an IPC [`SpikeBatch`] (not an in-memory training batch).
36 Spikes(SpikeBatch),
37 Embeddings(EmbeddingBatch),
38 /// Typed continuous runtime stimulus ingress between a producer and a
39 /// consumer. See [`StimulusBatch`] for channel-width and
40 /// invalid/missing-channel semantics.
41 Stimuli(StimulusBatch),
42 /// Typed neuromodulator ingress, replacing an unstructured float tail.
43 /// See [`NeuromodulatorSnapshot`] and [`Validate`].
44 Neuromodulators(NeuromodulatorSnapshot),
45 Loss(#[serde(deserialize_with = "de_loss")] f32),
46 ConfigUpdate(ConfigPayload),
47
48 // Output messages
49 GradientUpdate(GradientBatch),
50 /// Wire payload for an IPC [`TraceBatch`] (not an in-memory training batch).
51 EligibilityTraces(TraceBatch),
52 TrainingComplete,
53
54 // Control
55 Shutdown,
56 Ping,
57}
58
59impl Validate for IpcMessage {
60 fn validate_with(&self, limits: ProtocolLimits) -> Result<(), ValidationError> {
61 match self {
62 Self::Spikes(batch) => batch.validate_with(limits),
63 Self::Embeddings(batch) => batch.validate_with(limits),
64 Self::Stimuli(batch) => batch.validate_with(limits),
65 Self::Neuromodulators(snapshot) => snapshot.validate_with(limits),
66 Self::ConfigUpdate(payload) => payload.validate_with(limits),
67 Self::GradientUpdate(batch) => batch.validate_with(limits),
68 Self::EligibilityTraces(batch) => batch.validate_with(limits),
69 Self::Loss(value) => check_finite("Loss", *value),
70 Self::TrainingComplete | Self::Shutdown | Self::Ping => Ok(()),
71 }
72 }
73}