use crate::search::agent_classifier::AgentRoute;
pub(crate) const CLASSIFIER_SYSTEM_PROMPT: &str = "\
You are a code-search query classifier. Given a developer's query, pick the \
single best search strategy from the list below and respond with its name in \
snake_case. Respond with EXACTLY ONE route name, no other text.
Routes:
file_pattern — query is a glob / file-name pattern (e.g. **/*.rs)
regex — query is a regex (contains \\b, \\d, |, [...], anchors)
exact_symbol — query is a single identifier or symbol name (CamelCase, snake_case, dot.path)
structural — callers / callees / imports / type references / call graph
deep — open-ended \"how\" / \"why\" / \"explain\" questions
analytical — comparative or quality analysis (most, least, compare, review)
exhaustive — enumerate all occurrences of something (list all, find all, show every, list all config/env/CLI flags)
semantic — natural-language concept search with no structural intent
architecture — high-level overview: main components, subsystems, god nodes, data flow
feature_planning — impact analysis for adding a feature (\"if I wanted to add X\", \"what files would change\")
Respond with exactly one route name in snake_case, no other text.";
pub(crate) fn build_classify_prompt(query: &str) -> String {
format!("Classify this code-search query:\n\n{query}\n\nRoute:")
}
pub(crate) fn parse_route_from_llm_output(text: &str) -> anyhow::Result<AgentRoute> {
let lowered = text.trim().to_ascii_lowercase();
let body = strip_label_prefix(&lowered);
body.parse::<AgentRoute>()
.map_err(|_| anyhow::anyhow!("LLM returned unrecognized route: {text:?}"))
}
fn strip_label_prefix(lowered: &str) -> &str {
for label in ["route:", "route =", "route=", "answer:", "answer ="] {
if let Some(rest) = lowered.strip_prefix(label) {
return rest.trim();
}
}
lowered
}
pub(crate) const HYDE_SYSTEM_PROMPT: &str = "\
You are a code-synthesis assistant. Given a developer's question about a \
codebase, write a short code snippet (1-30 lines) that would directly answer \
their question. Output ONLY the code, no markdown fences, no explanation.";
pub(crate) fn build_hyde_prompt(query: &str) -> String {
let lang_hint = detect_language_hint(query);
if let Some(lang) = lang_hint {
format!("Write a short {lang} code snippet that would directly answer:\n\n{query}")
} else {
format!("Write a short code snippet that would directly answer:\n\n{query}")
}
}
fn detect_language_hint(query: &str) -> Option<&'static str> {
let lower = query.to_ascii_lowercase();
if lower.contains("typescript") || lower.contains("in ts ") || lower.ends_with("in ts") {
return Some("TypeScript");
}
if lower.contains("javascript") || lower.contains("in js ") || lower.ends_with("in js") {
return Some("JavaScript");
}
if lower.contains("objective-c") || lower.contains("objc") {
return Some("Objective-C");
}
if lower.contains("c#") || lower.contains("csharp") || lower.contains("c-sharp") {
return Some("C#");
}
if lower.contains("c++") || lower.contains("cplusplus") || lower.contains("cpp ") {
return Some("C++");
}
if lower.contains("python") {
return Some("Python");
}
if lower.contains("rust") {
return Some("Rust");
}
if lower.contains("golang") || lower.contains("in go ") || lower.ends_with("in go") {
return Some("Go");
}
if lower.contains("kotlin") {
return Some("Kotlin");
}
if lower.contains("java") {
return Some("Java");
}
if lower.contains("swift") {
return Some("Swift");
}
if lower.contains("dart") {
return Some("Dart");
}
if lower.contains("scala") {
return Some("Scala");
}
if lower.contains("in c ")
|| lower.contains("in c\n")
|| lower.ends_with("in c")
|| lower.contains(" c code")
{
return Some("C");
}
None
}
#[cfg(all(feature = "llm", test))]
mod tests {
use super::*;
#[test]
fn parse_route_each_variant_round_trips() {
let variants = [
AgentRoute::FilePattern,
AgentRoute::Regex,
AgentRoute::ExactSymbol,
AgentRoute::Structural,
AgentRoute::Deep,
AgentRoute::Analytical,
AgentRoute::Exhaustive,
AgentRoute::Semantic,
AgentRoute::Architecture,
AgentRoute::FeaturePlanning,
];
for variant in variants {
let display = format!("{variant}");
let parsed = parse_route_from_llm_output(&display)
.unwrap_or_else(|e| panic!("parse failed for {display:?}: {e}"));
assert_eq!(parsed, variant, "round-trip failed for {display:?}");
}
}
#[test]
fn parse_route_accepts_both_cases() {
assert_eq!(
parse_route_from_llm_output("Deep").unwrap(),
AgentRoute::Deep
);
assert_eq!(
parse_route_from_llm_output("deep").unwrap(),
AgentRoute::Deep
);
assert_eq!(
parse_route_from_llm_output("DEEP").unwrap(),
AgentRoute::Deep
);
assert_eq!(
parse_route_from_llm_output(" deep\n").unwrap(),
AgentRoute::Deep
);
}
#[test]
fn parse_route_rejects_unknown() {
let err = parse_route_from_llm_output("not a route").unwrap_err();
assert!(
err.to_string().contains("unrecognized"),
"expected 'unrecognized' in error, got: {err}"
);
}
#[test]
fn parse_route_accepts_both_naming_forms() {
assert_eq!(
parse_route_from_llm_output("exact_symbol").unwrap(),
AgentRoute::ExactSymbol
);
assert_eq!(
parse_route_from_llm_output("exactsymbol").unwrap(),
AgentRoute::ExactSymbol
);
assert_eq!(
parse_route_from_llm_output("file_pattern").unwrap(),
AgentRoute::FilePattern
);
assert_eq!(
parse_route_from_llm_output("filepattern").unwrap(),
AgentRoute::FilePattern
);
assert!(
parse_route_from_llm_output("exhaustive_structural").is_err(),
"exhaustive_structural is no longer a valid route"
);
assert!(
parse_route_from_llm_output("deep_with_examples").is_err(),
"deep_with_examples is no longer a valid route"
);
assert_eq!(
parse_route_from_llm_output("feature_planning").unwrap(),
AgentRoute::FeaturePlanning
);
assert_eq!(
parse_route_from_llm_output("featureplanning").unwrap(),
AgentRoute::FeaturePlanning
);
}
#[test]
fn parse_route_strips_label_prefix() {
assert_eq!(
parse_route_from_llm_output("Route: deep").unwrap(),
AgentRoute::Deep
);
assert_eq!(
parse_route_from_llm_output("route: deep").unwrap(),
AgentRoute::Deep
);
assert_eq!(
parse_route_from_llm_output("Route = structural").unwrap(),
AgentRoute::Structural
);
assert_eq!(
parse_route_from_llm_output("Answer: feature_planning").unwrap(),
AgentRoute::FeaturePlanning
);
assert_eq!(
parse_route_from_llm_output(" Route: exactSymbol ").unwrap(),
AgentRoute::ExactSymbol
);
assert_eq!(
parse_route_from_llm_output("architecture").unwrap(),
AgentRoute::Architecture
);
}
#[test]
fn detect_language_hint_ordering() {
assert_eq!(
detect_language_hint("write a server in javascript"),
Some("JavaScript")
);
assert_eq!(
detect_language_hint("write a server in typescript"),
Some("TypeScript")
);
assert_eq!(detect_language_hint("write a class in java"), Some("Java"));
assert_eq!(detect_language_hint("write this in js"), Some("JavaScript"));
assert_eq!(detect_language_hint("write this in ts"), Some("TypeScript"));
assert_eq!(detect_language_hint("write me a class in c#"), Some("C#"));
assert_eq!(
detect_language_hint("write me a class in csharp"),
Some("C#")
);
assert_eq!(detect_language_hint("write this in c++"), Some("C++"));
assert_eq!(detect_language_hint("write this in cplusplus"), Some("C++"));
assert_eq!(
detect_language_hint("write a function in objective-c"),
Some("Objective-C")
);
assert_eq!(detect_language_hint("write a function in c "), Some("C"));
assert_eq!(detect_language_hint("a snippet in c"), Some("C"));
assert_eq!(detect_language_hint("write this in golang"), Some("Go"));
assert_eq!(detect_language_hint("write this in go "), Some("Go"));
assert_eq!(detect_language_hint("in python please"), Some("Python"));
assert_eq!(detect_language_hint("rust example"), Some("Rust"));
assert_eq!(detect_language_hint("kotlin example"), Some("Kotlin"));
assert_eq!(detect_language_hint("swift example"), Some("Swift"));
assert_eq!(detect_language_hint("dart example"), Some("Dart"));
assert_eq!(detect_language_hint("scala example"), Some("Scala"));
assert_eq!(detect_language_hint("no language hint here"), None);
assert_eq!(detect_language_hint("how does auth work"), None);
}
#[test]
fn all_variants_covered_by_parse_exhaustiveness_guard() {
#[allow(dead_code)]
fn assert_all_covered(route: AgentRoute) {
match route {
AgentRoute::FilePattern => {}
AgentRoute::Regex => {}
AgentRoute::ExactSymbol => {}
AgentRoute::Structural => {}
AgentRoute::Deep => {}
AgentRoute::Analytical => {}
AgentRoute::Exhaustive => {}
AgentRoute::Semantic => {}
AgentRoute::Architecture => {}
AgentRoute::FeaturePlanning => {}
}
}
}
}