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