codetether_agent/provider/
tenant_keys.rs1use super::registry::ProviderRegistry;
4use crate::secrets::ProviderSecrets;
5use anyhow::Result;
6use serde::Deserialize;
7use std::collections::HashMap;
8
9#[derive(Debug, Clone, Deserialize, Default)]
10pub struct TenantProviderKeyPayload {
11 #[serde(default)]
12 pub source: Option<String>,
13 #[serde(default)]
14 pub providers: HashMap<String, ProviderSecrets>,
15}
16
17#[derive(Debug)]
20pub struct PerTaskProviderKeys {
21 payload: TenantProviderKeyPayload,
22}
23
24impl PerTaskProviderKeys {
25 pub fn from_value(value: &serde_json::Value) -> Result<Option<Self>> {
26 if value.is_null() {
27 return Ok(None);
28 }
29 let payload: TenantProviderKeyPayload = serde_json::from_value(value.clone())?;
30 if payload.providers.is_empty() {
31 Ok(None)
32 } else {
33 Ok(Some(Self { payload }))
34 }
35 }
36
37 pub fn source(&self) -> &str {
38 self.payload.source.as_deref().unwrap_or("byok")
39 }
40
41 pub fn provider_names(&self) -> Vec<String> {
42 let mut names = self.payload.providers.keys().cloned().collect::<Vec<_>>();
43 names.sort();
44 names
45 }
46
47 pub fn build_registry(&self) -> ProviderRegistry {
48 ProviderRegistry::from_provider_secrets_map(&self.payload.providers)
49 }
50}
51
52impl Drop for PerTaskProviderKeys {
53 fn drop(&mut self) {
54 for secrets in self.payload.providers.values_mut() {
55 if let Some(key) = secrets.api_key.as_mut() {
56 unsafe {
57 key.as_bytes_mut().fill(0);
58 }
59 key.clear();
60 }
61 if let Some(org) = secrets.organization.as_mut() {
62 unsafe {
63 org.as_bytes_mut().fill(0);
64 }
65 org.clear();
66 }
67 if let Some(headers) = secrets.headers.as_mut() {
68 for value in headers.values_mut() {
69 unsafe {
70 value.as_bytes_mut().fill(0);
71 }
72 value.clear();
73 }
74 headers.clear();
75 }
76 }
77 }
78}