systemprompt_models/profile/gateway/
catalog.rs1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4use systemprompt_identifiers::{ModelId, ProviderId, SecretName};
5
6use super::error::{GatewayProfileError, GatewayResult};
7use crate::services::ai::ModelPricing;
8
9pub(super) fn validate_endpoint(label: &str, endpoint: &str) -> GatewayResult<()> {
15 let trusted = crate::net::trusted_http_hosts_from_env();
19 crate::net::validate_outbound_url_with_trust(endpoint, &trusted)
20 .map(|_| ())
21 .map_err(|e| GatewayProfileError::BlockedEndpoint {
22 label: label.to_owned(),
23 endpoint: endpoint.to_owned(),
24 reason: e.to_string(),
25 })
26}
27
28#[derive(Debug, Clone, Default, Serialize, Deserialize, schemars::JsonSchema)]
29#[serde(deny_unknown_fields)]
30pub struct GatewayCatalog {
31 #[serde(default)]
32 pub providers: Vec<GatewayProvider>,
33 #[serde(default)]
34 pub models: Vec<GatewayModel>,
35}
36
37impl GatewayCatalog {
38 pub fn validate(&self) -> GatewayResult<()> {
39 for model in &self.models {
40 if model.id.as_str().is_empty() {
41 return Err(GatewayProfileError::ModelEmptyId);
42 }
43 if !self.providers.iter().any(|p| p.name == model.provider) {
44 return Err(GatewayProfileError::UnknownProvider {
45 model: model.id.as_str().to_owned(),
46 provider: model.provider.as_str().to_owned(),
47 });
48 }
49 }
50 for provider in &self.providers {
51 if provider.name.as_str().is_empty() {
52 return Err(GatewayProfileError::ProviderEmptyName);
53 }
54 if provider.endpoint.is_empty() {
55 return Err(GatewayProfileError::ProviderEmptyEndpoint {
56 name: provider.name.as_str().to_owned(),
57 });
58 }
59 validate_endpoint(
60 &format!("provider '{}'", provider.name.as_str()),
61 &provider.endpoint,
62 )?;
63 }
64 Ok(())
65 }
66
67 pub fn find_provider(&self, name: &str) -> Option<&GatewayProvider> {
68 self.providers.iter().find(|p| p.name.as_str() == name)
69 }
70
71 #[must_use]
72 pub fn contains_model(&self, requested: &str) -> bool {
73 self.models.iter().any(|m| {
74 m.id.as_str() == requested || m.aliases.iter().any(|a| a.as_str() == requested)
75 })
76 }
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
80#[serde(deny_unknown_fields)]
81pub struct GatewayProvider {
82 pub name: ProviderId,
83 pub endpoint: String,
84 pub api_key_secret: SecretName,
85 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
86 pub extra_headers: HashMap<String, String>,
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
90#[serde(deny_unknown_fields)]
91pub struct GatewayModel {
92 pub id: ModelId,
93 pub provider: ProviderId,
94 #[serde(default, skip_serializing_if = "Vec::is_empty")]
95 pub aliases: Vec<ModelId>,
96 #[serde(default, skip_serializing_if = "Option::is_none")]
97 pub display_name: Option<String>,
98 #[serde(default, skip_serializing_if = "Option::is_none")]
99 pub upstream_model: Option<String>,
100 #[serde(default, skip_serializing_if = "Option::is_none")]
101 pub pricing: Option<ModelPricing>,
102}