Skip to main content

ai_provider_sdk/
error.rs

1//! SDK 错误模型。统一承载配置错误、网络错误、超时错误与 API 状态错误。
2
3use reqwest::StatusCode;
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6use thiserror::Error;
7
8pub type Result<T> = std::result::Result<T, Error>;
9
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
11pub struct ApiErrorBody {
12    pub message: String,
13    #[serde(flatten)]
14    pub extra: Value,
15}
16
17#[derive(Debug, Error)]
18/// SDK 统一错误类型。
19///
20/// 设计要点:
21/// - `ApiStatus` 保留状态码、请求 ID 与原始错误体,便于上层诊断。
22/// - 网络层异常与协议层异常分离,避免错误语义混淆。
23pub enum Error {
24    #[error("{message}")]
25    ApiStatus {
26        message: String,
27        status: StatusCode,
28        request_id: Option<String>,
29        body: Option<Value>,
30    },
31    #[error("request timed out")]
32    Timeout,
33    #[error("connection error: {0}")]
34    Connection(String),
35    #[error("configuration error: {0}")]
36    Config(String),
37    #[error("invalid URL: {0}")]
38    Url(#[from] url::ParseError),
39    #[error("invalid header value: {0}")]
40    HeaderValue(#[from] http::header::InvalidHeaderValue),
41    #[error("invalid JSON: {0}")]
42    Json(#[from] serde_json::Error),
43    #[error("I/O error: {0}")]
44    Io(#[from] std::io::Error),
45    #[error("SSE stream error: {0}")]
46    Stream(String),
47}
48
49impl Error {
50    pub fn api_status(status: StatusCode, request_id: Option<String>, body: Option<Value>) -> Self {
51        let message = body
52            .as_ref()
53            .and_then(extract_error_message)
54            .unwrap_or_else(|| format!("OpenAI API returned status {status}"));
55
56        Self::ApiStatus {
57            message,
58            status,
59            request_id,
60            body,
61        }
62    }
63}
64
65fn extract_error_message(body: &Value) -> Option<String> {
66    body.get("error")
67        .and_then(|error| error.get("message"))
68        .and_then(Value::as_str)
69        .or_else(|| body.get("message").and_then(Value::as_str))
70        .map(ToOwned::to_owned)
71}