| 238 | } |
| 239 | |
| 240 | pub fn parse_stream_error(data: &str) -> Option<ParsedStreamError> { |
| 241 | let body: serde_json::Value = serde_json::from_str(data).ok()?; |
| 242 | |
| 243 | if body.get("type")?.as_str()? != "error" { |
| 244 | return None; |
| 245 | } |
| 246 | |
| 247 | let error = body.get("error")?; |
| 248 | let code = error.get("code")?.as_str()?; |
| 249 | let response_body = serde_json::to_string(&body).unwrap_or_default(); |
| 250 | |
| 251 | match code { |
| 252 | "context_length_exceeded" => Some(ParsedStreamError::ContextOverflow { |
| 253 | message: "Input exceeds context window of this model".to_string(), |
| 254 | response_body, |
| 255 | }), |
| 256 | "insufficient_quota" => Some(ParsedStreamError::ApiError { |
| 257 | message: "Quota exceeded. Check your plan and billing details.".to_string(), |
| 258 | is_retryable: false, |
| 259 | response_body, |
| 260 | }), |
| 261 | "usage_not_included" => Some(ParsedStreamError::ApiError { |
| 262 | message: "To use Codex with your ChatGPT plan, upgrade to Plus: https://chatgpt.com/explore/plus.".to_string(), |
| 263 | is_retryable: false, |
| 264 | response_body, |
| 265 | }), |
| 266 | "invalid_prompt" => { |
| 267 | let msg = error |
| 268 | .get("message") |
| 269 | .and_then(|m| m.as_str()) |
| 270 | .unwrap_or("Invalid prompt.") |
| 271 | .to_string(); |
| 272 | Some(ParsedStreamError::ApiError { |
| 273 | message: msg, |
| 274 | is_retryable: false, |
| 275 | response_body, |
| 276 | }) |
| 277 | } |
| 278 | _ => None, |
| 279 | } |
| 280 | } |
| 281 | |
| 282 | pub struct ProviderRegistry { |
| 283 | providers: HashMap<String, Arc<dyn Provider>>, |