Skip to main content

spatialrust_episode/
episode.rs

1//! Episode containers over synchronized multimodal memory episodes.
2
3use spatialrust_sync::MemoryEpisode;
4
5use crate::{AnnotationLayer, EpisodeError, EpisodeResult, ModelProvenance};
6
7/// Stable episode identifier.
8#[derive(Clone, Debug, PartialEq, Eq, Hash)]
9pub struct EpisodeId(pub String);
10
11impl EpisodeId {
12    /// Creates an episode id.
13    #[must_use]
14    pub fn new(value: impl Into<String>) -> Self {
15        Self(value.into())
16    }
17}
18
19/// One embodied-AI episode with optional annotations and provenance.
20#[derive(Clone, Debug, PartialEq)]
21pub struct Episode {
22    /// Episode id.
23    pub id: EpisodeId,
24    /// Deterministic multimodal payload.
25    pub memory: MemoryEpisode,
26    /// Annotation layers.
27    pub annotations: Vec<AnnotationLayer>,
28    /// Model provenance records.
29    pub provenance: Vec<ModelProvenance>,
30}
31
32impl Episode {
33    /// Creates an episode with empty annotation/provenance lists.
34    pub fn try_new(id: impl Into<EpisodeId>, memory: MemoryEpisode) -> EpisodeResult<Self> {
35        let id = id.into();
36        if id.0.is_empty() {
37            return Err(EpisodeError::InvalidConfiguration("episode id must be non-empty".into()));
38        }
39        Ok(Self { id, memory, annotations: Vec::new(), provenance: Vec::new() })
40    }
41}
42
43impl From<&str> for EpisodeId {
44    fn from(value: &str) -> Self {
45        Self(value.to_owned())
46    }
47}
48
49impl From<String> for EpisodeId {
50    fn from(value: String) -> Self {
51        Self(value)
52    }
53}