Skip to main content

systemprompt_models/profile/gateway/config/
mod.rs

1//! Gateway configuration: on-disk spec and resolved runtime form.
2//!
3//! [`GatewayConfigSpec`] is the serde shape accepted under `gateway:` in a
4//! profile; [`GatewayConfig`] is its runtime projection. Routes carry no
5//! embedded provider catalog — every route resolves its provider against
6//! `profile.providers` (`ProviderRegistry`) at use time.
7//!
8//! Copyright (c) systemprompt.io — Business Source License 1.1.
9//! See <https://systemprompt.io> for licensing details.
10
11mod runtime;
12mod validate;
13
14use serde::{Deserialize, Serialize};
15use systemprompt_identifiers::ProviderId;
16
17use crate::profile::gateway::override_rule::SystemPromptRule;
18use crate::profile::gateway::route::GatewayRoute;
19
20pub use runtime::GatewayConfig;
21
22pub(crate) const DEFAULT_ROUTE_PATTERN: &str = "*";
23
24#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
25#[serde(deny_unknown_fields)]
26pub struct GatewayConfigSpec {
27    #[serde(default)]
28    pub enabled: bool,
29    #[serde(default)]
30    pub routes: Vec<GatewayRoute>,
31    #[serde(default, skip_serializing_if = "Option::is_none")]
32    pub default_provider: Option<ProviderId>,
33    #[serde(default)]
34    pub allow_unlisted_models: bool,
35    #[serde(default = "default_auth_scheme")]
36    pub auth_scheme: String,
37    #[serde(default = "default_inference_path_prefix")]
38    pub inference_path_prefix: String,
39    #[serde(default, skip_serializing_if = "Vec::is_empty")]
40    pub system_prompt_overrides: Vec<SystemPromptRule>,
41    #[serde(default, skip_serializing_if = "Option::is_none")]
42    pub bridge_releases: Option<BridgeReleasesSpec>,
43}
44
45/// Release feed for the desktop bridge self-updater.
46///
47/// The bridge cannot reach these assets itself — the repository is private —
48/// so the gateway resolves and proxies them. Keeping the resolution here is
49/// also what makes staged rollouts a config change rather than a client
50/// release.
51#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
52#[serde(deny_unknown_fields)]
53pub struct BridgeReleasesSpec {
54    pub repo: String,
55    #[serde(default, skip_serializing_if = "Option::is_none")]
56    pub token_env: Option<String>,
57    #[serde(default = "default_tag_prefix")]
58    pub tag_prefix: String,
59    #[serde(default, skip_serializing_if = "Option::is_none")]
60    pub pinned_version: Option<String>,
61    #[serde(default)]
62    pub assets: std::collections::BTreeMap<String, String>,
63}
64
65fn default_tag_prefix() -> String {
66    "bridge-v".to_owned()
67}
68
69impl Default for GatewayConfigSpec {
70    fn default() -> Self {
71        Self {
72            enabled: false,
73            routes: Vec::new(),
74            default_provider: None,
75            allow_unlisted_models: false,
76            auth_scheme: default_auth_scheme(),
77            inference_path_prefix: default_inference_path_prefix(),
78            system_prompt_overrides: Vec::new(),
79            bridge_releases: None,
80        }
81    }
82}
83
84pub(crate) fn default_auth_scheme() -> String {
85    "bearer".to_owned()
86}
87
88pub(crate) fn default_inference_path_prefix() -> String {
89    "/v1".to_owned()
90}
91
92impl GatewayConfigSpec {
93    #[must_use]
94    pub fn resolve(self) -> GatewayConfig {
95        let Self {
96            enabled,
97            routes,
98            default_provider,
99            allow_unlisted_models,
100            auth_scheme,
101            inference_path_prefix,
102            system_prompt_overrides,
103            bridge_releases,
104        } = self;
105
106        GatewayConfig {
107            enabled,
108            routes,
109            default_provider,
110            allow_unlisted_models,
111            auth_scheme,
112            inference_path_prefix,
113            system_prompt_overrides,
114            bridge_releases,
115        }
116    }
117}