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)]
40pub struct ConfigPlane {
41 pub config: Arc<Config>,
42 pub app_paths: Arc<AppPaths>,
43 pub content_config: Option<Arc<ContentConfigRaw>>,
44 pub route_classifier: Arc<RouteClassifier>,
45}
46
47#[derive(Clone)]
48pub struct Plugins {
49 pub extension_registry: Arc<ExtensionRegistry>,
50 pub api_registry: Arc<ModuleApiRegistry>,
51 pub mcp_registry: RegistryService,
52 pub marketplace_filter: Arc<dyn MarketplaceFilter>,
53}
54
55#[derive(Clone)]
56pub struct Subsystems {
57 pub system_admin: Arc<SystemAdmin>,
58 pub authz_hook: SharedAuthzHook,
59 pub event_bridge: Arc<OnceLock<JoinHandle<()>>>,
60 pub geoip_reader: Option<GeoIpReader>,
61}
62
63#[derive(Clone)]
75pub struct AppContext {
76 pub(crate) data: DataPlane,
77 pub(crate) cfg: ConfigPlane,
78 pub(crate) plugins: Plugins,
79 pub(crate) subsystems: Subsystems,
80}
81
82impl std::fmt::Debug for AppContext {
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 f.debug_struct("AppContext")
85 .field("config", &"Config")
86 .field("database", &"DbPool")
87 .field("api_registry", &"ModuleApiRegistry")
88 .field("extension_registry", &self.plugins.extension_registry)
89 .field("geoip_reader", &self.subsystems.geoip_reader.is_some())
90 .field("content_config", &self.cfg.content_config.is_some())
91 .field("route_classifier", &"RouteClassifier")
92 .field("analytics_service", &"AnalyticsService")
93 .field("fingerprint_repo", &self.data.fingerprint_repo.is_some())
94 .field("user_service", &self.data.user_service.is_some())
95 .field("app_paths", &"AppPaths")
96 .field("marketplace_filter", &self.plugins.marketplace_filter)
97 .field(
98 "event_bridge",
99 &self.subsystems.event_bridge.get().is_some(),
100 )
101 .field("system_admin", &self.subsystems.system_admin.username())
102 .field("mcp_registry", &"RegistryService")
103 .field("authz_hook", &"SharedAuthzHook")
104 .finish()
105 }
106}
107
108impl std::fmt::Debug for DataPlane {
109 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110 f.debug_struct("DataPlane")
111 .field("database", &"DbPool")
112 .field("analytics_service", &"AnalyticsService")
113 .field("fingerprint_repo", &self.fingerprint_repo.is_some())
114 .field("user_service", &self.user_service.is_some())
115 .finish()
116 }
117}
118
119impl std::fmt::Debug for ConfigPlane {
120 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121 f.debug_struct("ConfigPlane")
122 .field("config", &"Config")
123 .field("app_paths", &"AppPaths")
124 .field("content_config", &self.content_config.is_some())
125 .field("route_classifier", &"RouteClassifier")
126 .finish()
127 }
128}
129
130impl std::fmt::Debug for Plugins {
131 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132 f.debug_struct("Plugins")
133 .field("extension_registry", &self.extension_registry)
134 .field("api_registry", &"ModuleApiRegistry")
135 .field("mcp_registry", &"RegistryService")
136 .field("marketplace_filter", &self.marketplace_filter)
137 .finish()
138 }
139}
140
141impl std::fmt::Debug for Subsystems {
142 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143 f.debug_struct("Subsystems")
144 .field("system_admin", &self.system_admin.username())
145 .field("authz_hook", &"SharedAuthzHook")
146 .field("event_bridge", &self.event_bridge.get().is_some())
147 .field("geoip_reader", &self.geoip_reader.is_some())
148 .finish()
149 }
150}
151
152impl AppContext {
153 pub async fn new() -> RuntimeResult<Self> {
157 Self::builder().build().await
158 }
159
160 #[must_use]
161 pub fn builder() -> AppContextBuilder {
162 AppContextBuilder::new()
163 }
164
165 #[must_use]
169 pub const fn from_parts(
170 data: DataPlane,
171 cfg: ConfigPlane,
172 plugins: Plugins,
173 subsystems: Subsystems,
174 ) -> Self {
175 Self {
176 data,
177 cfg,
178 plugins,
179 subsystems,
180 }
181 }
182
183 pub fn load_geoip_database(config: &Config, show_warnings: bool) -> Option<GeoIpReader> {
184 context_loaders::load_geoip_database(config, show_warnings)
185 }
186
187 pub fn load_content_config(
188 config: &Config,
189 app_paths: &AppPaths,
190 ) -> Option<Arc<ContentConfigRaw>> {
191 context_loaders::load_content_config(config, app_paths)
192 }
193
194 pub fn config(&self) -> &Config {
195 &self.cfg.config
196 }
197
198 pub fn content_config(&self) -> Option<&ContentConfigRaw> {
199 self.cfg.content_config.as_ref().map(AsRef::as_ref)
200 }
201
202 pub fn content_routing(&self) -> Option<Arc<dyn ContentRouting>> {
203 let concrete = Arc::clone(self.cfg.content_config.as_ref()?);
204 let routing: Arc<dyn ContentRouting> = concrete;
205 Some(routing)
206 }
207
208 pub const fn db_pool(&self) -> &DbPool {
209 &self.data.database
210 }
211
212 pub fn api_registry(&self) -> &ModuleApiRegistry {
213 &self.plugins.api_registry
214 }
215
216 pub fn extension_registry(&self) -> &ExtensionRegistry {
217 &self.plugins.extension_registry
218 }
219
220 pub fn server_address(&self) -> String {
221 format!("{}:{}", self.cfg.config.host, self.cfg.config.port)
222 }
223
224 pub const fn geoip_reader(&self) -> Option<&GeoIpReader> {
225 self.subsystems.geoip_reader.as_ref()
226 }
227
228 pub const fn analytics_service(&self) -> &Arc<AnalyticsService> {
229 &self.data.analytics_service
230 }
231
232 pub const fn route_classifier(&self) -> &Arc<RouteClassifier> {
233 &self.cfg.route_classifier
234 }
235
236 pub fn app_paths(&self) -> &AppPaths {
237 &self.cfg.app_paths
238 }
239
240 pub const fn app_paths_arc(&self) -> &Arc<AppPaths> {
241 &self.cfg.app_paths
242 }
243
244 pub fn marketplace_filter(&self) -> &Arc<dyn MarketplaceFilter> {
245 &self.plugins.marketplace_filter
246 }
247
248 pub const fn event_bridge(&self) -> &Arc<OnceLock<JoinHandle<()>>> {
249 &self.subsystems.event_bridge
250 }
251
252 pub fn system_admin(&self) -> &SystemAdmin {
253 &self.subsystems.system_admin
254 }
255
256 pub const fn mcp_registry(&self) -> &RegistryService {
257 &self.plugins.mcp_registry
258 }
259
260 pub const fn authz_hook(&self) -> &SharedAuthzHook {
261 &self.subsystems.authz_hook
262 }
263}