llm/
provider_connection.rs1use std::collections::BTreeMap;
2
3#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
4#[serde(rename_all = "kebab-case")]
5pub enum ProviderAuthMode {
6 #[default]
7 Default,
8 None,
9}
10
11#[derive(Clone, Debug, Default, PartialEq, Eq)]
12pub struct ProviderConnectionConfig {
13 pub base_url: Option<String>,
14 pub auth_mode: ProviderAuthMode,
15 pub request_model: Option<String>,
16 pub inference_profile_arn: Option<String>,
17}
18
19#[doc = include_str!("docs/provider_connection_override.md")]
20#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
21#[serde(rename_all = "camelCase", deny_unknown_fields)]
22pub struct ProviderConnectionOverride {
23 #[serde(default, rename = "url", skip_serializing_if = "Option::is_none")]
25 pub base_url: Option<String>,
26 #[serde(default, rename = "auth", skip_serializing_if = "Option::is_none")]
29 pub auth_mode: Option<ProviderAuthMode>,
30 #[serde(default, skip_serializing_if = "Option::is_none")]
32 pub request_model: Option<String>,
33 #[serde(default, skip_serializing_if = "Option::is_none")]
35 pub inference_profile_arn: Option<String>,
36}
37
38#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
39#[serde(transparent)]
40pub struct ProviderConnectionOverrides {
41 providers: BTreeMap<String, ProviderConnectionOverride>,
42}
43
44impl ProviderConnectionConfig {
45 pub fn from_override(value: ProviderConnectionOverride) -> Self {
46 Self {
47 base_url: value.base_url,
48 auth_mode: value.auth_mode.unwrap_or_default(),
49 request_model: value.request_model,
50 inference_profile_arn: value.inference_profile_arn,
51 }
52 }
53}
54
55impl ProviderConnectionOverride {
56 pub fn url(url: impl Into<String>) -> Self {
57 Self { base_url: Some(url.into()), ..Self::default() }
58 }
59
60 pub fn auth(auth_mode: ProviderAuthMode) -> Self {
61 Self { auth_mode: Some(auth_mode), ..Self::default() }
62 }
63
64 pub fn request_model(model: impl Into<String>) -> Self {
65 Self { request_model: Some(model.into()), ..Self::default() }
66 }
67
68 pub fn inference_profile_arn(arn: impl Into<String>) -> Self {
69 Self { inference_profile_arn: Some(arn.into()), ..Self::default() }
70 }
71
72 pub fn merge(&mut self, override_value: Self) {
73 if override_value.base_url.is_some() {
74 self.base_url = override_value.base_url;
75 }
76 if override_value.auth_mode.is_some() {
77 self.auth_mode = override_value.auth_mode;
78 }
79 if override_value.request_model.is_some() {
80 self.request_model = override_value.request_model;
81 }
82 if override_value.inference_profile_arn.is_some() {
83 self.inference_profile_arn = override_value.inference_profile_arn;
84 }
85 }
86}
87
88impl ProviderConnectionOverrides {
89 pub fn new(providers: BTreeMap<String, ProviderConnectionOverride>) -> Self {
90 Self { providers }
91 }
92
93 pub fn is_empty(&self) -> bool {
94 self.providers.is_empty()
95 }
96
97 pub fn merge(&mut self, overrides: ProviderConnectionOverrides) {
98 for (provider, override_value) in overrides.providers {
99 self.providers
100 .entry(provider)
101 .and_modify(|existing| existing.merge(override_value.clone()))
102 .or_insert(override_value);
103 }
104 }
105
106 pub fn config_for(&self, provider: &str) -> ProviderConnectionConfig {
107 self.providers.get(provider).cloned().map(ProviderConnectionConfig::from_override).unwrap_or_default()
108 }
109
110 pub fn into_inner(self) -> BTreeMap<String, ProviderConnectionOverride> {
111 self.providers
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn deserializes_and_merges_request_model() {
121 let mut first: ProviderConnectionOverrides =
122 serde_json::from_str(r#"{"azure-foundry":{"requestModel":"first"}}"#).unwrap();
123 first.merge(ProviderConnectionOverrides::new(BTreeMap::from([(
124 "azure-foundry".to_string(),
125 ProviderConnectionOverride::request_model("second"),
126 )])));
127
128 assert_eq!(first.config_for("azure-foundry").request_model.as_deref(), Some("second"));
129 }
130 #[test]
131 fn deserializes_bedrock_inference_profile_arn() {
132 let overrides: ProviderConnectionOverrides = serde_json::from_str(
133 r#"{"bedrock":{"inferenceProfileArn":"arn:aws:bedrock:us-west-2:000000000000:application-inference-profile/000000000000"}}"#,
134 )
135 .unwrap();
136
137 let config = overrides.config_for("bedrock");
138
139 assert_eq!(
140 config.inference_profile_arn.as_deref(),
141 Some("arn:aws:bedrock:us-west-2:000000000000:application-inference-profile/000000000000")
142 );
143 }
144
145 #[test]
146 fn merge_replaces_inference_profile_arn() {
147 let mut first = ProviderConnectionOverride::inference_profile_arn("arn:first");
148
149 first.merge(ProviderConnectionOverride::inference_profile_arn("arn:second"));
150
151 assert_eq!(first.inference_profile_arn.as_deref(), Some("arn:second"));
152 }
153
154 #[test]
155 fn provider_overrides_merge_inference_profile_arn() {
156 let mut first = ProviderConnectionOverrides::new(BTreeMap::from([(
157 "bedrock".to_string(),
158 ProviderConnectionOverride::inference_profile_arn("arn:first"),
159 )]));
160 let second = ProviderConnectionOverrides::new(BTreeMap::from([(
161 "bedrock".to_string(),
162 ProviderConnectionOverride::inference_profile_arn("arn:second"),
163 )]));
164
165 first.merge(second);
166
167 assert_eq!(first.config_for("bedrock").inference_profile_arn.as_deref(), Some("arn:second"));
168 }
169}