use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum CacheMode {
#[default]
Platform,
Local,
Off,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct IndexWarning {
pub path: String,
pub message: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Chunk {
pub content: String,
pub file_path: String,
pub start_line: usize,
pub end_line: usize,
pub language: Option<String>,
#[serde(default)]
pub symbols: Vec<Symbol>,
#[serde(default)]
pub breadcrumbs: Vec<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Symbol {
pub name: String,
pub kind: String,
pub line: usize,
}
impl Chunk {
pub fn location(&self) -> String {
format!("{}:{}-{}", self.file_path, self.start_line, self.end_line)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum SearchMode {
Hybrid,
Semantic,
Bm25,
}
impl std::str::FromStr for SearchMode {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"hybrid" => Ok(Self::Hybrid),
"semantic" => Ok(Self::Semantic),
"bm25" => Ok(Self::Bm25),
_ => Err(format!("Unknown search mode: {s}")),
}
}
}
impl std::fmt::Display for SearchMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Hybrid => write!(f, "hybrid"),
Self::Semantic => write!(f, "semantic"),
Self::Bm25 => write!(f, "bm25"),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct SearchOptions {
pub top_k: usize,
pub mode: SearchMode,
pub alpha: Option<f32>,
pub filter_languages: Vec<String>,
pub filter_paths: Vec<String>,
pub use_query_cache: bool,
pub explain: bool,
}
impl Default for SearchOptions {
fn default() -> Self {
Self {
top_k: 5,
mode: SearchMode::Hybrid,
alpha: None,
filter_languages: Vec::new(),
filter_paths: Vec::new(),
use_query_cache: true,
explain: false,
}
}
}
impl SearchOptions {
pub fn new(top_k: usize) -> Self {
Self {
top_k,
..Self::default()
}
}
pub fn with_mode(mut self, mode: SearchMode) -> Self {
self.mode = mode;
self
}
pub fn with_alpha(mut self, alpha: f32) -> Self {
self.alpha = Some(alpha);
self
}
pub fn with_languages(mut self, languages: impl IntoIterator<Item = String>) -> Self {
self.filter_languages = languages.into_iter().collect();
self
}
pub fn with_paths(mut self, paths: impl IntoIterator<Item = String>) -> Self {
self.filter_paths = paths.into_iter().collect();
self
}
pub fn with_cache(mut self, use_query_cache: bool) -> Self {
self.use_query_cache = use_query_cache;
self
}
pub fn with_explain(mut self, explain: bool) -> Self {
self.explain = explain;
self
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct SearchResult {
pub chunk: Chunk,
pub score: f32,
pub source: SearchMode,
#[serde(skip_serializing_if = "Option::is_none")]
pub explanation: Option<SearchExplanation>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct SearchHit {
pub chunk_id: usize,
pub score: f32,
pub source: SearchMode,
pub explanation: Option<SearchExplanation>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct SearchExplanation {
pub alpha: Option<f32>,
pub bm25_rank: Option<usize>,
pub bm25_score: Option<f32>,
pub semantic_rank: Option<usize>,
pub semantic_score: Option<f32>,
pub rrf_score: Option<f32>,
pub boosted_score: Option<f32>,
pub final_score: f32,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct IndexStats {
pub indexed_files: usize,
pub total_chunks: usize,
pub languages: std::collections::BTreeMap<String, usize>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct IndexOptions {
pub cache_mode: CacheMode,
}
impl Default for IndexOptions {
fn default() -> Self {
Self {
cache_mode: CacheMode::Platform,
}
}
}