use super::finish_reason;
use crate::completion::FinishReason;
use crate::providers::openai::wire::{OPENAI, TOGETHER, ZAI};
use bytes::Bytes;
#[test]
fn sse_error_detector_handles_null_empty_and_object_or_string_errors() {
let detect = |data: &str| {
let value = serde_json::from_str(data).expect("test frames are JSON");
super::provider_error_envelope(&value, data)
};
assert!(detect(r#"{"error":null}"#).is_none());
assert!(detect(r#"{"error":null,"usage":{"total_tokens":3}}"#).is_none());
assert!(detect(r#"{"error":""}"#).is_none());
assert!(detect(r#"{"choices":[{"delta":{"content":"hi"}}]}"#).is_none());
assert!(detect(r#"{"error":"metadata","choices":[{"delta":{"content":"hi"}}]}"#).is_none());
assert!(
detect(r#"{"error":{"message":"x"},"choices":[{"delta":{"content":"hi"}}]}"#).is_none()
);
let string_body = r#"{"error":"oops"}"#;
let string_error = detect(string_body).expect("string error should be detected");
assert_eq!(string_error.provider_response_body(), Some(string_body));
assert_eq!(string_error.provider_response_status(), None);
let body = r#"{"error":{"message":"rate limited","type":"rate_limit_error"}}"#;
let error = detect(body).expect("object error envelope should be detected");
assert_eq!(error.provider_response_body(), Some(body));
assert_eq!(error.provider_response_status(), None);
let masked = r#"{"error":{"message":"rate limited"},"choices":[]}"#;
let error = detect(masked).expect("an empty choices array must not mask the error");
assert_eq!(error.provider_response_body(), Some(masked));
assert!(
detect(r#"{"error":{"message":"rate limited"},"choices":null}"#).is_some(),
"a null choices value must not mask the error"
);
}
#[test]
fn a_dialect_states_its_own_finish_vocabulary() {
assert_eq!(
finish_reason("model_context_window_exceeded", &ZAI.quirks),
FinishReason::Length
);
assert_eq!(
finish_reason("sensitive", &ZAI.quirks),
FinishReason::ContentFilter
);
assert_eq!(
finish_reason("model_context_window_exceeded", &OPENAI.quirks),
FinishReason::Other("model_context_window_exceeded".to_owned())
);
}
#[test]
fn compatible_servers_stop_spellings_are_shared() {
for reason in ["stop", "end", "eos", "end_turn", "stop_sequence"] {
for quirks in [&OPENAI.quirks, &TOGETHER.quirks, &ZAI.quirks] {
assert_eq!(
finish_reason(reason, quirks),
FinishReason::Stop,
"{reason}"
);
}
}
assert_eq!(
finish_reason("MALFORMED", &OPENAI.quirks),
FinishReason::Other("MALFORMED".to_owned())
);
}
pub(crate) fn sse_bytes_from_data_lines<T>(events: impl IntoIterator<Item = T>) -> Bytes
where
T: AsRef<str>,
{
Bytes::from(
events
.into_iter()
.map(|event| format!("data: {}\n\n", event.as_ref()))
.collect::<String>(),
)
}
pub(crate) fn sse_bytes_from_json_events(events: &[serde_json::Value]) -> Bytes {
Bytes::from(
events
.iter()
.map(|event| {
format!(
"data: {}\n\n",
serde_json::to_string(event).expect("event should serialize")
)
})
.collect::<String>(),
)
}