systemprompt_loader/
services_bootstrap.rs1use std::future::Future;
26use std::path::Path;
27use std::pin::Pin;
28use std::sync::OnceLock;
29
30use systemprompt_manifest::services::{
31 DiscoveryReport, GatewayConfig, ProviderRegistry, ServicesConfig,
32};
33
34use crate::config_loader::ConfigLoader;
35use crate::error::{ConfigLoadError, ConfigLoadResult};
36
37static SERVICES: OnceLock<ServicesConfig> = OnceLock::new();
38pub type DiscoveryFuture<'a> = Pin<Box<dyn Future<Output = DiscoveryReport> + Send + 'a>>;
39
40static DISCOVERY: OnceLock<DiscoveryReport> = OnceLock::new();
41
42#[derive(Debug, Clone, Copy)]
43pub struct ServicesBootstrap;
44
45impl ServicesBootstrap {
46 pub fn init() -> ConfigLoadResult<&'static ServicesConfig> {
47 if SERVICES.get().is_some() {
48 return Err(ConfigLoadError::AlreadyInitialized);
49 }
50 let services = ConfigLoader::load()?;
51 Self::install(services)
52 }
53
54 pub fn init_from_path(path: &Path) -> ConfigLoadResult<&'static ServicesConfig> {
55 if SERVICES.get().is_some() {
56 return Err(ConfigLoadError::AlreadyInitialized);
57 }
58 let services = ConfigLoader::load_from_path(path)?;
59 Self::install(services)
60 }
61
62 pub async fn try_init_with_discovery<F>(augment: F) -> ConfigLoadResult<&'static ServicesConfig>
66 where
67 F: for<'a> FnOnce(&'a mut ProviderRegistry) -> DiscoveryFuture<'a>,
68 {
69 if let Some(services) = SERVICES.get() {
70 return Ok(services);
71 }
72 let mut services = ConfigLoader::load()?;
73 let report = augment(&mut services.providers).await;
74 services.validate()?;
75 let installed = Self::install(services)?;
76 if DISCOVERY.set(report).is_err() {
77 tracing::warn!(
78 "catalog discovery report already recorded for this process; keeping the first"
79 );
80 }
81 Ok(installed)
82 }
83
84 #[must_use]
85 pub fn discovery_report() -> Option<&'static DiscoveryReport> {
86 DISCOVERY.get()
87 }
88
89 pub fn try_init() -> ConfigLoadResult<&'static ServicesConfig> {
90 if let Some(services) = SERVICES.get() {
91 return Ok(services);
92 }
93 Self::init()
94 }
95
96 pub fn get() -> ConfigLoadResult<&'static ServicesConfig> {
97 SERVICES.get().ok_or(ConfigLoadError::NotInitialized)
98 }
99
100 pub fn providers() -> ConfigLoadResult<&'static ProviderRegistry> {
101 Self::get().map(|s| &s.providers)
102 }
103
104 pub fn gateway() -> ConfigLoadResult<Option<&'static GatewayConfig>> {
105 Self::get().map(ServicesConfig::gateway_config)
106 }
107
108 #[must_use]
109 pub fn is_initialized() -> bool {
110 SERVICES.get().is_some()
111 }
112
113 fn install(services: ServicesConfig) -> ConfigLoadResult<&'static ServicesConfig> {
114 SERVICES
115 .set(services)
116 .map_err(|_already| ConfigLoadError::AlreadyInitialized)?;
117 SERVICES.get().ok_or(ConfigLoadError::NotInitialized)
118 }
119}