systemprompt_models/execution/context/
mod.rs1mod call_source;
4mod context_error;
5mod context_types;
6mod propagation;
7
8pub use call_source::CallSource;
9pub use context_error::{ContextExtractionError, ContextIdSource};
10pub use context_types::{
11 AuthContext, ExecutionContext, ExecutionSettings, RequestMetadata, UserInteractionMode,
12};
13
14use crate::ai::ToolModelConfig;
15use crate::auth::{AuthenticatedUser, RateLimitTier, UserType};
16use serde::{Deserialize, Serialize};
17use std::time::{Duration, Instant};
18use systemprompt_identifiers::{
19 Actor, AgentName, AiToolCallId, ClientId, ContextId, JwtToken, McpExecutionId, SessionId,
20 TaskId, TraceId, UserId,
21};
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct RequestContext {
25 pub auth: AuthContext,
26 pub request: RequestMetadata,
27 pub execution: ExecutionContext,
28 pub settings: ExecutionSettings,
29
30 #[serde(skip)]
31 pub user: Option<AuthenticatedUser>,
32
33 #[serde(skip, default = "Instant::now")]
34 pub start_time: Instant,
35}
36
37impl RequestContext {
38 pub fn new(
39 session_id: SessionId,
40 trace_id: TraceId,
41 context_id: ContextId,
42 agent_name: AgentName,
43 ) -> Self {
44 Self {
45 auth: AuthContext {
46 auth_token: JwtToken::new(""),
47 actor: Actor::user(UserId::new("unset")),
48 user_type: UserType::Anon,
49 act_chain: Vec::new(),
50 jti: String::new(),
51 token_exp: 0,
52 },
53 request: RequestMetadata {
54 session_id,
55 timestamp: Instant::now(),
56 client_id: None,
57 is_tracked: true,
58 fingerprint_hash: None,
59 },
60 execution: ExecutionContext {
61 trace_id,
62 context_id,
63 task_id: None,
64 ai_tool_call_id: None,
65 mcp_execution_id: None,
66 call_source: None,
67 agent_name,
68 tool_model_config: None,
69 },
70 settings: ExecutionSettings::default(),
71 user: None,
72 start_time: Instant::now(),
73 }
74 }
75
76 pub fn with_user(mut self, user: AuthenticatedUser) -> Self {
77 self.auth.actor = Actor::user(UserId::new(user.id.to_string()));
78 self.user = Some(user);
79 self
80 }
81
82 pub fn with_actor(mut self, actor: Actor) -> Self {
83 self.auth.actor = actor;
84 self
85 }
86
87 pub fn with_agent_name(mut self, agent_name: AgentName) -> Self {
88 self.execution.agent_name = agent_name;
89 self
90 }
91
92 pub fn with_context_id(mut self, context_id: ContextId) -> Self {
93 self.execution.context_id = context_id;
94 self
95 }
96
97 pub fn with_task_id(mut self, task_id: TaskId) -> Self {
98 self.execution.task_id = Some(task_id);
99 self
100 }
101
102 pub fn with_task(mut self, task_id: TaskId, call_source: CallSource) -> Self {
103 self.execution.task_id = Some(task_id);
104 self.execution.call_source = Some(call_source);
105 self
106 }
107
108 pub fn with_ai_tool_call_id(mut self, ai_tool_call_id: AiToolCallId) -> Self {
109 self.execution.ai_tool_call_id = Some(ai_tool_call_id);
110 self
111 }
112
113 pub fn with_mcp_execution_id(mut self, mcp_execution_id: McpExecutionId) -> Self {
114 self.execution.mcp_execution_id = Some(mcp_execution_id);
115 self
116 }
117
118 pub fn with_client_id(mut self, client_id: ClientId) -> Self {
119 self.request.client_id = Some(client_id);
120 self
121 }
122
123 pub const fn with_user_type(mut self, user_type: UserType) -> Self {
124 self.auth.user_type = user_type;
125 self
126 }
127
128 pub fn with_auth_token(mut self, token: impl Into<String>) -> Self {
129 self.auth.auth_token = JwtToken::new(token.into());
130 self
131 }
132
133 #[must_use]
134 pub fn with_jti(mut self, jti: impl Into<String>) -> Self {
135 self.auth.jti = jti.into();
136 self
137 }
138
139 #[must_use]
140 pub const fn with_token_exp(mut self, token_exp: i64) -> Self {
141 self.auth.token_exp = token_exp;
142 self
143 }
144
145 #[must_use]
146 pub fn jti(&self) -> &str {
147 &self.auth.jti
148 }
149
150 #[must_use]
151 pub const fn token_exp(&self) -> i64 {
152 self.auth.token_exp
153 }
154
155 #[must_use]
156 pub fn with_act_chain(mut self, act_chain: Vec<Actor>) -> Self {
157 self.auth.act_chain = act_chain;
158 self
159 }
160
161 #[must_use]
162 pub fn act_chain(&self) -> &[Actor] {
163 &self.auth.act_chain
164 }
165
166 pub const fn with_call_source(mut self, call_source: CallSource) -> Self {
167 self.execution.call_source = Some(call_source);
168 self
169 }
170
171 pub const fn with_budget(mut self, cents: i32) -> Self {
172 self.settings.max_budget_cents = Some(cents);
173 self
174 }
175
176 pub const fn with_interaction_mode(mut self, mode: UserInteractionMode) -> Self {
177 self.settings.user_interaction_mode = Some(mode);
178 self
179 }
180
181 pub const fn with_tracked(mut self, is_tracked: bool) -> Self {
182 self.request.is_tracked = is_tracked;
183 self
184 }
185
186 pub fn with_fingerprint_hash(mut self, hash: impl Into<String>) -> Self {
187 self.request.fingerprint_hash = Some(hash.into());
188 self
189 }
190
191 pub fn fingerprint_hash(&self) -> Option<&str> {
192 self.request.fingerprint_hash.as_deref()
193 }
194
195 pub fn with_tool_model_config(mut self, config: ToolModelConfig) -> Self {
196 self.execution.tool_model_config = Some(config);
197 self
198 }
199
200 pub const fn tool_model_config(&self) -> Option<&ToolModelConfig> {
201 self.execution.tool_model_config.as_ref()
202 }
203
204 pub const fn session_id(&self) -> &SessionId {
205 &self.request.session_id
206 }
207
208 pub const fn user_id(&self) -> &UserId {
209 &self.auth.actor.user_id
210 }
211
212 pub const fn actor(&self) -> &Actor {
213 &self.auth.actor
214 }
215
216 pub const fn trace_id(&self) -> &TraceId {
217 &self.execution.trace_id
218 }
219
220 pub const fn context_id(&self) -> &ContextId {
221 &self.execution.context_id
222 }
223
224 pub const fn agent_name(&self) -> &AgentName {
225 &self.execution.agent_name
226 }
227
228 pub const fn auth_token(&self) -> &JwtToken {
229 &self.auth.auth_token
230 }
231
232 pub const fn user_type(&self) -> UserType {
233 self.auth.user_type
234 }
235
236 pub const fn rate_limit_tier(&self) -> RateLimitTier {
237 self.auth.user_type.rate_tier()
238 }
239
240 pub const fn task_id(&self) -> Option<&TaskId> {
241 self.execution.task_id.as_ref()
242 }
243
244 pub const fn client_id(&self) -> Option<&ClientId> {
245 self.request.client_id.as_ref()
246 }
247
248 pub const fn ai_tool_call_id(&self) -> Option<&AiToolCallId> {
249 self.execution.ai_tool_call_id.as_ref()
250 }
251
252 pub const fn mcp_execution_id(&self) -> Option<&McpExecutionId> {
253 self.execution.mcp_execution_id.as_ref()
254 }
255
256 pub const fn call_source(&self) -> Option<CallSource> {
257 self.execution.call_source
258 }
259
260 pub const fn is_authenticated(&self) -> bool {
261 self.user.is_some()
262 }
263
264 pub const fn is_system(&self) -> bool {
265 matches!(self.auth.user_type, UserType::Service)
266 }
267
268 pub const fn is_anonymous(&self) -> bool {
269 matches!(self.auth.user_type, UserType::Anon)
270 }
271
272 pub fn elapsed(&self) -> Duration {
273 self.start_time.elapsed()
274 }
275
276 pub fn validate_task_execution(&self) -> Result<(), String> {
277 if self.execution.task_id.is_none() {
278 return Err("Missing task_id for task execution".to_owned());
279 }
280 Ok(())
281 }
282
283 pub fn validate_authenticated(&self) -> Result<(), String> {
284 if self.auth.auth_token.as_str().is_empty() {
285 return Err("Missing authentication token".to_owned());
286 }
287 if matches!(self.auth.user_type, UserType::Anon) {
288 return Err("User is not authenticated".to_owned());
289 }
290 Ok(())
291 }
292}