Skip to main content

systemprompt_cloud/profile_authoring/
local_builder.rs

1//! Builder for the local-development [`Profile`].
2
3use std::path::Path;
4
5use systemprompt_identifiers::TenantId;
6use systemprompt_loader::ExtensionLoader;
7use systemprompt_models::profile::{
8    ProviderRegistry, SecretsConfig, SecretsSource, SecretsValidationMode,
9};
10use systemprompt_models::services::SystemAdminConfig;
11use systemprompt_models::{
12    CloudConfig, CloudValidationMode, ContentNegotiationConfig, ExtensionsConfig, PathsConfig,
13    Profile, ProfileDatabaseConfig, ProfileType, RateLimitsConfig, SecurityHeadersConfig,
14    ServerConfig, SiteConfig,
15};
16
17use super::{generate_display_name, local_runtime_config, security_config, webhook_governance};
18use crate::constants::profile as consts;
19use crate::paths::ProjectContext;
20
21#[derive(Debug)]
22pub struct LocalProfileBuilder {
23    name: String,
24    tenant_id: Option<TenantId>,
25    secrets_path: String,
26    services_path: String,
27}
28
29impl LocalProfileBuilder {
30    pub fn new(
31        name: impl Into<String>,
32        secrets_path: impl AsRef<Path>,
33        services_path: impl AsRef<Path>,
34    ) -> Self {
35        Self {
36            name: name.into(),
37            tenant_id: None,
38            secrets_path: secrets_path.as_ref().to_string_lossy().to_string(),
39            services_path: services_path.as_ref().to_string_lossy().to_string(),
40        }
41    }
42
43    #[must_use]
44    pub fn with_tenant_id(mut self, tenant_id: TenantId) -> Self {
45        self.tenant_id = Some(tenant_id);
46        self
47    }
48
49    #[must_use]
50    pub fn build(self) -> Profile {
51        let ctx = ProjectContext::discover();
52        let root = ctx.root();
53        let system_path = root.to_string_lossy().to_string();
54        let display_name = generate_display_name(&self.name);
55        let local_url = format!("http://localhost:{}", consts::DEFAULT_PORT);
56        let internal_url = local_url.clone();
57
58        Profile {
59            name: self.name,
60            display_name,
61            target: ProfileType::Local,
62            site: SiteConfig {
63                name: "systemprompt.io".to_owned(),
64                github_link: None,
65            },
66            database: ProfileDatabaseConfig {
67                db_type: consts::DEFAULT_DB_TYPE.to_owned(),
68                external_db_access: false,
69                pool: None,
70            },
71            server: ServerConfig {
72                host: consts::LOCAL_HOST.to_owned(),
73                port: consts::DEFAULT_PORT,
74                api_server_url: local_url.clone(),
75                api_internal_url: local_url.clone(),
76                api_external_url: local_url.clone(),
77                use_https: false,
78                cors_allowed_origins: vec![local_url, "http://localhost:5173".to_owned()],
79                content_negotiation: ContentNegotiationConfig::default(),
80                security_headers: SecurityHeadersConfig::default(),
81                instance_id: None,
82                max_concurrent_streams: systemprompt_models::config::DEFAULT_MAX_CONCURRENT_STREAMS,
83                trusted_proxies: Vec::new(),
84            },
85            paths: PathsConfig {
86                system: system_path,
87                services: self.services_path,
88                bin: ExtensionLoader::resolve_bin_directory(root, None)
89                    .to_string_lossy()
90                    .to_string(),
91                storage: Some(ctx.storage_dir().to_string_lossy().to_string()),
92                geoip_database: None,
93                web_path: None,
94            },
95            security: security_config(consts::LOCAL_ISSUER, Vec::new()),
96            rate_limits: RateLimitsConfig {
97                disabled: true,
98                ..Default::default()
99            },
100            runtime: local_runtime_config(),
101            cloud: Some(CloudConfig {
102                tenant_id: self.tenant_id,
103                validation: CloudValidationMode::Warn,
104            }),
105            secrets: Some(SecretsConfig {
106                secrets_path: self.secrets_path,
107                validation: SecretsValidationMode::Warn,
108                source: SecretsSource::File,
109            }),
110            extensions: ExtensionsConfig::default(),
111            providers: ProviderRegistry::default(),
112            gateway: None,
113            governance: Some(webhook_governance(&internal_url)),
114            system_admin: SystemAdminConfig {
115                username: "admin".to_owned(),
116            },
117        }
118    }
119}