systemprompt-models 0.1.22

Shared data models and types for systemprompt.io OS
Documentation
//! Request context for execution tracking.

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(())
    }
}