Skip to main content

systemprompt_models/brain/declarations/
mod.rs

1//! Declarative brain config: the YAML shapes under `services/brain/` and
2//! their cross-reference validation.
3//!
4//! `topics.yaml`, `filters.yaml`, `importance.yaml`, `retrieval.yaml` and
5//! `synonyms.yaml` are single documents; `kinds/`, `interests/` and `profiles/`
6//! hold one declaration per file. [`BrainDocs`] is the whole set and
7//! [`validate`] checks it.
8//!
9//! Copyright (c) systemprompt.io — Business Source License 1.1.
10//! See <https://systemprompt.io> for licensing details.
11
12mod importance;
13mod synonyms;
14mod validate;
15mod validate_retrieval;
16
17use std::collections::BTreeMap;
18
19use chrono::{DateTime, Utc};
20use serde::{Deserialize, Serialize};
21use systemprompt_identifiers::{FactKindId, InterestId, TopicId, UserId, WorkflowId};
22
23pub use importance::{
24    BaseImportance, FillOrder, ImportanceConfig, InContextImportance, ModelHint, QuorumUnit,
25    Recency, RetrievalDoc, TierWeights, UsageWeights,
26};
27pub use synonyms::{SynonymsDoc, SynonymsError};
28pub use validate::{BrainDeclError, MAX_LINKS_DEPTH, MAX_SUBJECT_LIMIT, validate};
29
30use super::envelope::Pathway;
31use super::kind_decl::KindDecl;
32use crate::notifications::AudienceSubject;
33
34/// How finely a topic or interest may report about people.
35#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
36#[serde(rename_all = "snake_case")]
37pub enum Granularity {
38    #[default]
39    Aggregate,
40    Individual,
41}
42
43/// A controlled topic and the audience it widens fact scope to.
44#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
45#[serde(deny_unknown_fields)]
46pub struct TopicDecl {
47    pub id: TopicId,
48    pub name: String,
49    #[serde(default)]
50    pub description: Option<String>,
51    #[serde(default)]
52    pub audience: Vec<AudienceSubject>,
53    #[serde(default)]
54    pub granularity: Granularity,
55}
56
57/// `topics.yaml`.
58#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
59#[serde(deny_unknown_fields, default)]
60pub struct TopicsDoc {
61    pub topics: Vec<TopicDecl>,
62}
63
64/// How an interest's matches are delivered.
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
66#[serde(rename_all = "snake_case")]
67pub enum InterestDelivery {
68    Memory,
69    Digest,
70    Alert,
71}
72
73/// Deterministic predicates of an interest; an empty list matches all.
74#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
75#[serde(deny_unknown_fields, default)]
76pub struct InterestMatch {
77    pub kinds: Vec<FactKindId>,
78    pub topics: Vec<TopicId>,
79    pub subjects: Vec<String>,
80    pub keywords: Vec<String>,
81}
82
83/// A standing question with an owner, pathways and delivery.
84#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
85#[serde(deny_unknown_fields)]
86pub struct InterestDecl {
87    pub id: InterestId,
88    pub name: String,
89    pub owner: UserId,
90    pub question: String,
91    #[serde(default)]
92    pub pathways: Vec<Pathway>,
93    #[serde(default)]
94    pub r#match: InterestMatch,
95    #[serde(default)]
96    pub granularity: Granularity,
97    #[serde(default)]
98    pub audience: Vec<AudienceSubject>,
99    #[serde(default)]
100    pub delivery: Vec<InterestDelivery>,
101    #[serde(default)]
102    pub expires: Option<DateTime<Utc>>,
103}
104
105/// Which actors an event filter admits.
106#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
107#[serde(deny_unknown_fields, default)]
108pub struct ActorFilter {
109    pub humans: bool,
110    pub attended_agents: bool,
111    pub unattended_agents: bool,
112}
113
114impl Default for ActorFilter {
115    fn default() -> Self {
116        Self {
117            humans: true,
118            attended_agents: true,
119            unattended_agents: false,
120        }
121    }
122}
123
124/// One declared event filter.
125#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
126#[serde(deny_unknown_fields)]
127pub struct FilterDecl {
128    pub name: String,
129    pub pathways: Vec<Pathway>,
130    #[serde(default)]
131    pub actors: ActorFilter,
132    #[serde(default)]
133    pub kinds: Vec<FactKindId>,
134    #[serde(default)]
135    pub debounce_secs: Option<u32>,
136}
137
138/// `filters.yaml`.
139#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
140#[serde(deny_unknown_fields, default)]
141pub struct FiltersDoc {
142    pub filters: Vec<FilterDecl>,
143}
144
145/// A workflow profile under `profiles/*.yaml`: the kinds a workflow cares
146/// about, who may confirm in it, and its injection limits.
147#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
148#[serde(deny_unknown_fields)]
149pub struct WorkflowProfileDecl {
150    pub id: WorkflowId,
151    pub name: String,
152    #[serde(default)]
153    pub kinds: Vec<FactKindId>,
154    #[serde(default)]
155    pub confirm_role: Option<String>,
156    #[serde(default)]
157    pub quotas: BTreeMap<FactKindId, u32>,
158    #[serde(default)]
159    pub budget_tokens: Option<u32>,
160}
161
162/// Every brain declaration, loaded together.
163#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
164#[serde(deny_unknown_fields, default)]
165pub struct BrainDocs {
166    pub topics: TopicsDoc,
167    pub filters: FiltersDoc,
168    pub importance: ImportanceConfig,
169    pub retrieval: RetrievalDoc,
170    pub kinds: Vec<KindDecl>,
171    pub interests: Vec<InterestDecl>,
172    pub workflows: Vec<WorkflowProfileDecl>,
173}