systemprompt_runtime/context/
mod.rs1use std::sync::{Arc, OnceLock};
11
12use tokio::task::JoinHandle;
13
14use systemprompt_agent::repository::A2ARepositories;
15use systemprompt_ai::repository::AiRepositories;
16use systemprompt_analytics::repository::AnalyticsRepositories;
17use systemprompt_analytics::{AnalyticsService, FingerprintRepository, GeoIpReader};
18use systemprompt_content::repository::ContentRepositories;
19use systemprompt_database::{DbPool, ServiceRepository};
20use systemprompt_extension::ExtensionRegistry;
21use systemprompt_files::FileRepository;
22use systemprompt_marketplace::MarketplaceFilter;
23use systemprompt_mcp::repository::McpSessionRepository;
24use systemprompt_mcp::services::registry::RegistryService;
25use systemprompt_models::services::SystemAdmin;
26use systemprompt_models::{AppPaths, Config, ContentConfigRaw, ContentRouting, RouteClassifier};
27use systemprompt_oauth::repository::OAuthRepositories;
28use systemprompt_security::authz::SharedAuthzHook;
29use systemprompt_users::{UserRepository, UserService};
30
31mod context_loaders;
32
33use crate::builder::AppContextBuilder;
34use crate::error::RuntimeResult;
35use crate::registry::ModuleApiRegistry;
36
37#[derive(Clone)]
42pub struct DataPlane {
43 pub database: DbPool,
44 pub analytics_service: Arc<AnalyticsService>,
45 pub fingerprint_repo: Option<Arc<FingerprintRepository>>,
46 pub user_service: Option<Arc<UserService>>,
47 pub a2a_repositories: Arc<A2ARepositories>,
48 pub content_repositories: Arc<ContentRepositories>,
49 pub oauth_repositories: Arc<OAuthRepositories>,
50 pub user_repository: Arc<UserRepository>,
51 pub service_repository: Arc<ServiceRepository>,
52 pub ai_repositories: Arc<AiRepositories>,
53 pub analytics_repositories: Arc<AnalyticsRepositories>,
54 pub file_repository: Arc<FileRepository>,
55 pub mcp_session_repository: Arc<McpSessionRepository>,
56}
57
58#[derive(Clone)]
59pub struct ConfigPlane {
60 pub config: Arc<Config>,
61 pub app_paths: Arc<AppPaths>,
62 pub content_config: Option<Arc<ContentConfigRaw>>,
63 pub route_classifier: Arc<RouteClassifier>,
64}
65
66#[derive(Clone)]
67pub struct Plugins {
68 pub extension_registry: Arc<ExtensionRegistry>,
69 pub api_registry: Arc<ModuleApiRegistry>,
70 pub mcp_registry: RegistryService,
71 pub marketplace_filter: Arc<dyn MarketplaceFilter>,
72}
73
74#[derive(Clone)]
75pub struct Subsystems {
76 pub system_admin: Arc<SystemAdmin>,
77 pub authz_hook: SharedAuthzHook,
78 pub event_bridge: Arc<OnceLock<JoinHandle<()>>>,
79 pub geoip_reader: Option<GeoIpReader>,
80}
81
82#[derive(Clone)]
94pub struct AppContext {
95 pub(crate) data: DataPlane,
96 pub(crate) cfg: ConfigPlane,
97 pub(crate) plugins: Plugins,
98 pub(crate) subsystems: Subsystems,
99}
100
101impl std::fmt::Debug for AppContext {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 f.debug_struct("AppContext")
104 .field("config", &"Config")
105 .field("database", &"DbPool")
106 .field("api_registry", &"ModuleApiRegistry")
107 .field("extension_registry", &self.plugins.extension_registry)
108 .field("geoip_reader", &self.subsystems.geoip_reader.is_some())
109 .field("content_config", &self.cfg.content_config.is_some())
110 .field("route_classifier", &"RouteClassifier")
111 .field("analytics_service", &"AnalyticsService")
112 .field("fingerprint_repo", &self.data.fingerprint_repo.is_some())
113 .field("user_service", &self.data.user_service.is_some())
114 .field("app_paths", &"AppPaths")
115 .field("marketplace_filter", &self.plugins.marketplace_filter)
116 .field(
117 "event_bridge",
118 &self.subsystems.event_bridge.get().is_some(),
119 )
120 .field("system_admin", &self.subsystems.system_admin.username())
121 .field("mcp_registry", &"RegistryService")
122 .field("authz_hook", &"SharedAuthzHook")
123 .finish()
124 }
125}
126
127impl std::fmt::Debug for DataPlane {
128 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129 f.debug_struct("DataPlane")
130 .field("database", &"DbPool")
131 .field("analytics_service", &"AnalyticsService")
132 .field("fingerprint_repo", &self.fingerprint_repo.is_some())
133 .field("user_service", &self.user_service.is_some())
134 .field("a2a_repositories", &"A2ARepositories")
135 .field("content_repositories", &"ContentRepositories")
136 .field("oauth_repositories", &"OAuthRepositories")
137 .field("user_repository", &"UserRepository")
138 .field("service_repository", &"ServiceRepository")
139 .field("ai_repositories", &"AiRepositories")
140 .field("analytics_repositories", &"AnalyticsRepositories")
141 .field("file_repository", &"FileRepository")
142 .field("mcp_session_repository", &"McpSessionRepository")
143 .finish()
144 }
145}
146
147impl std::fmt::Debug for ConfigPlane {
148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149 f.debug_struct("ConfigPlane")
150 .field("config", &"Config")
151 .field("app_paths", &"AppPaths")
152 .field("content_config", &self.content_config.is_some())
153 .field("route_classifier", &"RouteClassifier")
154 .finish()
155 }
156}
157
158impl std::fmt::Debug for Plugins {
159 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160 f.debug_struct("Plugins")
161 .field("extension_registry", &self.extension_registry)
162 .field("api_registry", &"ModuleApiRegistry")
163 .field("mcp_registry", &"RegistryService")
164 .field("marketplace_filter", &self.marketplace_filter)
165 .finish()
166 }
167}
168
169impl std::fmt::Debug for Subsystems {
170 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171 f.debug_struct("Subsystems")
172 .field("system_admin", &self.system_admin.username())
173 .field("authz_hook", &"SharedAuthzHook")
174 .field("event_bridge", &self.event_bridge.get().is_some())
175 .field("geoip_reader", &self.geoip_reader.is_some())
176 .finish()
177 }
178}
179
180impl AppContext {
181 pub async fn new() -> RuntimeResult<Self> {
182 Self::builder().build().await
183 }
184
185 #[must_use]
186 pub fn builder() -> AppContextBuilder {
187 AppContextBuilder::new()
188 }
189
190 #[must_use]
191 pub const fn from_parts(
192 data: DataPlane,
193 cfg: ConfigPlane,
194 plugins: Plugins,
195 subsystems: Subsystems,
196 ) -> Self {
197 Self {
198 data,
199 cfg,
200 plugins,
201 subsystems,
202 }
203 }
204
205 pub fn load_geoip_database(
206 config: &Config,
207 show_warnings: bool,
208 ) -> Result<Option<GeoIpReader>, crate::error::RuntimeError> {
209 context_loaders::load_geoip_database(config, show_warnings)
210 }
211
212 pub fn load_content_config(
213 config: &Config,
214 app_paths: &AppPaths,
215 ) -> Option<Arc<ContentConfigRaw>> {
216 context_loaders::load_content_config(config, app_paths)
217 }
218
219 pub fn config(&self) -> &Config {
220 &self.cfg.config
221 }
222
223 pub fn content_config(&self) -> Option<&ContentConfigRaw> {
224 self.cfg.content_config.as_ref().map(AsRef::as_ref)
225 }
226
227 pub fn content_routing(&self) -> Option<Arc<dyn ContentRouting>> {
228 let concrete = Arc::clone(self.cfg.content_config.as_ref()?);
229 let routing: Arc<dyn ContentRouting> = concrete;
230 Some(routing)
231 }
232
233 pub const fn db_pool(&self) -> &DbPool {
234 &self.data.database
235 }
236
237 pub fn api_registry(&self) -> &ModuleApiRegistry {
238 &self.plugins.api_registry
239 }
240
241 pub fn extension_registry(&self) -> &ExtensionRegistry {
242 &self.plugins.extension_registry
243 }
244
245 pub fn server_address(&self) -> String {
246 format!("{}:{}", self.cfg.config.host, self.cfg.config.port)
247 }
248
249 pub const fn geoip_reader(&self) -> Option<&GeoIpReader> {
250 self.subsystems.geoip_reader.as_ref()
251 }
252
253 pub const fn analytics_service(&self) -> &Arc<AnalyticsService> {
254 &self.data.analytics_service
255 }
256
257 #[must_use]
258 pub fn session_usage(&self) -> systemprompt_traits::DynSessionUsageCounters {
259 Arc::new(self.data.analytics_service.session_repo().clone())
260 }
261
262 pub fn context_materializer(&self) -> systemprompt_traits::DynContextMaterializer {
263 Arc::new(systemprompt_agent::services::ContextProviderService::new(
264 self.data.a2a_repositories.contexts.clone(),
265 ))
266 }
267
268 pub const fn a2a_repositories(&self) -> &Arc<A2ARepositories> {
269 &self.data.a2a_repositories
270 }
271
272 pub const fn content_repositories(&self) -> &Arc<ContentRepositories> {
273 &self.data.content_repositories
274 }
275
276 pub const fn oauth_repositories(&self) -> &Arc<OAuthRepositories> {
277 &self.data.oauth_repositories
278 }
279
280 pub const fn user_repository(&self) -> &Arc<UserRepository> {
281 &self.data.user_repository
282 }
283
284 pub const fn service_repository(&self) -> &Arc<ServiceRepository> {
285 &self.data.service_repository
286 }
287
288 pub const fn ai_repositories(&self) -> &Arc<AiRepositories> {
289 &self.data.ai_repositories
290 }
291
292 pub const fn analytics_repositories(&self) -> &Arc<AnalyticsRepositories> {
293 &self.data.analytics_repositories
294 }
295
296 pub const fn file_repository(&self) -> &Arc<FileRepository> {
297 &self.data.file_repository
298 }
299
300 pub const fn mcp_session_repository(&self) -> &Arc<McpSessionRepository> {
301 &self.data.mcp_session_repository
302 }
303
304 pub const fn route_classifier(&self) -> &Arc<RouteClassifier> {
305 &self.cfg.route_classifier
306 }
307
308 pub fn app_paths(&self) -> &AppPaths {
309 &self.cfg.app_paths
310 }
311
312 pub const fn app_paths_arc(&self) -> &Arc<AppPaths> {
313 &self.cfg.app_paths
314 }
315
316 pub fn marketplace_filter(&self) -> &Arc<dyn MarketplaceFilter> {
317 &self.plugins.marketplace_filter
318 }
319
320 pub const fn event_bridge(&self) -> &Arc<OnceLock<JoinHandle<()>>> {
321 &self.subsystems.event_bridge
322 }
323
324 pub fn system_admin(&self) -> &SystemAdmin {
325 &self.subsystems.system_admin
326 }
327
328 pub const fn mcp_registry(&self) -> &RegistryService {
329 &self.plugins.mcp_registry
330 }
331
332 pub const fn authz_hook(&self) -> &SharedAuthzHook {
333 &self.subsystems.authz_hook
334 }
335}