systemprompt_models/brain/
recall.rs1use std::collections::BTreeMap;
12
13use chrono::{DateTime, Utc};
14use serde::{Deserialize, Serialize};
15use systemprompt_identifiers::{BrainFactId, BrainRecallId, UserId, WorkflowId};
16
17use super::fact::BrainFact;
18use super::scope::ScopeLevel;
19use crate::auth::RequestActor;
20use crate::origin::ClientKind;
21
22pub use super::recall_query::RecallQuery;
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
26#[serde(rename_all = "snake_case")]
27pub enum RetrievalTier {
28 Pinned,
29 Subject,
30 Linked,
31 Context,
32 FullText,
33 Extension,
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
38#[serde(rename_all = "snake_case")]
39pub enum WithheldReason {
40 Scope,
41 Source,
42 AgentCapped,
43 Stale,
44 ReviewDue,
45 Disputed,
46 Quota,
47 MaxFacts,
48 Budget,
49 LowScore,
50 ContextCap,
51 UnknownName,
52 NoSignal,
53 Unavailable,
54}
55
56impl WithheldReason {
57 #[must_use]
58 pub const fn as_str(self) -> &'static str {
59 match self {
60 Self::Scope => "scope",
61 Self::Source => "source",
62 Self::AgentCapped => "agent_capped",
63 Self::Stale => "stale",
64 Self::ReviewDue => "review_due",
65 Self::Disputed => "disputed",
66 Self::Quota => "quota",
67 Self::MaxFacts => "max_facts",
68 Self::Budget => "budget",
69 Self::LowScore => "low_score",
70 Self::ContextCap => "context_cap",
71 Self::UnknownName => "unknown_name",
72 Self::NoSignal => "no_signal",
73 Self::Unavailable => "unavailable",
74 }
75 }
76}
77
78#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
88pub struct RecallCtx {
89 pub user_id: UserId,
90 pub actor: RequestActor,
91 pub client: ClientKind,
92 pub project: Option<String>,
93 pub workflow: Option<WorkflowId>,
94 pub scopes: Vec<ScopeLevel>,
95 pub now: DateTime<Utc>,
96 pub as_of: DateTime<Utc>,
97 #[serde(default)]
98 pub held_keys: Vec<String>,
99}
100
101impl RecallCtx {
102 #[must_use]
103 pub fn new(user_id: UserId, actor: RequestActor, now: DateTime<Utc>) -> Self {
104 Self {
105 user_id,
106 actor,
107 client: ClientKind::Other,
108 project: None,
109 workflow: None,
110 scopes: ScopeLevel::ALL.to_vec(),
111 now,
112 as_of: now,
113 held_keys: Vec::new(),
114 }
115 }
116
117 #[must_use]
118 pub fn with_as_of(mut self, as_of: DateTime<Utc>) -> Self {
119 self.as_of = as_of.min(self.now);
120 self
121 }
122
123 #[must_use]
124 pub fn with_held_keys(mut self, keys: Vec<String>) -> Self {
125 self.held_keys = keys;
126 self
127 }
128
129 #[must_use]
130 pub const fn with_client(mut self, client: ClientKind) -> Self {
131 self.client = client;
132 self
133 }
134
135 #[must_use]
136 pub fn with_project(mut self, project: impl Into<String>) -> Self {
137 self.project = Some(project.into());
138 self
139 }
140
141 #[must_use]
142 pub fn with_workflow(mut self, workflow: WorkflowId) -> Self {
143 self.workflow = Some(workflow);
144 self
145 }
146
147 #[must_use]
148 pub fn with_scopes(mut self, scopes: Vec<ScopeLevel>) -> Self {
149 self.scopes = scopes;
150 self
151 }
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
156pub struct TierRank {
157 pub tier: RetrievalTier,
158 pub rank: u32,
159}
160
161#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
163pub struct ScoredFact {
164 pub fact: BrainFact,
165 pub score: f64,
166 pub ranks: Vec<TierRank>,
167}
168
169#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
171pub struct WithheldFact {
172 pub id: BrainFactId,
173 pub reason: WithheldReason,
174}
175
176#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
178pub struct InjectionPlan {
179 pub facts: Vec<ScoredFact>,
180 pub withheld: Vec<WithheldFact>,
181 pub tokens: u32,
182}
183
184#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
186pub struct FactRef {
187 pub id: BrainFactId,
188 pub revision: i32,
189}
190
191#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
197pub struct StoredBlock {
198 pub rendered: String,
199 pub as_of: DateTime<Utc>,
200 pub facts: Vec<FactRef>,
201 #[serde(default)]
202 pub late: bool,
203}
204
205#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
208pub struct RecallAudit {
209 pub recall_id: BrainRecallId,
210 pub injected: Vec<FactRef>,
211 pub withheld: BTreeMap<WithheldReason, u32>,
212 pub tokens: u32,
213 pub as_of: DateTime<Utc>,
214}
215
216impl RecallAudit {
217 #[must_use]
218 pub fn withheld_total(&self) -> u32 {
219 self.withheld.values().sum()
220 }
221}
222
223#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
225pub struct RerankedFact {
226 pub id: BrainFactId,
227 pub rank: u32,
228}
229
230#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
234pub struct RecallRerank {
235 pub reranker: String,
236 pub ranks: Vec<RerankedFact>,
237}