use crate::error::LingerError;
use crate::retry::{NoopRetrySleeper, RetryPolicy, RetrySleeper};
use std::fmt;
use std::sync::Arc;
#[derive(Clone)]
#[non_exhaustive]
pub struct ClientConfig {
api_key: String,
base_url: String,
organization: Option<String>,
project: Option<String>,
retry_policy: RetryPolicy,
retry_sleeper: Arc<dyn RetrySleeper>,
}
impl ClientConfig {
pub fn builder() -> ClientConfigBuilder {
ClientConfigBuilder::default()
}
pub(crate) fn api_key(&self) -> &str {
&self.api_key
}
pub fn base_url(&self) -> &str {
&self.base_url
}
pub fn organization(&self) -> Option<&str> {
self.organization.as_deref()
}
pub fn project(&self) -> Option<&str> {
self.project.as_deref()
}
pub fn retry_policy(&self) -> &RetryPolicy {
&self.retry_policy
}
pub fn retry_sleeper(&self) -> Arc<dyn RetrySleeper> {
self.retry_sleeper.clone()
}
}
impl fmt::Debug for ClientConfig {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ClientConfig")
.field("api_key", &"<redacted>")
.field("base_url", &self.base_url)
.field(
"organization",
&self.organization.as_ref().map(|_| "<redacted>"),
)
.field("project", &self.project.as_ref().map(|_| "<redacted>"))
.field("retry_policy", &self.retry_policy)
.field("retry_sleeper", &self.retry_sleeper)
.finish()
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct ClientConfigBuilder {
api_key: Option<String>,
base_url: String,
organization: Option<String>,
project: Option<String>,
retry_policy: RetryPolicy,
retry_sleeper: Arc<dyn RetrySleeper>,
}
impl Default for ClientConfigBuilder {
fn default() -> Self {
Self {
api_key: None,
base_url: "https://api.openai.com".to_string(),
organization: None,
project: None,
retry_policy: RetryPolicy::default(),
retry_sleeper: Arc::new(NoopRetrySleeper),
}
}
}
impl ClientConfigBuilder {
pub fn api_key(mut self, api_key: impl Into<String>) -> Self {
self.api_key = Some(api_key.into());
self
}
pub fn base_url(mut self, base_url: impl Into<String>) -> Self {
self.base_url = base_url.into();
self
}
pub fn organization(mut self, organization: impl Into<String>) -> Self {
self.organization = Some(organization.into());
self
}
pub fn project(mut self, project: impl Into<String>) -> Self {
self.project = Some(project.into());
self
}
pub fn retry_policy(mut self, retry_policy: RetryPolicy) -> Self {
self.retry_policy = retry_policy;
self
}
pub fn retry_sleeper(mut self, retry_sleeper: Arc<dyn RetrySleeper>) -> Self {
self.retry_sleeper = retry_sleeper;
self
}
pub fn build(self) -> Result<ClientConfig, LingerError> {
let api_key = self
.api_key
.filter(|value| !value.trim().is_empty())
.ok_or_else(|| LingerError::invalid_config("api_key is required"))?;
if self.base_url.trim().is_empty() {
return Err(LingerError::invalid_config("base_url is required"));
}
Ok(ClientConfig {
api_key,
base_url: self.base_url.trim_end_matches('/').to_string(),
organization: self.organization,
project: self.project,
retry_policy: self.retry_policy,
retry_sleeper: self.retry_sleeper,
})
}
}