use reqwest::Method;
use serde::{Deserialize, Serialize};
use crate::{auth, error::OpenAIAPIError, OpenAI, OpenAIResult};
pub mod chat;
pub mod chat_reasoning;
pub mod embeddings;
const API_BASE_URL: &str = "https://api.openai.com/v1";
#[derive(Deserialize)]
#[serde(untagged)]
enum GenericOpenAIResponse<T> {
Success(T),
Error(ResponseDeserializableOpenAIAPIError),
}
#[derive(Deserialize)]
struct ResponseDeserializableOpenAIAPIError {
error: OpenAIAPIError,
}
impl<T> From<GenericOpenAIResponse<T>> for OpenAIResult<T> {
fn from(value: GenericOpenAIResponse<T>) -> Self {
match value {
GenericOpenAIResponse::Success(success) => Ok(success),
GenericOpenAIResponse::Error(error) => Err(crate::OpenAIError::API(error.error)),
}
}
}
pub(super) async fn send_request<Auth, R>(
openai: &OpenAI<Auth>,
request: &R,
) -> OpenAIResult<R::Response>
where
Auth: auth::AuthTokenProvider,
R: OpenAIRequestProvider,
{
let bearer_token = openai
.auth
.resolve()
.await
.ok_or(crate::error::OpenAIError::MissingAuthToken)?;
let response_text = openai
.client
.request(
R::METHOD,
format!("{API_BASE_URL}{}", R::path_with_leading_slash()),
)
.header("Authorization", format!("Bearer {bearer_token}"))
.json(request)
.send()
.await?
.text()
.await?;
match serde_json::from_str::<GenericOpenAIResponse<R::Response>>(&response_text) {
Ok(response) => response.into(),
Err(err) => Err(crate::error::OpenAIError::Serde(response_text, err)),
}
}
mod private {
pub trait Sealed {}
}
pub trait OpenAIRequestProvider: Serialize + private::Sealed {
type Response: for<'de> Deserialize<'de>;
const METHOD: Method;
fn path_with_leading_slash() -> String;
}