use serde_json::{Map, Value};
use crate::canonical::{CanonicalError, ErrorKind, Model};
use crate::protocol::{ModelKeys, WireRequest};
pub(crate) fn decode_models(data: &[u8], keys: &ModelKeys) -> Result<Vec<Model>, CanonicalError> {
let v: Value = serde_json::from_slice(data).map_err(|e| models_error(&e.to_string()))?;
let entries = v[keys.array_key]
.as_array()
.ok_or_else(|| models_error(&format!("models body has no `{}` array", keys.array_key)))?;
Ok(entries
.iter()
.filter_map(|e| {
let id = e[keys.id_key].as_str()?;
Some(Model {
id: id.strip_prefix(keys.strip).unwrap_or(id).to_owned(),
default: false,
context_window: opt_u32(e, keys.context_key),
max_output_tokens: opt_u32(e, keys.max_output_key),
display_name: opt_str(e, keys.display_name_key),
})
})
.collect())
}
fn opt_u32(v: &Value, key: &str) -> Option<u32> {
v[key].as_u64().and_then(|n| u32::try_from(n).ok())
}
fn opt_str(v: &Value, key: &str) -> Option<String> {
v[key].as_str().map(str::to_owned)
}
fn models_error(detail: &str) -> CanonicalError {
CanonicalError {
kind: ErrorKind::Provider { status: 502 },
message: format!("malformed models list: {detail}"),
provider_detail: None,
retry_after_seconds: None,
}
}
pub(crate) fn count_from_body(data: &[u8], key: &str) -> Result<u32, CanonicalError> {
let v: Value = serde_json::from_slice(data).map_err(|e| count_error(&e.to_string()))?;
v[key]
.as_u64()
.map(|n| n as u32)
.ok_or_else(|| count_error(&format!("count body has no `{key}` number")))
}
fn count_error(detail: &str) -> CanonicalError {
CanonicalError {
kind: ErrorKind::Provider { status: 502 },
message: format!("malformed token count: {detail}"),
provider_detail: None,
retry_after_seconds: None,
}
}
pub(crate) fn parse(data: &[u8]) -> Result<Value, CanonicalError> {
serde_json::from_slice(data).map_err(|e| CanonicalError {
kind: ErrorKind::Transport,
message: e.to_string(),
provider_detail: None,
retry_after_seconds: None,
})
}
pub(crate) fn text_of(v: &Value, key: &str) -> String {
v[key].as_str().unwrap_or_default().to_owned()
}
pub(crate) fn nonempty(v: &Value) -> Option<&str> {
v.as_str().filter(|s| !s.is_empty())
}
pub(crate) fn u32_at(v: &Value, key: &str) -> u32 {
v[key].as_u64().unwrap_or(0) as u32
}
pub(crate) fn to_json_string(v: &Value) -> String {
#[allow(clippy::expect_used)]
serde_json::to_string(v).expect("a serde_json::Value re-serializes infallibly")
}
pub(crate) fn finish_body(body: Map<String, Value>, url: String) -> WireRequest {
#[allow(clippy::expect_used)]
let bytes = serde_json::to_vec(&body).expect("request body is infallibly serializable");
WireRequest::new(url, bytes)
}
pub(crate) fn http_error(data: &[u8], status: u16) -> CanonicalError {
let kind = ErrorKind::from_http_status(status);
let (message, provider_detail) = match serde_json::from_slice::<Value>(data) {
Ok(body) => (error_message(&body), Some(body)),
Err(_) => {
let raw = String::from_utf8_lossy(data).trim().to_owned();
if raw.is_empty() {
(String::new(), None)
} else {
(raw.clone(), Some(Value::String(raw)))
}
}
};
CanonicalError {
kind,
message,
provider_detail,
retry_after_seconds: None,
}
}
fn error_message(body: &Value) -> String {
body["error"]["message"]
.as_str()
.or_else(|| body["error"].as_str())
.or_else(|| body["detail"].as_str())
.map(str::to_owned)
.unwrap_or_else(|| to_json_string(body))
}