use once_cell::sync::Lazy;
use regex::Regex;
static MODEL_PATH_RE: Lazy<Regex> = Lazy::new(|| {
Regex::new(r"^projects/[^/]+/locations/[^/]+/publishers/[^/]+/models/(.+)$").unwrap()
});
static GEMINI_VERSION_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"^gemini-(\d+)").unwrap());
pub fn extract_model_name(model_string: &str) -> &str {
MODEL_PATH_RE
.captures(model_string)
.and_then(|caps| caps.get(1))
.map(|m| m.as_str())
.unwrap_or(model_string)
}
pub fn is_gemini_model(model_string: &str) -> bool {
extract_model_name(model_string).starts_with("gemini-")
}
pub fn is_gemini1_model(model_string: &str) -> bool {
extract_model_name(model_string).starts_with("gemini-1")
}
pub fn is_gemini2_or_above(model_string: &str) -> bool {
let name = extract_model_name(model_string);
GEMINI_VERSION_RE
.captures(name)
.and_then(|caps| caps.get(1))
.and_then(|m| m.as_str().parse::<u32>().ok())
.map(|major| major >= 2)
.unwrap_or(false)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn extract_from_full_path() {
assert_eq!(
extract_model_name(
"projects/my-proj/locations/us-central1/publishers/google/models/gemini-2.5-flash"
),
"gemini-2.5-flash"
);
}
#[test]
fn extract_from_simple_name() {
assert_eq!(extract_model_name("gemini-2.5-flash"), "gemini-2.5-flash");
}
#[test]
fn extract_preserves_suffixes() {
assert_eq!(
extract_model_name("projects/p/locations/l/publishers/pub/models/gemini-1.5-pro-002"),
"gemini-1.5-pro-002"
);
}
#[test]
fn extract_non_gemini_full_path() {
assert_eq!(
extract_model_name("projects/p/locations/l/publishers/pub/models/custom-model"),
"custom-model"
);
}
#[test]
fn extract_empty_string() {
assert_eq!(extract_model_name(""), "");
}
#[test]
fn gemini_model_simple() {
assert!(is_gemini_model("gemini-2.5-flash"));
assert!(is_gemini_model("gemini-1.5-pro"));
}
#[test]
fn non_gemini_model() {
assert!(!is_gemini_model("claude-3-opus"));
assert!(!is_gemini_model("gpt-4"));
assert!(!is_gemini_model("custom-model"));
}
#[test]
fn gemini_model_full_path() {
assert!(is_gemini_model(
"projects/p/locations/l/publishers/pub/models/gemini-2.5-flash"
));
}
#[test]
fn gemini1_models() {
assert!(is_gemini1_model("gemini-1.5-pro"));
assert!(is_gemini1_model("gemini-1.5-flash"));
assert!(is_gemini1_model("gemini-1.0-pro"));
}
#[test]
fn gemini1_full_path() {
assert!(is_gemini1_model(
"projects/p/locations/l/publishers/pub/models/gemini-1.5-pro-002"
));
}
#[test]
fn gemini2_not_gemini1() {
assert!(!is_gemini1_model("gemini-2.5-flash"));
assert!(!is_gemini1_model("gemini-2.0-flash"));
}
#[test]
fn non_gemini_not_gemini1() {
assert!(!is_gemini1_model("gpt-4"));
}
#[test]
fn gemini2_or_above_positive() {
assert!(is_gemini2_or_above("gemini-2.5-flash"));
assert!(is_gemini2_or_above("gemini-2.0-flash"));
assert!(is_gemini2_or_above("gemini-3.0-ultra"));
}
#[test]
fn gemini2_or_above_negative() {
assert!(!is_gemini2_or_above("gemini-1.5-pro"));
assert!(!is_gemini2_or_above("gemini-1.0-pro"));
}
#[test]
fn gemini2_or_above_full_path() {
assert!(is_gemini2_or_above(
"projects/p/locations/l/publishers/pub/models/gemini-2.5-flash"
));
}
#[test]
fn gemini2_or_above_non_gemini() {
assert!(!is_gemini2_or_above("custom-model"));
assert!(!is_gemini2_or_above("gpt-4"));
}
#[test]
fn gemini2_or_above_edge_cases() {
assert!(!is_gemini2_or_above(""));
assert!(!is_gemini2_or_above("gemini-"));
}
}