Skip to main content

systemprompt_models/brain/
envelope.rs

1//! A claimed outbox event as the brain pipeline sees it: the event, its
2//! pathway and that pathway's weight.
3//!
4//! The session pathway (`session.turn.closed`) is scaffolding: the gateway
5//! emits it only when the profile opts in, and with no filter gathering it
6//! nothing is extracted.
7//!
8//! Copyright (c) systemprompt.io — Business Source License 1.1.
9//! See <https://systemprompt.io> for licensing details.
10
11use chrono::{DateTime, Utc};
12use schemars::JsonSchema;
13use serde::{Deserialize, Serialize};
14use systemprompt_identifiers::EventOutboxId;
15
16use super::event::{BrainEvent, BrainEventKind};
17
18/// The route an event takes into the brain.
19#[derive(
20    Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, JsonSchema,
21)]
22#[serde(rename_all = "snake_case")]
23pub enum Pathway {
24    DocumentRevision,
25    CommentResolved,
26    Entered,
27    Session,
28}
29
30impl Pathway {
31    #[must_use]
32    pub const fn as_str(self) -> &'static str {
33        match self {
34            Self::DocumentRevision => "document_revision",
35            Self::CommentResolved => "comment_resolved",
36            Self::Entered => "entered",
37            Self::Session => "session",
38        }
39    }
40
41    #[must_use]
42    pub fn from_kind(kind: &BrainEventKind) -> Option<Self> {
43        match kind.as_str() {
44            BrainEventKind::REVISION_LANDED => Some(Self::DocumentRevision),
45            BrainEventKind::COMMENT_RESOLVED => Some(Self::CommentResolved),
46            BrainEventKind::FACT_ENTERED => Some(Self::Entered),
47            BrainEventKind::SESSION_TURN_CLOSED => Some(Self::Session),
48            _ => None,
49        }
50    }
51
52    #[must_use]
53    pub const fn default_weight(self) -> PathwayWeight {
54        match self {
55            Self::DocumentRevision | Self::CommentResolved | Self::Entered => PathwayWeight::High,
56            Self::Session => PathwayWeight::Medium,
57        }
58    }
59}
60
61/// How urgently a pathway's events are processed: `High` immediately,
62/// `Medium` and `Low` in scheduled batches, `None` never.
63#[derive(
64    Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, JsonSchema,
65)]
66#[serde(rename_all = "snake_case")]
67pub enum PathwayWeight {
68    None,
69    Low,
70    Medium,
71    High,
72}
73
74/// One claimed outbox event, normalised for the filters and extractors.
75#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
76pub struct BrainEnvelope {
77    pub outbox_id: EventOutboxId,
78    pub event: BrainEvent,
79    pub pathway: Pathway,
80    pub weight: PathwayWeight,
81    pub received_at: DateTime<Utc>,
82}
83
84impl BrainEnvelope {
85    #[must_use]
86    pub fn new(
87        outbox_id: EventOutboxId,
88        event: BrainEvent,
89        received_at: DateTime<Utc>,
90    ) -> Option<Self> {
91        let pathway = Pathway::from_kind(&event.kind)?;
92        Some(Self {
93            outbox_id,
94            event,
95            pathway,
96            weight: pathway.default_weight(),
97            received_at,
98        })
99    }
100}