use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Plan {
VectorOnly,
Hybrid,
}
pub fn plan_query(text: &str) -> Plan {
if has_entity_like_capitalized_token(text) { Plan::Hybrid } else { Plan::VectorOnly }
}
fn has_entity_like_capitalized_token(text: &str) -> bool {
text.split_whitespace().enumerate().any(|(i, tok)| {
if i == 0 {
return false;
}
let first = tok.chars().find(|c| c.is_ascii_alphanumeric());
matches!(first, Some(c) if c.is_ascii_uppercase())
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn picks_hybrid_for_capitalized_entity_token() {
assert_eq!(plan_query("what did Alice do at Acme last April?"), Plan::Hybrid);
}
#[test]
fn picks_vector_only_for_lowercase_phrase() {
assert_eq!(plan_query("show me everything"), Plan::VectorOnly);
assert_eq!(plan_query("how can i find this thing"), Plan::VectorOnly);
}
#[test]
fn first_word_capitalization_is_not_a_signal() {
assert_eq!(plan_query("What is going on"), Plan::VectorOnly);
}
#[test]
fn punctuation_prefix_is_skipped() {
assert_eq!(plan_query("hello (Alice) how are you"), Plan::Hybrid);
}
#[test]
fn empty_query_is_vector_only() {
assert_eq!(plan_query(""), Plan::VectorOnly);
}
#[test]
fn cjk_query_always_plans_vector_only() {
assert_eq!(plan_query("爱丽丝在北京清华大学做了什么?"), Plan::VectorOnly);
assert_eq!(plan_query("田中さん は トヨタ で どこで 働いていますか"), Plan::VectorOnly);
assert_eq!(plan_query("삼성이 서울에서 무엇을 발표했나요?"), Plan::VectorOnly);
}
}