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