Skip to main content

kcode_openai_api/
error.rs

1use std::{error::Error as StdError, fmt};
2
3/// The result type returned by this crate.
4pub type Result<T> = std::result::Result<T, Error>;
5
6/// A local validation, transport, provider, or protocol failure.
7#[derive(Debug)]
8pub enum Error {
9    /// The API key was empty or invalid as an HTTP header.
10    InvalidApiKey,
11    /// The caller supplied an invalid request.
12    InvalidInput(String),
13    /// The OpenAI service could not be reached or timed out.
14    Transport(String),
15    /// OpenAI rejected the request.
16    Provider {
17        /// HTTP status returned by OpenAI.
18        status: u16,
19        /// Sanitized provider error code, when present.
20        code: Option<String>,
21        /// Sanitized provider message.
22        message: String,
23        /// OpenAI request identifier, when returned.
24        request_id: Option<String>,
25    },
26    /// A successful HTTP response did not satisfy the expected protocol.
27    Protocol(String),
28}
29
30impl fmt::Display for Error {
31    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32        match self {
33            Self::InvalidApiKey => f.write_str("the OpenAI API key is empty or invalid"),
34            Self::InvalidInput(message) => write!(f, "invalid OpenAI request: {message}"),
35            Self::Transport(message) => write!(f, "OpenAI transport failed: {message}"),
36            Self::Provider {
37                status,
38                code,
39                message,
40                request_id,
41            } => {
42                write!(f, "OpenAI returned HTTP {status}")?;
43                if let Some(code) = code {
44                    write!(f, " ({code})")?;
45                }
46                write!(f, ": {message}")?;
47                if let Some(request_id) = request_id {
48                    write!(f, " [request_id: {request_id}]")?;
49                }
50                Ok(())
51            }
52            Self::Protocol(message) => write!(f, "invalid OpenAI response: {message}"),
53        }
54    }
55}
56
57impl StdError for Error {}
58
59pub(crate) fn transport(error: impl fmt::Display) -> Error {
60    let message = error.to_string();
61    Error::Transport(if message.to_ascii_lowercase().contains("timed out") {
62        "the request timed out".into()
63    } else {
64        "the service could not be reached".into()
65    })
66}
67
68pub(crate) fn clean_message(value: &str, limit: usize) -> String {
69    let value = value
70        .chars()
71        .map(|character| {
72            if character.is_control() {
73                ' '
74            } else {
75                character
76            }
77        })
78        .take(limit)
79        .collect::<String>()
80        .split_whitespace()
81        .collect::<Vec<_>>()
82        .join(" ");
83    if value.is_empty() {
84        "unknown error".into()
85    } else {
86        value
87    }
88}