mod call_source;
mod context_error;
mod context_types;
mod propagation;
pub use call_source::CallSource;
pub use context_error::{ContextExtractionError, ContextIdSource, TASK_BASED_CONTEXT_MARKER};
pub use context_types::{
AuthContext, ExecutionContext, ExecutionSettings, RequestMetadata, UserInteractionMode,
};
use crate::ai::ToolModelConfig;
use crate::auth::{AuthenticatedUser, RateLimitTier, UserType};
use serde::{Deserialize, Serialize};
use std::time::{Duration, Instant};
use systemprompt_identifiers::{
AgentName, AiToolCallId, ClientId, ContextId, JwtToken, McpExecutionId, SessionId, TaskId,
TraceId, UserId,
};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RequestContext {
pub auth: AuthContext,
pub request: RequestMetadata,
pub execution: ExecutionContext,
pub settings: ExecutionSettings,
#[serde(skip)]
pub user: Option<AuthenticatedUser>,
#[serde(skip, default = "Instant::now")]
pub start_time: Instant,
}
impl RequestContext {
pub fn new(
session_id: SessionId,
trace_id: TraceId,
context_id: ContextId,
agent_name: AgentName,
) -> Self {
Self {
auth: AuthContext {
auth_token: JwtToken::new(""),
user_id: UserId::anonymous(),
user_type: UserType::Anon,
},
request: RequestMetadata {
session_id,
timestamp: Instant::now(),
client_id: None,
is_tracked: true,
fingerprint_hash: None,
},
execution: ExecutionContext {
trace_id,
context_id,
task_id: None,
ai_tool_call_id: None,
mcp_execution_id: None,
call_source: None,
agent_name,
tool_model_config: None,
},
settings: ExecutionSettings::default(),
user: None,
start_time: Instant::now(),
}
}
pub fn with_user(mut self, user: AuthenticatedUser) -> Self {
self.auth.user_id = UserId::new(user.id.to_string());
self.user = Some(user);
self
}
pub fn with_user_id(mut self, user_id: UserId) -> Self {
self.auth.user_id = user_id;
self
}
pub fn with_agent_name(mut self, agent_name: AgentName) -> Self {
self.execution.agent_name = agent_name;
self
}
pub fn with_context_id(mut self, context_id: ContextId) -> Self {
self.execution.context_id = context_id;
self
}
pub fn with_task_id(mut self, task_id: TaskId) -> Self {
self.execution.task_id = Some(task_id);
self
}
pub fn with_task(mut self, task_id: TaskId, call_source: CallSource) -> Self {
self.execution.task_id = Some(task_id);
self.execution.call_source = Some(call_source);
self
}
pub fn with_ai_tool_call_id(mut self, ai_tool_call_id: AiToolCallId) -> Self {
self.execution.ai_tool_call_id = Some(ai_tool_call_id);
self
}
pub fn with_mcp_execution_id(mut self, mcp_execution_id: McpExecutionId) -> Self {
self.execution.mcp_execution_id = Some(mcp_execution_id);
self
}
pub fn with_client_id(mut self, client_id: ClientId) -> Self {
self.request.client_id = Some(client_id);
self
}
pub const fn with_user_type(mut self, user_type: UserType) -> Self {
self.auth.user_type = user_type;
self
}
pub fn with_auth_token(mut self, token: impl Into<String>) -> Self {
self.auth.auth_token = JwtToken::new(token.into());
self
}
pub const fn with_call_source(mut self, call_source: CallSource) -> Self {
self.execution.call_source = Some(call_source);
self
}
pub const fn with_budget(mut self, cents: i32) -> Self {
self.settings.max_budget_cents = Some(cents);
self
}
pub const fn with_interaction_mode(mut self, mode: UserInteractionMode) -> Self {
self.settings.user_interaction_mode = Some(mode);
self
}
pub const fn with_tracked(mut self, is_tracked: bool) -> Self {
self.request.is_tracked = is_tracked;
self
}
pub fn with_fingerprint_hash(mut self, hash: impl Into<String>) -> Self {
self.request.fingerprint_hash = Some(hash.into());
self
}
pub fn fingerprint_hash(&self) -> Option<&str> {
self.request.fingerprint_hash.as_deref()
}
pub fn with_tool_model_config(mut self, config: ToolModelConfig) -> Self {
self.execution.tool_model_config = Some(config);
self
}
pub const fn tool_model_config(&self) -> Option<&ToolModelConfig> {
self.execution.tool_model_config.as_ref()
}
pub const fn session_id(&self) -> &SessionId {
&self.request.session_id
}
pub const fn user_id(&self) -> &UserId {
&self.auth.user_id
}
pub const fn trace_id(&self) -> &TraceId {
&self.execution.trace_id
}
pub const fn context_id(&self) -> &ContextId {
&self.execution.context_id
}
pub const fn agent_name(&self) -> &AgentName {
&self.execution.agent_name
}
pub const fn auth_token(&self) -> &JwtToken {
&self.auth.auth_token
}
pub const fn user_type(&self) -> UserType {
self.auth.user_type
}
pub const fn rate_limit_tier(&self) -> RateLimitTier {
self.auth.user_type.rate_tier()
}
pub const fn task_id(&self) -> Option<&TaskId> {
self.execution.task_id.as_ref()
}
pub const fn client_id(&self) -> Option<&ClientId> {
self.request.client_id.as_ref()
}
pub const fn ai_tool_call_id(&self) -> Option<&AiToolCallId> {
self.execution.ai_tool_call_id.as_ref()
}
pub const fn mcp_execution_id(&self) -> Option<&McpExecutionId> {
self.execution.mcp_execution_id.as_ref()
}
pub const fn call_source(&self) -> Option<CallSource> {
self.execution.call_source
}
pub const fn is_authenticated(&self) -> bool {
self.user.is_some()
}
pub fn is_system(&self) -> bool {
self.auth.user_id.is_system() && self.execution.context_id.is_system()
}
pub fn elapsed(&self) -> Duration {
self.start_time.elapsed()
}
pub fn validate_task_execution(&self) -> Result<(), String> {
if self.execution.task_id.is_none() {
return Err("Missing task_id for task execution".to_string());
}
if self.execution.context_id.as_str().is_empty() {
return Err("Missing context_id for task execution".to_string());
}
Ok(())
}
pub fn validate_authenticated(&self) -> Result<(), String> {
if self.auth.auth_token.as_str().is_empty() {
return Err("Missing authentication token".to_string());
}
if self.auth.user_id.is_anonymous() {
return Err("User is not authenticated".to_string());
}
Ok(())
}
}