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