use crate::common::utils::{AgentIntent, strip_code_blocks};
use crate::prompts::generic::INTENT_DETECTION_PROMPT;
use anyhow::Result;
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct IntentResponse {
intent: String,
#[serde(default)]
tool: Option<String>,
#[serde(default)]
args: Option<serde_json::Value>,
}
pub type GenerateFn =
dyn FnMut(&str) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<String>> + Send>>;
pub async fn classify_intent(
prompt: &str,
workspace_snapshot: &str,
mcp_tools: &str,
generate_fn: &mut GenerateFn,
) -> AgentIntent {
let full = INTENT_DETECTION_PROMPT
.replace("{USER_PROMPT}", prompt)
.replace("{WORKSPACE}", workspace_snapshot)
.replace("{MCP_TOOLS}", mcp_tools);
let raw = match generate_fn(&full).await {
Ok(r) => r,
Err(_) => return AgentIntent::TaskPlan,
};
let clean = strip_code_blocks(&raw);
let parsed: IntentResponse = match serde_json::from_str(clean.trim()) {
Ok(p) => p,
Err(_) => return AgentIntent::TaskPlan,
};
match parsed.intent.as_str() {
"direct_answer" => AgentIntent::DirectAnswer,
"tool_call" => AgentIntent::ToolCall {
tool: parsed.tool.unwrap_or_else(|| "list_dir".to_string()),
args: parsed.args.unwrap_or(serde_json::Value::Null),
},
_ => AgentIntent::TaskPlan,
}
}