use serde::{Deserialize, Serialize};
pub use brainwires_core::SearchResult;
use super::index::PROJECT_NAME_MAX_LENGTH;
pub fn default_hybrid() -> bool {
true
}
pub fn default_limit() -> usize {
10
}
pub fn default_min_score() -> f32 {
0.7
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "rag", derive(schemars::JsonSchema))]
pub struct QueryRequest {
pub query: String,
#[serde(default)]
pub path: Option<String>,
#[serde(default)]
pub project: Option<String>,
#[serde(default = "default_limit")]
pub limit: usize,
#[serde(default = "default_min_score")]
pub min_score: f32,
#[serde(default = "default_hybrid")]
pub hybrid: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "rag", derive(schemars::JsonSchema))]
pub struct QueryResponse {
pub results: Vec<SearchResult>,
pub duration_ms: u64,
#[serde(default)]
pub threshold_used: f32,
#[serde(default)]
pub threshold_lowered: bool,
}
impl QueryRequest {
pub fn validate(&self) -> Result<(), String> {
if self.query.trim().is_empty() {
return Err("query cannot be empty".to_string());
}
const MAX_QUERY_LENGTH: usize = 10_240; if self.query.len() > MAX_QUERY_LENGTH {
return Err(format!(
"query too long: {} bytes (max: {} bytes)",
self.query.len(),
MAX_QUERY_LENGTH
));
}
if !(0.0..=1.0).contains(&self.min_score) {
return Err(format!(
"min_score must be between 0.0 and 1.0, got: {}",
self.min_score
));
}
const MAX_LIMIT: usize = 1000;
if self.limit > MAX_LIMIT {
return Err(format!(
"limit too large: {} (max: {})",
self.limit, MAX_LIMIT
));
}
if let Some(ref project) = self.project {
if project.is_empty() {
return Err("project name cannot be empty".to_string());
}
if project.len() > PROJECT_NAME_MAX_LENGTH {
return Err(format!(
"project name too long (max {} characters)",
PROJECT_NAME_MAX_LENGTH
));
}
}
Ok(())
}
}