use serde_json::{Value, json};
pub const OPENCRABS_BASE_URL: &str = "https://inference.opencrabs.com/v1";
pub const OPENCRABS_CONTEXT_WINDOW: u32 = 200_000;
const OPENCRABS_HOST: &str = "inference.opencrabs.com";
pub fn is_opencrabs_endpoint(base_url: Option<&str>) -> bool {
base_url
.map(str::trim)
.filter(|s| !s.is_empty())
.is_some_and(|url| url.to_ascii_lowercase().contains(OPENCRABS_HOST))
}
pub const INSTALL_PATH: &str = "/install";
const INSTALL_ID_LEN: usize = 32;
#[derive(Clone, PartialEq, Eq)]
pub struct IssuedToken {
pub token: String,
pub label: String,
}
impl std::fmt::Debug for IssuedToken {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("IssuedToken")
.field("token", &"<redacted>")
.field("label", &self.label)
.finish()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum InstallError {
IdRejected,
AlreadyIssued,
IssuanceOff,
RateLimited,
ProxyFailed,
Unexpected(u16),
MalformedReply,
Unreachable,
StorageFailed,
}
pub fn new_install_id() -> String {
let mut id = String::with_capacity(INSTALL_ID_LEN);
while id.len() < INSTALL_ID_LEN {
id.push_str(&format!("{:016x}", rand::random::<u64>()));
}
id.truncate(INSTALL_ID_LEN);
id
}
pub fn is_valid_install_id(id: &str) -> bool {
(8..=64).contains(&id.len())
&& id
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
}
pub fn install_request_body(install_id: &str) -> Value {
json!({ "install_id": install_id })
}
pub fn parse_install_response(status: u16, body: &str) -> Result<IssuedToken, InstallError> {
match status {
200 => {
let value: Value =
serde_json::from_str(body).map_err(|_| InstallError::MalformedReply)?;
let token = value
.get("token")
.and_then(Value::as_str)
.filter(|t| !t.is_empty())
.ok_or(InstallError::MalformedReply)?;
let label = value
.get("label")
.and_then(Value::as_str)
.unwrap_or_default();
Ok(IssuedToken {
token: token.to_string(),
label: label.to_string(),
})
}
400 => Err(InstallError::IdRejected),
403 | 404 => Err(InstallError::IssuanceOff),
409 => Err(InstallError::AlreadyIssued),
429 => Err(InstallError::RateLimited),
500..=599 => Err(InstallError::ProxyFailed),
other => Err(InstallError::Unexpected(other)),
}
}
pub fn install_error_message(err: &InstallError) -> &'static str {
match err {
InstallError::IdRejected => {
"The built-in OpenCrabs provider could not register this install: the install ID was rejected. Please report this."
}
InstallError::AlreadyIssued => {
"The built-in OpenCrabs provider already issued a token for this install. Generating a new install ID."
}
InstallError::IssuanceOff => {
"The built-in OpenCrabs provider is not issuing tokens right now. Choose another provider in Advanced setup."
}
InstallError::RateLimited => {
"The built-in OpenCrabs provider is rate limiting this connection. Wait a minute and try again."
}
InstallError::ProxyFailed => {
"The built-in OpenCrabs provider failed while issuing a token. Try again later or choose another provider."
}
InstallError::Unexpected(_) => {
"The built-in OpenCrabs provider gave an unexpected answer. Try again later or choose another provider."
}
InstallError::MalformedReply => {
"The built-in OpenCrabs provider sent a reply without a token. Try again later or choose another provider."
}
InstallError::Unreachable => {
"Could not reach the built-in OpenCrabs provider. Check your connection, or choose another provider."
}
InstallError::StorageFailed => {
"The built-in OpenCrabs provider issued a token, but it could not be saved to keys.toml. Check the file permissions and try again."
}
}
}