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