Skip to main content

systemprompt_runtime/
context.rs

1//! [`AppContext`] — the application-wide runtime container.
2//!
3//! Holds shared handles (config, database pool, extension registry,
4//! analytics, route classifier, etc.) cloned cheaply via [`Arc`].
5//! Constructed via [`crate::AppContextBuilder`] or [`AppContext::new`].
6
7use std::sync::Arc;
8
9use systemprompt_analytics::{AnalyticsService, FingerprintRepository, GeoIpReader};
10use systemprompt_database::DbPool;
11use systemprompt_extension::ExtensionRegistry;
12use systemprompt_marketplace::MarketplaceFilter;
13use systemprompt_models::{AppPaths, Config, ContentConfigRaw, ContentRouting, RouteClassifier};
14use systemprompt_users::UserService;
15
16use crate::builder::AppContextBuilder;
17use crate::context_loaders;
18use crate::error::RuntimeResult;
19use crate::registry::ModuleApiRegistry;
20
21#[derive(Clone)]
22pub struct AppContext {
23    pub(crate) config: Arc<Config>,
24    pub(crate) database: DbPool,
25    pub(crate) api_registry: Arc<ModuleApiRegistry>,
26    pub(crate) extension_registry: Arc<ExtensionRegistry>,
27    pub(crate) geoip_reader: Option<GeoIpReader>,
28    pub(crate) content_config: Option<Arc<ContentConfigRaw>>,
29    pub(crate) route_classifier: Arc<RouteClassifier>,
30    pub(crate) analytics_service: Arc<AnalyticsService>,
31    pub(crate) fingerprint_repo: Option<Arc<FingerprintRepository>>,
32    pub(crate) user_service: Option<Arc<UserService>>,
33    pub(crate) app_paths: Arc<AppPaths>,
34    pub(crate) marketplace_filter: Arc<dyn MarketplaceFilter>,
35}
36
37impl std::fmt::Debug for AppContext {
38    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39        f.debug_struct("AppContext")
40            .field("config", &"Config")
41            .field("database", &"DbPool")
42            .field("api_registry", &"ModuleApiRegistry")
43            .field("extension_registry", &self.extension_registry)
44            .field("geoip_reader", &self.geoip_reader.is_some())
45            .field("content_config", &self.content_config.is_some())
46            .field("route_classifier", &"RouteClassifier")
47            .field("analytics_service", &"AnalyticsService")
48            .field("fingerprint_repo", &self.fingerprint_repo.is_some())
49            .field("user_service", &self.user_service.is_some())
50            .field("app_paths", &"AppPaths")
51            .field("marketplace_filter", &self.marketplace_filter)
52            .finish()
53    }
54}
55
56#[derive(Debug)]
57pub struct AppContextParts {
58    pub config: Arc<Config>,
59    pub database: DbPool,
60    pub api_registry: Arc<ModuleApiRegistry>,
61    pub extension_registry: Arc<ExtensionRegistry>,
62    pub geoip_reader: Option<GeoIpReader>,
63    pub content_config: Option<Arc<ContentConfigRaw>>,
64    pub route_classifier: Arc<RouteClassifier>,
65    pub analytics_service: Arc<AnalyticsService>,
66    pub fingerprint_repo: Option<Arc<FingerprintRepository>>,
67    pub user_service: Option<Arc<UserService>>,
68    pub app_paths: Arc<AppPaths>,
69    pub marketplace_filter: Arc<dyn MarketplaceFilter>,
70}
71
72impl AppContext {
73    pub async fn new() -> RuntimeResult<Self> {
74        Self::builder().build().await
75    }
76
77    #[must_use]
78    pub fn builder() -> AppContextBuilder {
79        AppContextBuilder::new()
80    }
81
82    pub fn from_parts(parts: AppContextParts) -> Self {
83        Self {
84            config: parts.config,
85            database: parts.database,
86            api_registry: parts.api_registry,
87            extension_registry: parts.extension_registry,
88            geoip_reader: parts.geoip_reader,
89            content_config: parts.content_config,
90            route_classifier: parts.route_classifier,
91            analytics_service: parts.analytics_service,
92            fingerprint_repo: parts.fingerprint_repo,
93            user_service: parts.user_service,
94            app_paths: parts.app_paths,
95            marketplace_filter: parts.marketplace_filter,
96        }
97    }
98
99    pub fn load_geoip_database(config: &Config, show_warnings: bool) -> Option<GeoIpReader> {
100        context_loaders::load_geoip_database(config, show_warnings)
101    }
102
103    pub fn load_content_config(
104        config: &Config,
105        app_paths: &AppPaths,
106    ) -> Option<Arc<ContentConfigRaw>> {
107        context_loaders::load_content_config(config, app_paths)
108    }
109
110    pub fn config(&self) -> &Config {
111        &self.config
112    }
113
114    pub fn content_config(&self) -> Option<&ContentConfigRaw> {
115        self.content_config.as_ref().map(AsRef::as_ref)
116    }
117
118    pub fn content_routing(&self) -> Option<Arc<dyn ContentRouting>> {
119        let concrete = Arc::clone(self.content_config.as_ref()?);
120        let routing: Arc<dyn ContentRouting> = concrete;
121        Some(routing)
122    }
123
124    pub const fn db_pool(&self) -> &DbPool {
125        &self.database
126    }
127
128    pub const fn database(&self) -> &DbPool {
129        &self.database
130    }
131
132    pub fn api_registry(&self) -> &ModuleApiRegistry {
133        &self.api_registry
134    }
135
136    pub fn extension_registry(&self) -> &ExtensionRegistry {
137        &self.extension_registry
138    }
139
140    pub fn server_address(&self) -> String {
141        format!("{}:{}", self.config.host, self.config.port)
142    }
143
144    pub fn get_provided_audiences() -> Vec<String> {
145        vec!["a2a".to_string(), "api".to_string(), "mcp".to_string()]
146    }
147
148    pub fn get_valid_audiences(_module_name: &str) -> Vec<String> {
149        Self::get_provided_audiences()
150    }
151
152    pub fn get_server_audiences(_server_name: &str, _port: u16) -> Vec<String> {
153        Self::get_provided_audiences()
154    }
155
156    pub const fn geoip_reader(&self) -> Option<&GeoIpReader> {
157        self.geoip_reader.as_ref()
158    }
159
160    pub const fn analytics_service(&self) -> &Arc<AnalyticsService> {
161        &self.analytics_service
162    }
163
164    pub const fn route_classifier(&self) -> &Arc<RouteClassifier> {
165        &self.route_classifier
166    }
167
168    pub fn app_paths(&self) -> &AppPaths {
169        &self.app_paths
170    }
171
172    pub const fn app_paths_arc(&self) -> &Arc<AppPaths> {
173        &self.app_paths
174    }
175
176    pub fn marketplace_filter(&self) -> &Arc<dyn MarketplaceFilter> {
177        &self.marketplace_filter
178    }
179}