use std::time::Duration;
#[derive(Clone)]
pub struct OpenAiConfig {
pub api_key: String,
pub model: String,
pub base_url: String,
pub organization: Option<String>,
pub timeout: Option<Duration>,
pub client: Option<reqwest::Client>,
}
impl std::fmt::Debug for OpenAiConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("OpenAiConfig")
.field("api_key", &"[REDACTED]")
.field("model", &self.model)
.field("base_url", &self.base_url)
.field("organization", &self.organization)
.field("timeout", &self.timeout)
.field("client", &self.client.as_ref().map(|_| "..."))
.finish()
}
}
impl Default for OpenAiConfig {
fn default() -> Self {
Self {
api_key: String::new(),
model: "gpt-4o".into(),
base_url: "https://api.openai.com/v1".into(),
organization: None,
timeout: None,
client: None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = OpenAiConfig::default();
assert_eq!(config.model, "gpt-4o");
assert_eq!(config.base_url, "https://api.openai.com/v1");
assert!(config.api_key.is_empty());
assert!(config.organization.is_none());
assert!(config.timeout.is_none());
assert!(config.client.is_none());
}
#[test]
fn test_debug_redacts_api_key() {
let config = OpenAiConfig {
api_key: "sk-super-secret".into(),
..Default::default()
};
let debug_output = format!("{config:?}");
assert!(!debug_output.contains("sk-super-secret"));
assert!(debug_output.contains("[REDACTED]"));
}
#[test]
fn test_config_override() {
let config = OpenAiConfig {
api_key: "test-key".into(),
model: "gpt-4o-mini".into(),
organization: Some("org-123".into()),
..Default::default()
};
assert_eq!(config.api_key, "test-key");
assert_eq!(config.model, "gpt-4o-mini");
assert_eq!(config.organization.as_deref(), Some("org-123"));
}
}