#[cfg(feature = "reqwest")]
use crate::v1::error::APIError;
use bytes::Bytes;
#[cfg(feature = "reqwest")]
use reqwest::{header::HeaderMap, multipart::Part};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Usage {
#[serde(skip_serializing_if = "Option::is_none")]
pub prompt_tokens: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub completion_tokens: Option<u32>,
pub total_tokens: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub prompt_tokens_details: Option<PromptTokensDetails>,
#[serde(skip_serializing_if = "Option::is_none")]
pub completion_tokens_details: Option<CompletionTokensDetails>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct InputTokensDetails {
pub cached_tokens: u32,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct OutputTokensDetails {
pub reasoning_tokens: u32,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct PromptTokensDetails {
#[serde(skip_serializing_if = "Option::is_none")]
pub audio_tokens: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cached_tokens: Option<u32>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct CompletionTokensDetails {
#[serde(skip_serializing_if = "Option::is_none")]
pub reasoning_tokens: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub audio_tokens: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub accepted_prediction_tokens: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rejected_prediction_tokens: Option<u32>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct ResponseWrapper<T> {
pub data: T,
pub headers: Headers,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Headers {
#[serde(rename = "x-ratelimit-limit-requests")]
pub x_ratelimit_limit_requests: Option<u32>,
#[serde(rename = "x-ratelimit-limit-tokens")]
pub x_ratelimit_limit_tokens: Option<u32>,
#[serde(rename = "x-ratelimit-remaining-requests")]
pub x_ratelimit_remaining_requests: Option<u32>,
#[serde(rename = "x-ratelimit-remaining-tokens")]
pub x_ratelimit_remaining_tokens: Option<u32>,
#[serde(rename = "x-ratelimit-reset-requests")]
pub x_ratelimit_reset_requests: Option<String>,
#[serde(rename = "x-ratelimit-reset-tokens")]
pub x_ratelimit_reset_tokens: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct SimpleListParameters {
pub after: Option<String>,
pub limit: Option<u32>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct ListParameters {
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub order: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub after: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub before: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct ListResponse<T> {
pub object: String,
pub data: Vec<T>,
pub first_id: Option<String>,
pub last_id: Option<String>,
pub has_more: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct DeletedObject {
pub id: String,
pub object: String,
pub deleted: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct LastError {
pub code: LastErrorCode,
pub message: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum LastErrorCode {
ServerError,
RateLimitExceeded,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum FinishReason {
#[serde(rename = "stop", alias = "STOP")]
StopSequenceReached,
#[serde(rename = "length", alias = "MAX_TOKENS")]
TokenLimitReached,
#[serde(
rename = "content_filter",
alias = "SAFETY",
alias = "SPII",
alias = "PROHIBITED_CONTENT",
alias = "BLOCKLIST",
alias = "RECITATION"
)]
ContentFilterFlagged,
ToolCalls,
EndTurn,
#[serde(rename = "FINISH_REASON_UNSPECIFIED ")]
FinishReasonUnspecified,
#[serde(rename = "MALFORMED_FUNCTION_CALL")]
MalformedFunctionCall,
#[serde(rename = "OTHER")]
Other,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum ReasoningEffort {
High,
Medium,
Low,
Minimal,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum WebSearchContextSize {
Low,
Medium,
Large,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(untagged)]
pub enum StopToken {
String(String),
Array(Vec<String>),
}
#[cfg(feature = "reqwest")]
impl From<HeaderMap> for Headers {
fn from(value: HeaderMap) -> Self {
if value.get("x-ratelimit-limit-requests").is_none()
|| value.get("x-ratelimit-limit-tokens").is_none()
|| value.get("x-ratelimit-remaining-requests").is_none()
|| value.get("x-ratelimit-remaining-tokens").is_none()
|| value.get("x-ratelimit-reset-requests").is_none()
|| value.get("x-ratelimit-reset-tokens").is_none()
{
return Self {
x_ratelimit_limit_requests: None,
x_ratelimit_limit_tokens: None,
x_ratelimit_remaining_requests: None,
x_ratelimit_remaining_tokens: None,
x_ratelimit_reset_requests: None,
x_ratelimit_reset_tokens: None,
};
}
Self {
x_ratelimit_limit_requests: Some(
value
.get("x-ratelimit-limit-requests")
.unwrap()
.to_str()
.unwrap()
.parse::<u32>()
.unwrap(),
),
x_ratelimit_limit_tokens: Some(
value
.get("x-ratelimit-limit-tokens")
.unwrap()
.to_str()
.unwrap()
.parse::<u32>()
.unwrap(),
),
x_ratelimit_remaining_requests: Some(
value
.get("x-ratelimit-remaining-requests")
.unwrap()
.to_str()
.unwrap()
.parse::<u32>()
.unwrap(),
),
x_ratelimit_remaining_tokens: Some(
value
.get("x-ratelimit-remaining-tokens")
.unwrap()
.to_str()
.unwrap()
.parse::<u32>()
.unwrap(),
),
x_ratelimit_reset_requests: Some(
value
.get("x-ratelimit-reset-requests")
.unwrap()
.to_str()
.unwrap()
.to_string(),
),
x_ratelimit_reset_tokens: Some(
value
.get("x-ratelimit-reset-tokens")
.unwrap()
.to_str()
.unwrap()
.to_string(),
),
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct FileUploadBytes {
pub bytes: Bytes,
pub filename: String,
}
impl FileUploadBytes {
pub fn new(bytes: impl Into<Bytes>, filename: impl Into<String>) -> Self {
Self {
bytes: bytes.into(),
filename: filename.into(),
}
}
#[cfg(feature = "reqwest")]
pub(crate) fn into_part(self) -> Result<Part, APIError> {
reqwest::multipart::Part::bytes(self.bytes.to_vec())
.file_name(self.filename.clone())
.mime_str("application/octet-stream")
.map_err(|error| APIError::FileError(error.to_string()))
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum FileUpload {
Bytes(FileUploadBytes),
BytesArray(Vec<FileUploadBytes>),
#[cfg(all(feature = "tokio", feature = "tokio-util"))]
File(String),
#[cfg(all(feature = "tokio", feature = "tokio-util"))]
FileArray(Vec<String>),
}
impl FileUpload {
#[cfg(feature = "reqwest")]
pub(crate) async fn into_part(self) -> Result<Part, APIError> {
match self {
FileUpload::Bytes(bytes) => bytes.into_part(),
FileUpload::BytesArray(_) => {
unimplemented!("BytesArray is not supported for this route")
}
#[cfg(all(feature = "tokio", feature = "tokio-util"))]
FileUpload::File(path) => {
use tokio::fs::File;
use tokio_util::codec::{BytesCodec, FramedRead};
let file = File::open(&path)
.await
.map_err(|error| APIError::FileError(error.to_string()))?;
let stream = FramedRead::new(file, BytesCodec::new());
let file_body = reqwest::Body::wrap_stream(stream);
let file_part = reqwest::multipart::Part::stream(file_body).file_name(path);
Ok(file_part)
}
#[cfg(all(feature = "tokio", feature = "tokio-util"))]
FileUpload::FileArray(_) => {
unimplemented!("FileArray is not supported for this route")
}
}
}
#[cfg(feature = "reqwest")]
pub(crate) async fn into_parts(self) -> Result<Vec<Part>, APIError> {
match self {
FileUpload::Bytes(bytes) => bytes.into_part().map(|part| vec![part]),
FileUpload::BytesArray(bytes) => bytes
.into_iter()
.map(|bytes| bytes.into_part())
.collect::<Result<Vec<Part>, APIError>>(),
#[cfg(all(feature = "tokio", feature = "tokio-util"))]
FileUpload::File(path) => {
use tokio::fs::File;
use tokio_util::codec::{BytesCodec, FramedRead};
let file = File::open(&path)
.await
.map_err(|error| APIError::FileError(error.to_string()))?;
let stream = FramedRead::new(file, BytesCodec::new());
let file_body = reqwest::Body::wrap_stream(stream);
let file_part = reqwest::multipart::Part::stream(file_body)
.file_name(path)
.mime_str("application/octet-stream")
.unwrap();
Ok(vec![file_part])
}
#[cfg(all(feature = "tokio", feature = "tokio-util"))]
FileUpload::FileArray(paths) => {
use tokio::fs::File;
use tokio_util::codec::{BytesCodec, FramedRead};
let mut file_parts = vec![];
for path in paths {
let file = File::open(&path)
.await
.map_err(|error| APIError::FileError(error.to_string()))?;
let stream = FramedRead::new(file, BytesCodec::new());
let file_body = reqwest::Body::wrap_stream(stream);
let file_part = reqwest::multipart::Part::stream(file_body)
.file_name(path)
.mime_str("application/octet-stream")
.unwrap();
file_parts.push(file_part);
}
Ok(file_parts)
}
}
}
}
impl Default for FileUpload {
fn default() -> Self {
Self::Bytes(FileUploadBytes::new(Bytes::new(), ""))
}
}
pub(crate) fn default_created() -> u32 {
use std::time::{SystemTime, UNIX_EPOCH};
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs() as u32
}