systemprompt_runtime/builder/
mod.rs1mod assembly;
12mod core_layer;
13
14use std::sync::{Arc, OnceLock};
15
16use systemprompt_database::MigrationConfig;
17use systemprompt_extension::ExtensionRegistry;
18use systemprompt_marketplace::MarketplaceFilter;
19use systemprompt_mcp::services::registry::RegistryService;
20use systemprompt_security::authz::{AuthzDecisionHook, SharedAuthzHook};
21use systemprompt_users::UserService;
22
23use crate::context::{AppContext, ConfigPlane, DataPlane, Plugins, Subsystems};
24use crate::error::RuntimeResult;
25use crate::registry::ModuleApiRegistry;
26use core_layer::{CoreLayer, init_core, init_extensions};
27
28#[derive(Default)]
37pub struct AppContextBuilder {
38 extension_registry: Option<ExtensionRegistry>,
39 show_startup_warnings: bool,
40 marketplace_filter: Option<Arc<dyn MarketplaceFilter>>,
41 authz_hook: Option<SharedAuthzHook>,
42 install_schemas: bool,
43 migration_config: MigrationConfig,
44}
45
46impl std::fmt::Debug for AppContextBuilder {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 f.debug_struct("AppContextBuilder")
49 .field("extension_registry", &self.extension_registry.is_some())
50 .field("show_startup_warnings", &self.show_startup_warnings)
51 .field("marketplace_filter", &self.marketplace_filter.is_some())
52 .field("authz_hook", &self.authz_hook.is_some())
53 .field("install_schemas", &self.install_schemas)
54 .field("migration_config", &self.migration_config)
55 .finish()
56 }
57}
58
59impl AppContextBuilder {
60 #[must_use]
61 pub fn new() -> Self {
62 Self::default()
63 }
64
65 #[must_use]
68 pub fn with_extensions(mut self, registry: ExtensionRegistry) -> Self {
69 self.extension_registry = Some(registry);
70 self
71 }
72
73 #[must_use]
74 pub const fn with_startup_warnings(mut self, show: bool) -> Self {
75 self.show_startup_warnings = show;
76 self
77 }
78
79 #[must_use]
83 pub fn with_marketplace_filter(mut self, filter: Arc<dyn MarketplaceFilter>) -> Self {
84 self.marketplace_filter = Some(filter);
85 self
86 }
87
88 #[must_use]
92 pub const fn with_migrations(mut self, install: bool) -> Self {
93 self.install_schemas = install;
94 self
95 }
96
97 #[must_use]
101 pub fn with_authz_hook<H>(mut self, hook: H) -> Self
102 where
103 H: AuthzDecisionHook + 'static,
104 {
105 self.authz_hook = Some(Arc::new(hook));
106 self
107 }
108
109 #[must_use]
110 pub fn with_shared_authz_hook(mut self, hook: SharedAuthzHook) -> Self {
111 self.authz_hook = Some(hook);
112 self
113 }
114
115 #[must_use]
116 pub const fn with_migration_config(mut self, config: MigrationConfig) -> Self {
117 self.migration_config = config;
118 self
119 }
120
121 pub async fn build(self) -> RuntimeResult<AppContext> {
122 let CoreLayer {
123 config,
124 app_paths,
125 database,
126 authz_hook,
127 } = init_core(self.authz_hook).await?;
128
129 let api_registry = Arc::new(ModuleApiRegistry::new());
130 let extension_registry = init_extensions(
131 self.extension_registry,
132 self.install_schemas,
133 self.migration_config,
134 &database,
135 )
136 .await?;
137
138 let assembly::ContentAnalytics {
139 geoip_reader,
140 content_config,
141 route_classifier,
142 analytics_service,
143 fingerprint_repo,
144 } = assembly::assemble_content_analytics(
145 &config,
146 &app_paths,
147 &database,
148 self.show_startup_warnings,
149 )?;
150
151 let user_service = Arc::new(UserService::new(&database)?);
155
156 let system_admin =
157 assembly::resolve_and_install_system_admin(&config, &user_service).await?;
158 let mcp_registry = RegistryService::new(system_admin.id().clone());
159
160 let marketplace_filter = self
161 .marketplace_filter
162 .unwrap_or_else(|| assembly::build_marketplace_filter(&database));
163
164 let event_bridge = Arc::new(OnceLock::new());
165
166 Ok(AppContext::from_parts(
167 DataPlane {
168 database,
169 analytics_service,
170 fingerprint_repo,
171 user_service: Some(user_service),
172 },
173 ConfigPlane {
174 config,
175 app_paths,
176 content_config,
177 route_classifier,
178 },
179 Plugins {
180 extension_registry,
181 api_registry,
182 mcp_registry,
183 marketplace_filter,
184 },
185 Subsystems {
186 system_admin,
187 authz_hook,
188 event_bridge,
189 geoip_reader,
190 },
191 ))
192 }
193}