mod api;
mod client;
use std::marker::PhantomData;
use chat_core::types::provider_meta::ProviderMeta;
pub use crate::client::CompletionsClient;
pub use chat_core::error::{ChatError, ChatFailure};
pub use chat_core::transport::{Request, ReqwestTransport, Response, Transport, TransportError};
pub struct WithoutModel;
pub struct WithModel;
pub struct WithoutUrl;
pub struct WithUrl;
pub struct CompletionsBuilder<M = WithoutModel, U = WithoutUrl, T: Transport = ReqwestTransport> {
model_name: Option<String>,
api_key: Option<String>,
scheme: String,
host: String,
base_path: String,
extra_headers: Vec<(String, String)>,
transport: Option<T>,
meta: ProviderMeta,
_m: PhantomData<M>,
_u: PhantomData<U>,
}
impl Default for CompletionsBuilder<WithoutModel, WithoutUrl, ReqwestTransport> {
fn default() -> Self {
Self::new()
}
}
impl CompletionsBuilder<WithoutModel, WithoutUrl, ReqwestTransport> {
pub fn new() -> Self {
Self {
model_name: None,
api_key: None,
scheme: String::new(),
host: String::new(),
base_path: String::new(),
extra_headers: Vec::new(),
transport: Some(ReqwestTransport::default()),
meta: ProviderMeta::default(),
_m: PhantomData,
_u: PhantomData,
}
}
}
impl<M, U, T: Transport> CompletionsBuilder<M, U, T> {
pub fn with_api_key(mut self, api_key: impl Into<String>) -> Self {
self.api_key = Some(api_key.into());
self
}
pub fn with_header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.extra_headers.push((key.into(), value.into()));
self
}
pub fn with_description(mut self, description: impl Into<String>) -> Self {
self.meta.description = Some(description.into());
self
}
pub fn with_metadata(
mut self,
key: impl Into<String>,
value: impl std::any::Any + Send + Sync + 'static,
) -> Self {
self.meta.data.insert(key.into(), Box::new(value));
self
}
pub fn with_transport<T2: Transport>(self, transport: T2) -> CompletionsBuilder<M, U, T2> {
CompletionsBuilder {
model_name: self.model_name,
api_key: self.api_key,
scheme: self.scheme,
host: self.host,
base_path: self.base_path,
extra_headers: self.extra_headers,
transport: Some(transport),
meta: self.meta,
_m: PhantomData,
_u: PhantomData,
}
}
}
impl<U, T: Transport> CompletionsBuilder<WithoutModel, U, T> {
pub fn with_model(self, model_name: impl Into<String>) -> CompletionsBuilder<WithModel, U, T> {
CompletionsBuilder {
model_name: Some(model_name.into()),
api_key: self.api_key,
scheme: self.scheme,
host: self.host,
base_path: self.base_path,
extra_headers: self.extra_headers,
transport: self.transport,
meta: self.meta,
_m: PhantomData,
_u: PhantomData,
}
}
}
impl<M, T: Transport> CompletionsBuilder<M, WithoutUrl, T> {
pub fn with_base_url(self, base_url: impl AsRef<str>) -> CompletionsBuilder<M, WithUrl, T> {
let parsed = url::Url::parse(base_url.as_ref()).expect("Invalid base URL");
let scheme = parsed.scheme().to_string();
let host = parsed
.host_str()
.expect("base URL missing host")
.to_string()
+ &parsed.port().map(|p| format!(":{p}")).unwrap_or_default();
let base_path = parsed.path().trim_end_matches('/').to_string();
CompletionsBuilder {
model_name: self.model_name,
api_key: self.api_key,
scheme,
host,
base_path,
extra_headers: self.extra_headers,
transport: self.transport,
meta: self.meta,
_m: PhantomData,
_u: PhantomData,
}
}
}
impl<T: Transport> CompletionsBuilder<WithModel, WithUrl, T> {
pub fn build(self) -> CompletionsClient<T> {
let transport = self.transport.expect(
"No transport provided. Call .with_transport() or rely on the default ReqwestTransport.",
);
CompletionsClient {
model_name: self.model_name.unwrap(),
api_key: self.api_key,
scheme: self.scheme,
host: self.host,
base_path: self.base_path,
extra_headers: self.extra_headers,
transport,
meta: self.meta,
}
}
}