systemprompt_models/services/gateway/config/
runtime.rs1use std::borrow::Cow;
7use std::collections::HashMap;
8
9use systemprompt_identifiers::{ProviderId, RouteId};
10
11use crate::services::gateway::config::{
12 BridgeReleasesSpec, DEFAULT_ROUTE_PATTERN, GatewayConfigSpec, QuotaFaultMode,
13 default_auth_scheme, default_inference_path_prefix,
14};
15use crate::services::gateway::override_rule::SystemPromptRule;
16use crate::services::gateway::route::GatewayRoute;
17use crate::services::providers::ProviderRegistry;
18use crate::wire::canonical::CanonicalRequest;
19
20#[derive(Debug, Clone)]
27pub struct GatewayConfig {
28 pub enabled: bool,
29 pub routes: Vec<GatewayRoute>,
30 pub default_provider: Option<ProviderId>,
31 pub default_model: Option<String>,
32 pub allow_unlisted_models: bool,
33 pub quota_fault_mode: QuotaFaultMode,
34 pub auth_scheme: String,
35 pub inference_path_prefix: String,
36 pub system_prompt_overrides: Vec<SystemPromptRule>,
37 pub bridge_releases: Option<BridgeReleasesSpec>,
38}
39
40impl Default for GatewayConfig {
41 fn default() -> Self {
42 Self {
43 enabled: false,
44 routes: Vec::new(),
45 default_provider: None,
46 default_model: None,
47 allow_unlisted_models: false,
48 quota_fault_mode: QuotaFaultMode::default(),
49 auth_scheme: default_auth_scheme(),
50 inference_path_prefix: default_inference_path_prefix(),
51 system_prompt_overrides: Vec::new(),
52 bridge_releases: None,
53 }
54 }
55}
56
57impl GatewayConfig {
58 pub fn find_route(&self, model: &str) -> Option<&GatewayRoute> {
59 self.routes.iter().find(|route| route.matches(model))
60 }
61
62 pub fn candidate_routes<'a>(
63 &'a self,
64 registry: &ProviderRegistry,
65 ) -> impl Iterator<Item = Cow<'a, GatewayRoute>> {
66 self.routes
67 .iter()
68 .map(Cow::Borrowed)
69 .chain(self.synthesize_default_route(registry).map(Cow::Owned))
70 }
71
72 #[must_use]
73 pub fn resolve_route<'a>(
74 &'a self,
75 registry: &ProviderRegistry,
76 request: &CanonicalRequest,
77 ) -> Option<Cow<'a, GatewayRoute>> {
78 self.candidate_routes(registry)
79 .find(|route| route.matches_request(request))
80 }
81
82 #[must_use]
83 pub fn dispatchable_route_ids(&self, registry: &ProviderRegistry) -> Vec<RouteId> {
84 let mut ids: Vec<RouteId> = Vec::new();
85 let mut seen: std::collections::HashSet<RouteId> = std::collections::HashSet::new();
86 for route in self.candidate_routes(registry) {
87 let mut route = route.into_owned();
88 route.ensure_id();
89 if seen.insert(route.id.clone()) {
90 ids.push(route.id);
91 }
92 }
93 ids
94 }
95
96 fn synthesize_default_route(&self, registry: &ProviderRegistry) -> Option<GatewayRoute> {
97 let provider = self.default_provider.as_ref()?;
98 registry.find_provider(provider.as_str())?;
99 let mut route = GatewayRoute {
100 id: RouteId::new(""),
101 model_pattern: DEFAULT_ROUTE_PATTERN.to_owned(),
102 provider: provider.clone(),
103 upstream_model: None,
104 extra_headers: HashMap::new(),
105 pricing: None,
106 when: None,
107 requires: None,
108 };
109 route.ensure_id();
110 Some(route)
111 }
112
113 #[must_use]
114 pub fn is_model_exposed(&self, registry: &ProviderRegistry, model: &str) -> bool {
115 if self.find_route(model).is_some() || registry.contains_model(model) {
116 return true;
117 }
118 if self.default_provider.is_some() && self.allow_unlisted_models {
119 tracing::warn!(
120 model,
121 "gateway forwarding an unlisted model to default_provider \
122 (allow_unlisted_models=true): open allowlist posture"
123 );
124 return true;
125 }
126 false
127 }
128
129 #[must_use]
130 pub fn to_spec(&self) -> GatewayConfigSpec {
131 GatewayConfigSpec {
132 enabled: self.enabled,
133 routes: self.routes.clone(),
134 default_provider: self.default_provider.clone(),
135 default_model: self.default_model.clone(),
136 allow_unlisted_models: self.allow_unlisted_models,
137 quota_fault_mode: self.quota_fault_mode,
138 auth_scheme: self.auth_scheme.clone(),
139 inference_path_prefix: self.inference_path_prefix.clone(),
140 system_prompt_overrides: self.system_prompt_overrides.clone(),
141 bridge_releases: self.bridge_releases.clone(),
142 }
143 }
144}