kcode-openai-api 0.1.2

OpenAI transcription, image analysis, and GPT Image generation
Documentation
use std::{error::Error as StdError, fmt};

/// The result type returned by this crate.
pub type Result<T> = std::result::Result<T, Error>;

/// A local validation, transport, provider, or protocol failure.
#[derive(Debug)]
pub enum Error {
    /// The API key was empty or invalid as an HTTP header.
    InvalidApiKey,
    /// The caller supplied an invalid request.
    InvalidInput(String),
    /// The OpenAI service could not be reached or timed out.
    Transport(String),
    /// OpenAI rejected the request.
    Provider {
        /// HTTP status returned by OpenAI.
        status: u16,
        /// Sanitized provider error code, when present.
        code: Option<String>,
        /// Sanitized provider message.
        message: String,
        /// OpenAI request identifier, when returned.
        request_id: Option<String>,
    },
    /// A successful HTTP response did not satisfy the expected protocol.
    Protocol(String),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::InvalidApiKey => f.write_str("the OpenAI API key is empty or invalid"),
            Self::InvalidInput(message) => write!(f, "invalid OpenAI request: {message}"),
            Self::Transport(message) => write!(f, "OpenAI transport failed: {message}"),
            Self::Provider {
                status,
                code,
                message,
                request_id,
            } => {
                write!(f, "OpenAI returned HTTP {status}")?;
                if let Some(code) = code {
                    write!(f, " ({code})")?;
                }
                write!(f, ": {message}")?;
                if let Some(request_id) = request_id {
                    write!(f, " [request_id: {request_id}]")?;
                }
                Ok(())
            }
            Self::Protocol(message) => write!(f, "invalid OpenAI response: {message}"),
        }
    }
}

impl StdError for Error {}

pub(crate) fn transport(error: impl fmt::Display) -> Error {
    let message = error.to_string();
    Error::Transport(if message.to_ascii_lowercase().contains("timed out") {
        "the request timed out".into()
    } else {
        "the service could not be reached".into()
    })
}

pub(crate) fn clean_message(value: &str, limit: usize) -> String {
    let value = value
        .chars()
        .map(|character| {
            if character.is_control() {
                ' '
            } else {
                character
            }
        })
        .take(limit)
        .collect::<String>()
        .split_whitespace()
        .collect::<Vec<_>>()
        .join(" ");
    if value.is_empty() {
        "unknown error".into()
    } else {
        value
    }
}