use crate::{catalog::Model, serenity, store::State}; pub fn no_mentions() -> serenity::CreateAllowedMentions { serenity::CreateAllowedMentions::new() .all_users(false) .all_roles(false) .everyone(false) .replied_user(false) } pub fn text(value: &str, limit: usize) -> String { if limit == 0 { return String::new(); } let mut result = String::new(); let mut length = 0; for ch in value.chars().filter(|ch| !ch.is_control() || *ch == '\n') { let escape = matches!( ch, '\\' | '`' | '*' | '_' | '~' | '[' | ']' | '<' | '>' | '|' | '#' | '-' ); let width = ch.len_utf16() + usize::from(escape); if length + width > limit.saturating_sub(1) { result.push('…'); break; } if escape { result.push('\\'); } result.push(ch); length += width; } if result.is_empty() { "—".into() } else { result } } pub fn embed(title: &str) -> serenity::CreateEmbed { serenity::CreateEmbed::new() .title(text(title, 256)) .color(0x8367ef) .footer(serenity::CreateEmbedFooter::new( "Model Radar • Catalog prices, not unlimited access • Limits may apply", )) } pub struct Browse { pub free_only: bool, pub search: String, pub min_context: u32, pub page: u32, pub page_size: u32, pub compact: bool, } impl Default for Browse { fn default() -> Self { Self { free_only: true, search: String::new(), min_context: 0, page: 1, page_size: 10, compact: false, } } } pub fn model_line(model: &Model, compact: bool) -> String { let pricing = if model.is_free() { "Free" } else { "Paid / unknown" }; if compact { format!( "{} · {} tokens · {pricing}", text(&model.id, 220), model.context_length ) } else { format!( "**{}**\n{} · {} tokens · {pricing}", text(&model.name, 85), text(&model.id, 220), model.context_length ) } } pub fn snapshot(state: &State, options: &Browse, poll_seconds: u64) -> serenity::CreateEmbed { let query = options.search.to_lowercase(); let matches: Vec<_> = state .catalog .values() .filter(|model| { (!options.free_only || model.is_free()) && model.context_length >= u64::from(options.min_context) && (model.id.to_lowercase().contains(&query) || model.name.to_lowercase().contains(&query)) }) .collect(); let size = options.page_size.clamp(1, 10) as usize; let pages = matches.len().div_ceil(size).max(1); let page = (options.page as usize).clamp(1, pages); let lines = matches .iter() .skip((page - 1) * size) .take(size) .map(|model| model_line(model, options.compact)) .collect::>() .join("\n\n"); let freshness = state .last_success .map(|time| format!("Last successful catalog fetch: ().")) .unwrap_or_else(|| "No successful catalog fetch recorded.".into()); embed("Catalog snapshot") .description(format!("Snapshot only; not live updating. Catalog freshness, not bot uptime or health.\n{freshness}\n\n{}", if lines.is_empty() { "No matching models." } else { &lines })) .field("Catalog", format!("{} total · {} free · {} matching", state.catalog.len(), state.catalog.values().filter(|model| model.is_free()).count(), matches.len()), false) .field("Filters", format!("Scope: {}\nSearch: {}\nMinimum context: {} tokens\nCompact: {}", if options.free_only { "Free only" } else { "All models" }, text(&options.search, 240), options.min_context, options.compact), false) .field("Page", format!("{page}/{pages} · {size} per page"), true) .field("Poll interval", format!("{poll_seconds} seconds · run /status again to refresh this snapshot"), false) } pub fn model_details(model: &Model, compact: bool, show_pricing: bool) -> serenity::CreateEmbed { let mut result = embed(&model.name) .field("Model ID", text(&model.id, 1024), false) .field("Context", format!("{} tokens", model.context_length), true) .field( "Free catalog pricing", if model.is_free() { "Yes" } else { "No / unknown" }, true, ); if !compact { result = result.description(text(&model.description, 2000)); } if show_pricing { let prices = model .pricing .iter() .take(10) .map(|(key, value)| format!("{}: {}", text(key, 40), text(value, 45))) .collect::>() .join("\n"); result = result.field( "Raw USD prices (prompt/completion per token)", if prices.is_empty() { "Unavailable" } else { &prices }, false, ); } result } #[cfg(test)] pub mod tests { use super::*; use std::collections::BTreeMap; pub fn assert_embed_bounds(value: &serde_json::Value) { let units = |value: &serde_json::Value| value.as_str().unwrap_or_default().encode_utf16().count(); assert!(units(&value["title"]) <= 256); assert!(units(&value["description"]) <= 4096); assert!(units(&value["footer"]["text"]) <= 2048); let mut total = units(&value["title"]) + units(&value["description"]) + units(&value["footer"]["text"]); if let Some(fields) = value["fields"].as_array() { assert!(fields.len() <= 25); for field in fields { assert!((1..=256).contains(&units(&field["name"]))); assert!((1..=1024).contains(&units(&field["value"]))); total += units(&field["name"]) + units(&field["value"]); } } assert!(total <= 6000, "embed has {total} UTF-16 units"); } fn model(id: &str, free: bool, context: u64) -> Model { Model { id: id.into(), name: "Example".into(), description: "Description".into(), context_length: context, pricing: BTreeMap::from([ ("prompt".into(), if free { "0" } else { "1" }.into()), ("completion".into(), "0".into()), ]), } } #[test] fn markdown_escaping_preserves_id_characters() { assert_eq!( text("vendor/model_under`tick", 1024), "vendor/model\\_under\\`tick" ); assert_eq!(text("a\\b[*]", 1024), "a\\\\b\\[\\*\\]"); let payload = serde_json::to_value(model_details( &model("vendor/model_under`tick", true, 8192), false, true, )) .unwrap(); assert_eq!(payload["fields"][0]["value"], "vendor/model\\_under\\`tick"); assert_embed_bounds(&payload); } #[test] fn unicode_lengths_and_all_payloads_are_bounded() { let large = "\u{1d11e}_`\\[]".repeat(2000); for limit in [0, 1, 2, 3, 40, 256, 1024, 2000] { assert!(text(&large, limit).encode_utf16().count() <= limit); } let mut example = model(&large, false, u64::MAX); example.name = large.clone(); example.description = large.clone(); example.pricing = (0..30) .map(|index| (format!("{index}{large}"), large.clone())) .collect(); for compact in [false, true] { for show_pricing in [false, true] { let value = serde_json::to_value(model_details(&example, compact, show_pricing)).unwrap(); assert_embed_bounds(&value); assert_eq!( value["fields"].as_array().unwrap().len(), if show_pricing { 4 } else { 3 } ); assert_eq!(value.get("description").is_none(), compact); } let state = State { catalog: (0..30) .map(|index| (index.to_string(), example.clone())) .collect(), last_success: Some(u64::MAX), ..Default::default() }; for page_size in [0, 1, 10, u32::MAX] { let options = Browse { free_only: false, compact, page_size, ..Default::default() }; assert_embed_bounds( &serde_json::to_value(snapshot(&state, &options, 86400)).unwrap(), ); } } } #[test] fn snapshot_filters_pagination_and_freshness() { let state = State { catalog: [ model("a/free", true, 100), model("b/paid", false, 200), model("c/free", true, 50), ] .into_iter() .map(|model| (model.id.clone(), model)) .collect(), last_success: Some(123), ..Default::default() }; let value = serde_json::to_value(snapshot(&state, &Browse::default(), 60)).unwrap(); let description = value["description"].as_str().unwrap(); assert!(description.contains("not live updating")); assert!(description.contains("not bot uptime or health")); assert!(description.contains("")); assert!(!description.contains("b/paid")); assert!(description.contains("a/free") && description.contains("c/free")); let options = Browse { free_only: false, search: "PAID".into(), min_context: 200, page: u32::MAX, page_size: 1, compact: true, }; let value = serde_json::to_value(snapshot(&state, &options, 75)).unwrap(); assert!(value["description"].as_str().unwrap().contains("b/paid")); assert_eq!(value["fields"][2]["value"], "1/1 · 1 per page"); assert!( value["fields"][3]["value"] .as_str() .unwrap() .contains("75 seconds") ); let options = Browse { min_context: 201, ..options }; let value = serde_json::to_value(snapshot(&state, &options, 600)).unwrap(); assert!( value["description"] .as_str() .unwrap() .contains("No matching models.") ); let value = serde_json::to_value(snapshot(&State::default(), &Browse::default(), 600)).unwrap(); assert!( value["description"] .as_str() .unwrap() .contains("No successful catalog fetch") ); let options = Browse { free_only: false, page_size: 1, page: 2, ..Default::default() }; let value = serde_json::to_value(snapshot(&state, &options, 600)).unwrap(); assert!(value["description"].as_str().unwrap().contains("b/paid")); assert!(!value["description"].as_str().unwrap().contains("a/free")); assert_eq!(value["fields"][2]["value"], "2/3 · 1 per page"); } }