Skip to main content

systemprompt_cloud/profile_authoring/
local_builder.rs

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