brainwires_rag/rag/types/
query.rs1use serde::{Deserialize, Serialize};
2
3pub use brainwires_core::SearchResult;
5
6use super::index::PROJECT_NAME_MAX_LENGTH;
7
8pub fn default_hybrid() -> bool {
10 true
11}
12
13pub fn default_limit() -> usize {
15 10
16}
17
18pub fn default_min_score() -> f32 {
20 0.7
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
25pub struct QueryRequest {
26 pub query: String,
28 #[serde(default)]
30 pub path: Option<String>,
31 #[serde(default)]
33 pub project: Option<String>,
34 #[serde(default = "default_limit")]
36 pub limit: usize,
37 #[serde(default = "default_min_score")]
39 pub min_score: f32,
40 #[serde(default = "default_hybrid")]
42 pub hybrid: bool,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
47pub struct QueryResponse {
48 pub results: Vec<SearchResult>,
50 pub duration_ms: u64,
52 #[serde(default)]
54 pub threshold_used: f32,
55 #[serde(default)]
57 pub threshold_lowered: bool,
58}
59
60impl QueryRequest {
61 pub fn validate(&self) -> Result<(), String> {
63 if self.query.trim().is_empty() {
65 return Err("query cannot be empty".to_string());
66 }
67
68 const MAX_QUERY_LENGTH: usize = 10_240; if self.query.len() > MAX_QUERY_LENGTH {
71 return Err(format!(
72 "query too long: {} bytes (max: {} bytes)",
73 self.query.len(),
74 MAX_QUERY_LENGTH
75 ));
76 }
77
78 if !(0.0..=1.0).contains(&self.min_score) {
80 return Err(format!(
81 "min_score must be between 0.0 and 1.0, got: {}",
82 self.min_score
83 ));
84 }
85
86 const MAX_LIMIT: usize = 1000;
88 if self.limit > MAX_LIMIT {
89 return Err(format!(
90 "limit too large: {} (max: {})",
91 self.limit, MAX_LIMIT
92 ));
93 }
94
95 if let Some(ref project) = self.project {
97 if project.is_empty() {
98 return Err("project name cannot be empty".to_string());
99 }
100 if project.len() > PROJECT_NAME_MAX_LENGTH {
101 return Err(format!(
102 "project name too long (max {} characters)",
103 PROJECT_NAME_MAX_LENGTH
104 ));
105 }
106 }
107
108 Ok(())
109 }
110}