const NON_CHAT_MARKERS: &[&str] = &[
"embedding",
"embed",
"bge-",
"gte-",
"e5-",
"rerank",
"whisper",
"sensevoice",
"cosyvoice",
"tts",
"asr",
"stable-diffusion",
"sdxl",
"kolors",
"flux",
"image",
"captioner",
"ocr",
"moderation",
"guard",
];
pub fn is_chat_model_id(id: &str) -> bool {
let s = id.trim().to_ascii_lowercase();
if s.is_empty() {
return false;
}
if s.starts_with("lora/") {
return false;
}
!NON_CHAT_MARKERS.iter().any(|m| s.contains(m))
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct CleanedModels {
pub models: Vec<String>,
pub dropped: usize,
pub dropped_models: Vec<String>,
}
pub fn clean_fetched_models<I, S>(ids: I) -> CleanedModels
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
let mut unique: Vec<String> = Vec::new();
for id in ids {
let t = id.as_ref().trim();
if t.is_empty() {
continue;
}
if !unique.iter().any(|x| x == t) {
unique.push(t.to_string());
}
}
let kept: Vec<String> = unique
.iter()
.filter(|s| is_chat_model_id(s))
.cloned()
.collect();
if kept.is_empty() {
return CleanedModels {
models: unique,
dropped: 0,
dropped_models: Vec::new(),
};
}
let dropped_models: Vec<String> = unique
.into_iter()
.filter(|s| !is_chat_model_id(s))
.collect();
CleanedModels {
models: kept,
dropped: dropped_models.len(),
dropped_models,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn keeps_chat_models_and_drops_others() {
let r = clean_fetched_models([
"deepseek-flash",
"BAAI/bge-large-zh-v1.5",
"Qwen/Qwen3-VL-32B-Instruct", "FunAudioLLM/CosyVoice2-0.5B",
"Kwai-Kolors/Kolors",
]);
assert_eq!(
r.models,
vec!["deepseek-flash", "Qwen/Qwen3-VL-32B-Instruct"]
);
assert_eq!(r.dropped, 3);
assert_eq!(
r.dropped_models,
vec![
"BAAI/bge-large-zh-v1.5",
"FunAudioLLM/CosyVoice2-0.5B",
"Kwai-Kolors/Kolors"
],
"被滤掉的 id 要原样带回,顺序同端点"
);
}
#[test]
fn dedups_before_filtering() {
let r = clean_fetched_models(["gpt-4o", " gpt-4o ", "gpt-4o"]);
assert_eq!(r.models, vec!["gpt-4o"]);
assert_eq!(r.dropped, 0);
}
#[test]
fn returns_original_when_everything_filtered() {
let r = clean_fetched_models(["bge-m3", "text-embedding-3-large"]);
assert_eq!(r.models.len(), 2, "宁可摆出原始清单,也不能给空下拉");
assert_eq!(r.dropped, 0);
assert!(r.dropped_models.is_empty(), "已放回 models,不能再算一遍");
}
#[test]
fn multimodal_chat_models_are_not_dropped() {
assert!(is_chat_model_id("Qwen/Qwen3-VL-32B-Instruct"));
assert!(is_chat_model_id("Qwen3-Omni-30B-A3B-Instruct"));
assert!(!is_chat_model_id("Qwen3-Omni-30B-A3B-Captioner"));
assert!(!is_chat_model_id("Qwen/Qwen-Image-Edit-2509"));
}
}