atman_runtime/providers/
mod.rs1pub mod anthropic;
2pub mod codex;
3pub mod mock;
4pub mod openai;
5
6pub(crate) fn classify_attachment_error(status: u16, body: &str) -> Option<String> {
7 if status == 413 {
8 return Some("payload_too_large".into());
9 }
10 let lower = body.to_ascii_lowercase();
11 let attachment_markers = [
12 "image",
13 "attachment",
14 "media_type",
15 "unsupported",
16 "invalid image",
17 "image_parse_error",
18 "invalid_image_url",
19 "request_too_large",
20 ];
21 let has_marker = attachment_markers.iter().any(|m| lower.contains(m));
22 let is_size_signal = lower.contains("too large") || lower.contains("payload_too_large");
23 if status == 400 && has_marker {
24 return Some(pick_reason(&lower));
25 }
26 if is_size_signal {
27 return Some("payload_too_large".into());
28 }
29 None
30}
31
32fn pick_reason(lower: &str) -> String {
33 if lower.contains("invalid_image_url") {
34 "invalid_image_url".into()
35 } else if lower.contains("image_parse_error") {
36 "image_parse_error".into()
37 } else if lower.contains("unsupported") {
38 "unsupported_media_type".into()
39 } else if lower.contains("too large") {
40 "payload_too_large".into()
41 } else {
42 "invalid_image".into()
43 }
44}
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49
50 #[test]
51 fn http_413_maps_to_payload_too_large() {
52 let r = classify_attachment_error(413, "").unwrap();
53 assert_eq!(r, "payload_too_large");
54 }
55
56 #[test]
57 fn openai_invalid_image_url_recognized() {
58 let body = r#"{"error":{"code":"invalid_image_url","message":"..."}}"#;
59 let r = classify_attachment_error(400, body).unwrap();
60 assert_eq!(r, "invalid_image_url");
61 }
62
63 #[test]
64 fn anthropic_image_error_recognized() {
65 let body = "unsupported media_type for image";
66 let r = classify_attachment_error(400, body).unwrap();
67 assert_eq!(r, "unsupported_media_type");
68 }
69
70 #[test]
71 fn non_attachment_error_returns_none() {
72 assert!(classify_attachment_error(500, "internal").is_none());
73 assert!(classify_attachment_error(401, "unauthorized").is_none());
74 }
75}