systemprompt_runtime/context/
mod.rs1use std::sync::{Arc, OnceLock};
8
9use tokio::task::JoinHandle;
10
11use systemprompt_analytics::{AnalyticsService, FingerprintRepository, GeoIpReader};
12use systemprompt_database::DbPool;
13use systemprompt_extension::ExtensionRegistry;
14use systemprompt_marketplace::MarketplaceFilter;
15use systemprompt_mcp::services::registry::RegistryService;
16use systemprompt_models::services::SystemAdmin;
17use systemprompt_models::{AppPaths, Config, ContentConfigRaw, ContentRouting, RouteClassifier};
18use systemprompt_security::authz::SharedAuthzHook;
19use systemprompt_users::UserService;
20
21mod context_loaders;
22
23use crate::builder::AppContextBuilder;
24use crate::error::RuntimeResult;
25use crate::registry::ModuleApiRegistry;
26
27#[derive(Clone)]
32pub struct DataPlane {
33 pub database: DbPool,
34 pub analytics_service: Arc<AnalyticsService>,
35 pub fingerprint_repo: Option<Arc<FingerprintRepository>>,
36 pub user_service: Option<Arc<UserService>>,
37}
38
39#[derive(Clone)]
43pub struct ConfigPlane {
44 pub config: Arc<Config>,
45 pub app_paths: Arc<AppPaths>,
46 pub content_config: Option<Arc<ContentConfigRaw>>,
47 pub route_classifier: Arc<RouteClassifier>,
48}
49
50#[derive(Clone)]
52pub struct Plugins {
53 pub extension_registry: Arc<ExtensionRegistry>,
54 pub api_registry: Arc<ModuleApiRegistry>,
55 pub mcp_registry: RegistryService,
56 pub marketplace_filter: Arc<dyn MarketplaceFilter>,
57}
58
59#[derive(Clone)]
62pub struct Subsystems {
63 pub system_admin: Arc<SystemAdmin>,
64 pub authz_hook: SharedAuthzHook,
65 pub event_bridge: Arc<OnceLock<JoinHandle<()>>>,
66 pub geoip_reader: Option<GeoIpReader>,
67}
68
69#[derive(Clone)]
81pub struct AppContext {
82 pub(crate) data: DataPlane,
83 pub(crate) cfg: ConfigPlane,
84 pub(crate) plugins: Plugins,
85 pub(crate) subsystems: Subsystems,
86}
87
88impl std::fmt::Debug for AppContext {
89 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90 f.debug_struct("AppContext")
91 .field("config", &"Config")
92 .field("database", &"DbPool")
93 .field("api_registry", &"ModuleApiRegistry")
94 .field("extension_registry", &self.plugins.extension_registry)
95 .field("geoip_reader", &self.subsystems.geoip_reader.is_some())
96 .field("content_config", &self.cfg.content_config.is_some())
97 .field("route_classifier", &"RouteClassifier")
98 .field("analytics_service", &"AnalyticsService")
99 .field("fingerprint_repo", &self.data.fingerprint_repo.is_some())
100 .field("user_service", &self.data.user_service.is_some())
101 .field("app_paths", &"AppPaths")
102 .field("marketplace_filter", &self.plugins.marketplace_filter)
103 .field(
104 "event_bridge",
105 &self.subsystems.event_bridge.get().is_some(),
106 )
107 .field("system_admin", &self.subsystems.system_admin.username())
108 .field("mcp_registry", &"RegistryService")
109 .field("authz_hook", &"SharedAuthzHook")
110 .finish()
111 }
112}
113
114impl std::fmt::Debug for DataPlane {
115 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116 f.debug_struct("DataPlane")
117 .field("database", &"DbPool")
118 .field("analytics_service", &"AnalyticsService")
119 .field("fingerprint_repo", &self.fingerprint_repo.is_some())
120 .field("user_service", &self.user_service.is_some())
121 .finish()
122 }
123}
124
125impl std::fmt::Debug for ConfigPlane {
126 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127 f.debug_struct("ConfigPlane")
128 .field("config", &"Config")
129 .field("app_paths", &"AppPaths")
130 .field("content_config", &self.content_config.is_some())
131 .field("route_classifier", &"RouteClassifier")
132 .finish()
133 }
134}
135
136impl std::fmt::Debug for Plugins {
137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138 f.debug_struct("Plugins")
139 .field("extension_registry", &self.extension_registry)
140 .field("api_registry", &"ModuleApiRegistry")
141 .field("mcp_registry", &"RegistryService")
142 .field("marketplace_filter", &self.marketplace_filter)
143 .finish()
144 }
145}
146
147impl std::fmt::Debug for Subsystems {
148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149 f.debug_struct("Subsystems")
150 .field("system_admin", &self.system_admin.username())
151 .field("authz_hook", &"SharedAuthzHook")
152 .field("event_bridge", &self.event_bridge.get().is_some())
153 .field("geoip_reader", &self.geoip_reader.is_some())
154 .finish()
155 }
156}
157
158impl AppContext {
159 pub async fn new() -> RuntimeResult<Self> {
163 Self::builder().build().await
164 }
165
166 #[must_use]
167 pub fn builder() -> AppContextBuilder {
168 AppContextBuilder::new()
169 }
170
171 #[must_use]
175 pub const fn from_parts(
176 data: DataPlane,
177 cfg: ConfigPlane,
178 plugins: Plugins,
179 subsystems: Subsystems,
180 ) -> Self {
181 Self {
182 data,
183 cfg,
184 plugins,
185 subsystems,
186 }
187 }
188
189 pub fn load_geoip_database(config: &Config, show_warnings: bool) -> Option<GeoIpReader> {
190 context_loaders::load_geoip_database(config, show_warnings)
191 }
192
193 pub fn load_content_config(
194 config: &Config,
195 app_paths: &AppPaths,
196 ) -> Option<Arc<ContentConfigRaw>> {
197 context_loaders::load_content_config(config, app_paths)
198 }
199
200 pub fn config(&self) -> &Config {
201 &self.cfg.config
202 }
203
204 pub fn content_config(&self) -> Option<&ContentConfigRaw> {
205 self.cfg.content_config.as_ref().map(AsRef::as_ref)
206 }
207
208 pub fn content_routing(&self) -> Option<Arc<dyn ContentRouting>> {
209 let concrete = Arc::clone(self.cfg.content_config.as_ref()?);
210 let routing: Arc<dyn ContentRouting> = concrete;
211 Some(routing)
212 }
213
214 pub const fn db_pool(&self) -> &DbPool {
215 &self.data.database
216 }
217
218 pub fn api_registry(&self) -> &ModuleApiRegistry {
219 &self.plugins.api_registry
220 }
221
222 pub fn extension_registry(&self) -> &ExtensionRegistry {
223 &self.plugins.extension_registry
224 }
225
226 pub fn server_address(&self) -> String {
227 format!("{}:{}", self.cfg.config.host, self.cfg.config.port)
228 }
229
230 pub const fn geoip_reader(&self) -> Option<&GeoIpReader> {
231 self.subsystems.geoip_reader.as_ref()
232 }
233
234 pub const fn analytics_service(&self) -> &Arc<AnalyticsService> {
235 &self.data.analytics_service
236 }
237
238 pub const fn route_classifier(&self) -> &Arc<RouteClassifier> {
239 &self.cfg.route_classifier
240 }
241
242 pub fn app_paths(&self) -> &AppPaths {
243 &self.cfg.app_paths
244 }
245
246 pub const fn app_paths_arc(&self) -> &Arc<AppPaths> {
247 &self.cfg.app_paths
248 }
249
250 pub fn marketplace_filter(&self) -> &Arc<dyn MarketplaceFilter> {
251 &self.plugins.marketplace_filter
252 }
253
254 pub const fn event_bridge(&self) -> &Arc<OnceLock<JoinHandle<()>>> {
255 &self.subsystems.event_bridge
256 }
257
258 pub fn system_admin(&self) -> &SystemAdmin {
259 &self.subsystems.system_admin
260 }
261
262 pub const fn mcp_registry(&self) -> &RegistryService {
263 &self.plugins.mcp_registry
264 }
265
266 pub const fn authz_hook(&self) -> &SharedAuthzHook {
267 &self.subsystems.authz_hook
268 }
269}