use std::collections::BTreeMap;
use super::{MetadataObject, ReadPresentationMode, ResponseMode};
use crate::domain::{
ChannelHealthStatus, EvidenceAnchor, PathClass, SourceClass, model::SymbolMatch,
model::TextMatch,
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum SearchPatternType {
Literal,
Regex,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ExploreOperation {
Probe,
Zoom,
Refine,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct ExploreAnchor {
pub start_line: usize,
pub start_column: usize,
pub end_line: usize,
pub end_column: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct ExploreCursor {
pub line: usize,
pub column: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct ExploreLineWindow {
pub start_line: usize,
pub end_line: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct ExploreWindow {
pub start_line: usize,
pub end_line: usize,
pub bytes: usize,
pub content: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct ExploreMatch {
pub match_id: String,
pub start_line: usize,
pub start_column: usize,
pub end_line: usize,
pub end_column: usize,
pub excerpt: String,
pub window: ExploreWindow,
pub anchor: ExploreAnchor,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct ExploreMetadata {
pub lossy_utf8: bool,
pub effective_context_lines: usize,
pub effective_max_matches: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct ExploreParams {
pub path: String,
pub repository_id: Option<String>,
pub operation: ExploreOperation,
pub query: Option<String>,
pub pattern_type: Option<SearchPatternType>,
pub anchor: Option<ExploreAnchor>,
pub context_lines: Option<usize>,
pub max_matches: Option<usize>,
pub resume_from: Option<ExploreCursor>,
pub presentation_mode: Option<ReadPresentationMode>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct ExploreResponse {
pub repository_id: String,
pub path: String,
pub operation: ExploreOperation,
#[serde(skip_serializing_if = "Option::is_none")]
pub query: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pattern_type: Option<SearchPatternType>,
pub total_lines: usize,
pub scan_scope: ExploreLineWindow,
#[serde(skip_serializing_if = "Option::is_none")]
pub window: Option<ExploreWindow>,
pub total_matches: usize,
pub matches: Vec<ExploreMatch>,
pub truncated: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub resume_from: Option<ExploreCursor>,
pub metadata: ExploreMetadata,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
pub struct SearchTextParams {
pub query: String,
pub pattern_type: Option<SearchPatternType>,
pub repository_id: Option<String>,
pub path_regex: Option<String>,
pub limit: Option<usize>,
pub context_lines: Option<usize>,
pub max_matches_per_file: Option<usize>,
pub collapse_by_file: Option<bool>,
pub response_mode: Option<ResponseMode>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct SearchTextResponse {
pub total_matches: usize,
pub matches: Vec<TextMatch>,
#[serde(skip_serializing_if = "Option::is_none")]
pub result_handle: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<SearchTextMetadata>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum SearchLexicalBackendMetadata {
Native,
Ripgrep,
Mixed,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct SearchTextMetadata {
pub lexical_backend: SearchLexicalBackendMetadata,
#[serde(skip_serializing_if = "Option::is_none")]
pub lexical_backend_note: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct SearchHybridChannelWeightsParams {
pub lexical: Option<f32>,
pub graph: Option<f32>,
pub semantic: Option<f32>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
pub struct SearchHybridParams {
pub query: String,
pub repository_id: Option<String>,
pub language: Option<String>,
pub limit: Option<usize>,
pub weights: Option<SearchHybridChannelWeightsParams>,
pub semantic: Option<bool>,
pub response_mode: Option<ResponseMode>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct SearchHybridMatch {
#[serde(skip_serializing_if = "Option::is_none")]
pub match_id: Option<String>,
pub repository_id: String,
pub path: String,
pub line: usize,
pub column: usize,
pub excerpt: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub anchor: Option<EvidenceAnchor>,
pub blended_score: f32,
pub lexical_score: f32,
pub graph_score: f32,
pub semantic_score: f32,
pub lexical_sources: Vec<String>,
pub graph_sources: Vec<String>,
pub semantic_sources: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub path_class: Option<PathClass>,
#[serde(skip_serializing_if = "Option::is_none")]
pub source_class: Option<SourceClass>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub surface_families: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub navigation_hint: Option<SearchHybridNavigationHint>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub rank_reasons: Vec<SearchHybridRankReason>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum SearchHybridRankReason {
ExactSymbolMatch,
ExactTextMatch,
StrongLexicalAnchor,
GraphAdjacency,
SemanticContribution,
WitnessOnlyFallback,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct SearchHybridNavigationHint {
pub pivotable: bool,
pub document_symbols: bool,
pub go_to_definition: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct SearchHybridUtilitySummary {
pub pivotable_match_count: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub best_pivot_rank: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub best_pivot_path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub best_pivot_repository_id: Option<String>,
pub symbol_navigation_ready: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct SearchHybridChannelDiagnostic {
pub code: String,
pub message: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct SearchHybridChannelMetadata {
pub status: ChannelHealthStatus,
#[serde(skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
pub candidate_count: usize,
pub hit_count: usize,
pub match_count: usize,
pub diagnostic_count: usize,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub diagnostics: Vec<SearchHybridChannelDiagnostic>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct SearchHybridDiagnosticsSummary {
pub walk: usize,
pub read: usize,
pub total: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct SearchHybridStageSample {
pub elapsed_us: u64,
pub input_count: usize,
pub output_count: usize,
}
impl From<&crate::searcher::SearchStageSample> for SearchHybridStageSample {
fn from(value: &crate::searcher::SearchStageSample) -> Self {
Self {
elapsed_us: value.elapsed_us,
input_count: value.input_count,
output_count: value.output_count,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct SearchHybridStageAttribution {
pub candidate_intake: SearchHybridStageSample,
pub freshness_validation: SearchHybridStageSample,
pub scan: SearchHybridStageSample,
pub witness_scoring: SearchHybridStageSample,
pub graph_expansion: SearchHybridStageSample,
pub semantic_retrieval: SearchHybridStageSample,
pub anchor_blending: SearchHybridStageSample,
pub document_aggregation: SearchHybridStageSample,
pub final_diversification: SearchHybridStageSample,
}
impl From<&crate::searcher::SearchStageAttribution> for SearchHybridStageAttribution {
fn from(value: &crate::searcher::SearchStageAttribution) -> Self {
Self {
candidate_intake: SearchHybridStageSample::from(&value.candidate_intake),
freshness_validation: SearchHybridStageSample::from(&value.freshness_validation),
scan: SearchHybridStageSample::from(&value.scan),
witness_scoring: SearchHybridStageSample::from(&value.witness_scoring),
graph_expansion: SearchHybridStageSample::from(&value.graph_expansion),
semantic_retrieval: SearchHybridStageSample::from(&value.semantic_retrieval),
anchor_blending: SearchHybridStageSample::from(&value.anchor_blending),
document_aggregation: SearchHybridStageSample::from(&value.document_aggregation),
final_diversification: SearchHybridStageSample::from(&value.final_diversification),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct ResponseFreshnessRepositoryMetadata {
pub repository_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub snapshot_id: Option<String>,
pub manifest: String,
pub semantic: String,
pub dirty_root: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub provider: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct ResponseFreshnessBasisMetadata {
pub mode: String,
pub cacheable: bool,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub repositories: Vec<ResponseFreshnessRepositoryMetadata>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct SearchHybridSemanticAcceleratorMetadata {
pub tier: String,
pub state: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<ChannelHealthStatus>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct SearchHybridLanguageCapabilityMetadata {
pub requested_language: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub display_name: Option<String>,
pub semantic_chunking: String,
pub semantic_accelerator: SearchHybridSemanticAcceleratorMetadata,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub capabilities: BTreeMap<String, String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum SearchHybridQueryShape {
BroadNaturalLanguage,
CodeShaped,
Neutral,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct SearchHybridExactPivotAssistance {
pub applied: bool,
pub exact_symbol_hit_count: usize,
pub exact_text_hit_count: usize,
pub boosted_match_count: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct SearchHybridMetadata {
pub channels: BTreeMap<String, SearchHybridChannelMetadata>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lexical_backend: Option<SearchLexicalBackendMetadata>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lexical_backend_note: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub semantic_requested: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub semantic_enabled: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub semantic_status: Option<ChannelHealthStatus>,
#[serde(skip_serializing_if = "Option::is_none")]
pub semantic_reason: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub semantic_candidate_count: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub semantic_hit_count: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub semantic_match_count: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lexical_only_mode: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub query_shape: Option<SearchHybridQueryShape>,
#[serde(skip_serializing_if = "Option::is_none")]
pub warning: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub exact_pivot_assistance: Option<SearchHybridExactPivotAssistance>,
#[serde(skip_serializing_if = "Option::is_none")]
pub witness_demotion_applied: Option<bool>,
pub diagnostics_count: usize,
pub diagnostics: SearchHybridDiagnosticsSummary,
#[serde(skip_serializing_if = "Option::is_none")]
pub stage_attribution: Option<SearchHybridStageAttribution>,
#[serde(skip_serializing_if = "Option::is_none")]
pub semantic_capability: Option<SearchHybridLanguageCapabilityMetadata>,
#[serde(skip_serializing_if = "Option::is_none")]
pub utility: Option<SearchHybridUtilitySummary>,
pub freshness_basis: ResponseFreshnessBasisMetadata,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct SearchHybridResponse {
pub matches: Vec<SearchHybridMatch>,
#[serde(skip_serializing_if = "Option::is_none")]
pub result_handle: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub semantic_requested: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub semantic_enabled: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub semantic_status: Option<ChannelHealthStatus>,
#[serde(skip_serializing_if = "Option::is_none")]
pub semantic_reason: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub semantic_hit_count: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub semantic_match_count: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub warning: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<SearchHybridMetadata>,
#[serde(skip_serializing_if = "Option::is_none")]
pub note: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
pub struct SearchSymbolParams {
pub query: String,
pub repository_id: Option<String>,
pub path_class: Option<SearchSymbolPathClass>,
pub path_regex: Option<String>,
pub limit: Option<usize>,
pub response_mode: Option<ResponseMode>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct SearchSymbolResponse {
pub matches: Vec<SymbolMatch>,
#[serde(skip_serializing_if = "Option::is_none")]
pub result_handle: Option<String>,
#[schemars(schema_with = "super::metadata_object_field_schema")]
pub metadata: Option<MetadataObject>,
pub note: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum SearchSymbolPathClass {
Runtime,
Project,
Support,
}
impl SearchSymbolPathClass {
pub fn as_str(self) -> &'static str {
match self {
Self::Runtime => "runtime",
Self::Project => "project",
Self::Support => "support",
}
}
}