use serde_json::Value;
use super::wire::{CODE, MESSAGE, TYPE};
const NAMES: [&str; 3] = [
"context_length_exceeded",
"context_window_exceeded",
"exceed_context_size_error",
];
const PHRASES: [&str; 5] = [
"context length",
"context size",
"context window",
"context budget",
"compacted context",
];
pub(crate) fn is_overflow(error: &Value) -> bool {
let field = |key: &str| error.get(key).and_then(Value::as_str);
let named = [field(CODE), field(TYPE)]
.into_iter()
.flatten()
.any(|name| NAMES.contains(&name));
named
|| field(MESSAGE)
.or_else(|| error.as_str())
.is_some_and(says_overflow)
}
pub(crate) fn says_overflow(message: &str) -> bool {
let message = message.to_ascii_lowercase();
PHRASES.iter().any(|phrase| message.contains(phrase))
}
#[cfg(test)]
mod tests {
use serde_json::json;
use super::*;
#[test]
fn a_code_or_a_type_names_an_overflow() {
assert!(is_overflow(&json!({"code": "context_length_exceeded"})));
assert!(is_overflow(&json!({"code": "context_window_exceeded"})));
assert!(is_overflow(
&json!({"code": 400, "type": "exceed_context_size_error"})
));
assert!(!is_overflow(&json!({"code": "unsupported_parameter"})));
assert!(!is_overflow(
&json!({"code": 400, "type": "invalid_request_error"})
));
}
#[test]
fn a_message_says_it_in_the_words_servers_use() {
for message in [
"This model's maximum context length is 8192 tokens.",
"The number of tokens to keep from the initial prompt is greater than the context length.",
"the request exceeds the available context size, try increasing it",
"Input exceeds the Context Window of this model",
"Protected conversation exceeds the available context budget.",
"Output reservation leaves no room for compacted context.",
] {
assert!(is_overflow(&json!({"message": message})), "{message}");
assert!(is_overflow(&json!(message)), "{message}");
}
for message in ["engine died", "max_tokens is too large", ""] {
assert!(!is_overflow(&json!({"message": message})), "{message}");
}
assert!(!is_overflow(&json!({})));
assert!(!is_overflow(&json!(null)));
}
}