aurum_core/provider_platform/
context.rs1use crate::observability::Metrics;
4use crate::providers::local::SttContextPool;
5use crate::providers::OpenRouterSttMode;
6use crate::runtime::ResourceGovernor;
7use crate::secret::SecretString;
8use std::path::{Path, PathBuf};
9use std::sync::Arc;
10
11#[cfg(feature = "tts")]
12use crate::tts::local::TtsSessionPool;
13
14#[derive(Clone)]
23pub struct ProviderBuildContext {
24 cache_dir: PathBuf,
25 local_only: bool,
26 api_key: Option<SecretString>,
28 base_url: Option<String>,
29 allow_custom_endpoint: bool,
30 use_system_proxy: bool,
31 show_progress: bool,
33 stt_mode: OpenRouterSttMode,
35 tts_max_chars: Option<usize>,
37 stt_pool: Option<Arc<SttContextPool>>,
38 governor: Option<Arc<ResourceGovernor>>,
39 metrics: Option<Arc<Metrics>>,
40 #[cfg(feature = "tts")]
41 tts_pool: Option<Arc<TtsSessionPool>>,
42}
43
44impl ProviderBuildContext {
45 pub fn new(cache_dir: impl Into<PathBuf>) -> Self {
47 Self {
48 cache_dir: cache_dir.into(),
49 local_only: false,
50 api_key: None,
51 base_url: None,
52 allow_custom_endpoint: false,
53 use_system_proxy: false,
54 show_progress: false,
55 stt_mode: OpenRouterSttMode::Auto,
56 tts_max_chars: None,
57 stt_pool: None,
58 governor: None,
59 metrics: None,
60 #[cfg(feature = "tts")]
61 tts_pool: None,
62 }
63 }
64
65 pub fn with_local_only(mut self, local_only: bool) -> Self {
66 self.local_only = local_only;
67 self
68 }
69
70 pub fn with_api_key(mut self, key: Option<SecretString>) -> Self {
72 self.api_key = key;
73 self
74 }
75
76 pub fn with_base_url(mut self, base_url: Option<String>) -> Self {
77 self.base_url = base_url;
78 self
79 }
80
81 pub fn with_allow_custom_endpoint(mut self, allow: bool) -> Self {
82 self.allow_custom_endpoint = allow;
83 self
84 }
85
86 pub fn with_use_system_proxy(mut self, use_proxy: bool) -> Self {
87 self.use_system_proxy = use_proxy;
88 self
89 }
90
91 pub fn with_show_progress(mut self, show: bool) -> Self {
92 self.show_progress = show;
93 self
94 }
95
96 pub fn with_stt_mode(mut self, mode: OpenRouterSttMode) -> Self {
97 self.stt_mode = mode;
98 self
99 }
100
101 pub fn with_tts_max_chars(mut self, n: Option<usize>) -> Self {
102 self.tts_max_chars = n;
103 self
104 }
105
106 pub fn with_stt_pool(mut self, pool: Arc<SttContextPool>) -> Self {
107 self.stt_pool = Some(pool);
108 self
109 }
110
111 pub fn with_governor(mut self, gov: Arc<ResourceGovernor>) -> Self {
112 self.governor = Some(gov);
113 self
114 }
115
116 pub fn with_metrics(mut self, metrics: Arc<Metrics>) -> Self {
117 self.metrics = Some(metrics);
118 self
119 }
120
121 #[cfg(feature = "tts")]
122 pub fn with_tts_pool(mut self, pool: Arc<TtsSessionPool>) -> Self {
123 self.tts_pool = Some(pool);
124 self
125 }
126
127 pub fn cache_dir(&self) -> &Path {
128 &self.cache_dir
129 }
130
131 pub fn local_only(&self) -> bool {
132 self.local_only
133 }
134
135 pub fn base_url(&self) -> Option<&str> {
136 self.base_url.as_deref()
137 }
138
139 pub fn allow_custom_endpoint(&self) -> bool {
140 self.allow_custom_endpoint
141 }
142
143 pub fn use_system_proxy(&self) -> bool {
144 self.use_system_proxy
145 }
146
147 pub fn show_progress(&self) -> bool {
148 self.show_progress
149 }
150
151 pub fn stt_mode(&self) -> OpenRouterSttMode {
152 self.stt_mode
153 }
154
155 pub fn tts_max_chars(&self) -> Option<usize> {
156 self.tts_max_chars
157 }
158
159 pub fn stt_pool(&self) -> Option<&Arc<SttContextPool>> {
160 self.stt_pool.as_ref()
161 }
162
163 pub fn governor(&self) -> Option<&Arc<ResourceGovernor>> {
164 self.governor.as_ref()
165 }
166
167 pub fn metrics(&self) -> Option<&Arc<Metrics>> {
168 self.metrics.as_ref()
169 }
170
171 #[cfg(feature = "tts")]
172 pub fn tts_pool(&self) -> Option<&Arc<TtsSessionPool>> {
173 self.tts_pool.as_ref()
174 }
175
176 pub fn api_key(&self) -> Option<&SecretString> {
181 self.api_key.as_ref()
182 }
183
184 pub fn api_key_cloned(&self) -> Option<SecretString> {
186 self.api_key.clone()
187 }
188
189 pub fn has_api_key(&self) -> bool {
191 self.api_key.is_some()
192 }
193
194 #[deprecated(note = "use api_key_cloned() — does not return plaintext")]
196 pub fn api_key_exposed(&self) -> Option<SecretString> {
197 self.api_key_cloned()
198 }
199}
200
201impl std::fmt::Debug for ProviderBuildContext {
202 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
203 f.debug_struct("ProviderBuildContext")
204 .field("cache_dir", &self.cache_dir)
205 .field("local_only", &self.local_only)
206 .field("api_key", &self.api_key) .field("base_url", &self.base_url)
208 .field("allow_custom_endpoint", &self.allow_custom_endpoint)
209 .field("use_system_proxy", &self.use_system_proxy)
210 .field("show_progress", &self.show_progress)
211 .field("stt_mode", &self.stt_mode)
212 .field("tts_max_chars", &self.tts_max_chars)
213 .field("has_stt_pool", &self.stt_pool.is_some())
214 .field("has_governor", &self.governor.is_some())
215 .field("has_metrics", &self.metrics.is_some())
216 .finish_non_exhaustive()
217 }
218}
219
220#[cfg(test)]
221mod tests {
222 use super::*;
223 use crate::secret::SecretString;
224
225 #[test]
226 fn debug_redacts_api_key() {
227 let ctx = ProviderBuildContext::new("/tmp/aurum-test")
228 .with_api_key(Some(SecretString::new("sk-super-secret-key-value")));
229 let dbg = format!("{ctx:?}");
230 assert!(!dbg.contains("sk-super-secret"));
231 assert!(dbg.contains("api_key"));
232 }
233
234 #[test]
235 fn scoped_key_is_optional() {
236 let ctx = ProviderBuildContext::new("/tmp/x");
237 assert!(!ctx.has_api_key());
238 assert!(ctx.api_key().is_none());
239 assert!(ctx.api_key_cloned().is_none());
240 }
241}