use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::{
errors::{AUTH_FAILED, INVALID_ITEM, MODEL_UNAVAILABLE, PROVIDER_ERROR, RATE_LIMITED},
question::{Image, Question},
reply::{Answer, Usage},
};
pub mod catalog_ids {
pub const CLEF: &str = "cloudflare-workers-ai/@cf/cloudflare/clef";
pub const CLEF_FLASH: &str = "cloudflare-workers-ai/@cf/cloudflare/clef-flash";
pub const JEV_1_13_0: &str = "typesafe/jev-1.13.0";
}
pub mod models {
pub const CLEF: &str = "clef";
pub const CLEF_FLASH: &str = "clef-flash";
pub const JEV_LATEST: &str = "jev-latest";
}
pub mod hosts {
pub const CLOUDFLARE: &str = "api.cloudflare.com";
pub const TYPESAFE: &str = "api.typesafe.ai";
}
pub const TYPESAFE_ENDPOINT: &str = "https://api.typesafe.ai/v1/systemone";
pub mod clef_limits {
pub const MIN_QUESTIONS: usize = 1;
pub const MAX_QUESTIONS: usize = 64;
pub const MAX_QUESTION_ID_LEN: usize = 100;
pub const MAX_IMAGES: usize = 4;
pub const MAX_BODY_BYTES: usize = 13 * 1024 * 1024;
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[non_exhaustive]
pub struct ProviderRequest {
pub model: String,
pub state: Value,
pub questions: IndexMap<String, Question>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub images: Option<Vec<Image>>,
}
impl ProviderRequest {
pub fn new(
model: impl Into<String>,
state: impl Into<Value>,
questions: IndexMap<String, Question>,
) -> Self {
Self {
model: model.into(),
state: state.into(),
questions,
images: None,
}
}
pub fn with_images(mut self, images: Vec<Image>) -> Self {
self.images = Some(images);
self
}
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[non_exhaustive]
pub struct ProviderResponse {
pub model: String,
pub answers: IndexMap<String, Answer>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub usage: Option<Usage>,
}
impl ProviderResponse {
pub fn new(model: impl Into<String>, answers: IndexMap<String, Answer>) -> Self {
Self {
model: model.into(),
answers,
usage: None,
}
}
pub fn with_usage(mut self, usage: Usage) -> Self {
self.usage = Some(usage);
self
}
}
pub fn item_code_for_status(status: u16) -> Option<&'static str> {
match status {
401 | 403 => Some(AUTH_FAILED),
404 => Some(MODEL_UNAVAILABLE),
429 | 529 => Some(RATE_LIMITED),
408 | 500..=599 => Some(PROVIDER_ERROR),
400..=499 => Some(INVALID_ITEM),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn documented_statuses_map_to_item_codes() {
for status in [429, 529] {
assert_eq!(item_code_for_status(status), Some(RATE_LIMITED));
}
for status in [408, 500, 502, 503] {
assert_eq!(item_code_for_status(status), Some(PROVIDER_ERROR));
}
for status in [400, 413, 422, 409] {
assert_eq!(item_code_for_status(status), Some(INVALID_ITEM));
}
for status in [401, 403] {
assert_eq!(item_code_for_status(status), Some(AUTH_FAILED));
}
assert_eq!(item_code_for_status(404), Some(MODEL_UNAVAILABLE));
assert_eq!(item_code_for_status(200), None);
}
}