mod private
{
use crate::error::OpenAIError;
use crate::secret::Secret;
use crate::diagnostics::DiagnosticsConfig;
use reqwest::header;
use url::Url;
use secrecy::ExposeSecret;
use error_tools::untyped::Result;
const OPENAI_BASE_URL : &str = "https://api.openai.com/v1/";
const OPENAI_REALTIME_BASE_URL : &str = "https://api.openai.com/v1/realtime/";
pub const OPENAI_BETA_HEADER : &str = "OpenAI-Beta";
#[ derive( Debug ) ]
pub struct OpenAIRecommended;
impl OpenAIRecommended
{
#[ inline ]
#[ must_use ]
pub fn base_url() -> &'static str
{
OPENAI_BASE_URL
}
#[ inline ]
#[ must_use ]
pub fn realtime_base_url() -> &'static str
{
OPENAI_REALTIME_BASE_URL
}
}
pub trait EnvironmentInterface : Send + Sync + 'static
{
fn api_key( &self ) -> &crate::secret::Secret;
fn organization_id( &self ) -> Option< &str >;
fn project_id( &self ) -> Option< &str >;
}
pub trait OpenaiEnvironment : Send + Sync + 'static
{
fn api_key( &self ) -> &Secret;
fn organization_id( &self ) -> Option< &str >;
fn project_id( &self ) -> Option< &str >;
fn base_url( &self ) -> &Url;
fn realtime_base_url( &self ) -> &Url;
fn diagnostics_config( &self ) -> Option< &DiagnosticsConfig >;
fn headers( &self ) -> Result< header::HeaderMap >;
fn join_base_url( &self, path : &str ) -> Result< Url >;
fn join_realtime_base_url( &self, path : &str ) -> Result< Url >;
}
#[ derive( Debug, Clone ) ]
#[ non_exhaustive ]
pub struct OpenaiEnvironmentImpl
{
pub api_key : Secret,
pub base_url : Url,
pub organization_id : Option< String >,
pub project_id : Option< String >,
pub realtime_base_url : Url,
pub diagnostics_config : Option< DiagnosticsConfig >,
}
impl OpenaiEnvironmentImpl
{
#[ inline ]
#[ allow( clippy::needless_pass_by_value ) ]
pub fn build
(
api_key : Secret,
organization_id : Option< String >,
project_id : Option< String >,
base_url : String,
realtime_base_url : String,
) -> Result< Self >
{
let base_url = Url::parse( &base_url ).map_err( | e | error_tools::Error::from( OpenAIError::InvalidArgument( format!( "Invalid base URL: {e}" ) ) ) )?;
let realtime_base_url = Url::parse( &realtime_base_url ).map_err( | e | error_tools::Error::from( OpenAIError::InvalidArgument( format!( "Invalid realtime base URL: {e}" ) ) ) )?;
Ok( Self
{
api_key,
base_url,
organization_id,
project_id,
realtime_base_url,
diagnostics_config : None,
})
}
#[ inline ]
#[ allow( clippy::needless_pass_by_value ) ]
pub fn build_with_diagnostics
(
api_key : Secret,
organization_id : Option< String >,
project_id : Option< String >,
base_url : String,
realtime_base_url : String,
diagnostics_config : Option< DiagnosticsConfig >,
) -> Result< Self >
{
let base_url = Url::parse( &base_url ).map_err( | e | error_tools::Error::from( OpenAIError::InvalidArgument( format!( "Invalid base URL: {e}" ) ) ) )?;
let realtime_base_url = Url::parse( &realtime_base_url ).map_err( | e | error_tools::Error::from( OpenAIError::InvalidArgument( format!( "Invalid realtime base URL: {e}" ) ) ) )?;
Ok( Self
{
api_key,
base_url,
organization_id,
project_id,
realtime_base_url,
diagnostics_config,
})
}
}
impl OpenaiEnvironment for OpenaiEnvironmentImpl
{
#[ inline ]
fn api_key( &self ) -> &Secret
{
&self.api_key
}
#[ inline ]
fn organization_id( &self ) -> Option< &str >
{
self.organization_id.as_deref()
}
#[ inline ]
fn project_id( &self ) -> Option< &str >
{
self.project_id.as_deref()
}
#[ inline ]
fn base_url( &self ) -> &Url
{
&self.base_url
}
#[ inline ]
fn realtime_base_url( &self ) -> &Url
{
&self.realtime_base_url
}
#[ inline ]
fn diagnostics_config( &self ) -> Option< &DiagnosticsConfig >
{
self.diagnostics_config.as_ref()
}
#[ inline ]
fn headers( &self ) -> Result< header::HeaderMap >
{
let mut headers = header::HeaderMap::new();
let api_key = self.api_key.expose_secret();
let auth_value = header::HeaderValue::from_str( &format!( "Bearer {api_key}" ) )
.map_err( | error | error_tools::Error::from( OpenAIError::InvalidArgument( format!( "Invalid API key : {error}" ) ) ) )?;
headers.insert( header::AUTHORIZATION, auth_value );
if let Some( org_id ) = OpenaiEnvironment::organization_id( self )
{
let org_value = header::HeaderValue::from_str( org_id )
.map_err( | error | error_tools::Error::from( OpenAIError::InvalidArgument( format!( "Invalid Organization ID: {error}" ) ) ) )?;
headers.insert( header::HeaderName::from_static( "openai-organization" ), org_value );
}
if let Some( project_id ) = OpenaiEnvironment::project_id( self )
{
let project_value = header::HeaderValue::from_str( project_id )
.map_err( | error | error_tools::Error::from( OpenAIError::InvalidArgument( format!( "Invalid Project ID: {error}" ) ) ) )?;
headers.insert( header::HeaderName::from_static( "openai-project" ), project_value );
}
Ok( headers )
}
#[ inline ]
fn join_base_url( &self, path : &str ) -> Result< Url >
{
self.base_url.join( path ).map_err( | e | error_tools::Error::from( OpenAIError::Internal( format!( "Failed to join base URL: {e}" ) ) ) )
}
#[ inline ]
fn join_realtime_base_url( &self, path : &str ) -> Result< Url >
{
self.realtime_base_url.join( path ).map_err( | e | error_tools::Error::from( OpenAIError::Internal( format!( "Failed to join realtime URL: {e}" ) ) ) )
}
}
impl EnvironmentInterface for OpenaiEnvironmentImpl
{
#[ inline ]
fn api_key( &self ) -> &Secret
{
&self.api_key
}
#[ inline ]
fn organization_id( &self ) -> Option< &str >
{
self.organization_id.as_deref()
}
#[ inline ]
fn project_id( &self ) -> Option< &str >
{
self.project_id.as_deref()
}
}
}
crate ::mod_interface!
{
exposed use
{
EnvironmentInterface,
OpenaiEnvironment,
OpenaiEnvironmentImpl,
OpenAIRecommended,
OPENAI_BETA_HEADER,
};
}