xz_rag/types/retrieval.rs
1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4
5use super::chunk::ChunkMetadata;
6
7use crate::pipeline::channel::ChannelConfig;
8
9// === Retrieval Request ===
10
11/// Multi-channel retrieval request.
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct RetrieveRequest {
14 /// Search query string.
15 pub query: String,
16 /// Channel configurations for this request.
17 pub channels: Vec<ChannelConfig>,
18 /// Global filters applied across all channels.
19 ///
20 /// **Note on channel support**: `StructuredFilter` values are currently only applied by the
21 /// `metadata` channel. The `semantic`, `bm25`, and `graph` channels silently ignore
22 /// `global_filters` in their current implementation.
23 pub global_filters: Vec<StructuredFilter>,
24 /// Maximum number of results after fusion.
25 pub top_k: usize,
26 /// Optional namespace for scoping.
27 pub namespace: Option<String>,
28 /// Whether to include embedding vectors in results.
29 pub include_embeddings: bool,
30 /// Optional query preprocessing strategy.
31 pub query_preprocessing: Option<QueryPreprocessing>,
32}
33
34impl RetrieveRequest {
35 /// Create a new builder for `RetrieveRequest`.
36 pub fn builder(query: impl Into<String>) -> RetrieveRequestBuilder {
37 RetrieveRequestBuilder {
38 query: query.into(),
39 channels: vec![ChannelConfig::semantic(0.5, 10)],
40 global_filters: vec![],
41 top_k: 10,
42 namespace: None,
43 include_embeddings: false,
44 query_preprocessing: None,
45 }
46 }
47}
48
49/// Builder for `RetrieveRequest`.
50pub struct RetrieveRequestBuilder {
51 query: String,
52 channels: Vec<ChannelConfig>,
53 global_filters: Vec<StructuredFilter>,
54 top_k: usize,
55 namespace: Option<String>,
56 include_embeddings: bool,
57 query_preprocessing: Option<QueryPreprocessing>,
58}
59
60impl RetrieveRequestBuilder {
61 /// Set the channels to search.
62 pub fn channels(mut self, channels: Vec<ChannelConfig>) -> Self {
63 self.channels = channels;
64 self
65 }
66
67 /// Set the maximum number of results.
68 pub fn top_k(mut self, top_k: usize) -> Self {
69 self.top_k = top_k;
70 self
71 }
72
73 /// Set the namespace for scoping.
74 pub fn namespace(mut self, ns: impl Into<String>) -> Self {
75 self.namespace = Some(ns.into());
76 self
77 }
78
79 /// Build the `RetrieveRequest`.
80 pub fn build(self) -> RetrieveRequest {
81 RetrieveRequest {
82 query: self.query,
83 channels: self.channels,
84 global_filters: self.global_filters,
85 top_k: self.top_k,
86 namespace: self.namespace,
87 include_embeddings: self.include_embeddings,
88 query_preprocessing: self.query_preprocessing,
89 }
90 }
91}
92
93// === Query Preprocessing ===
94
95/// Query preprocessing strategy.
96#[derive(Debug, Clone, Serialize, Deserialize)]
97pub enum QueryPreprocessing {
98 /// No preprocessing.
99 None,
100 /// HYDE: generate a hypothetical answer passage.
101 Hyde,
102 /// Generate N query variations for better recall.
103 QueryExpansion {
104 /// Number of variations to generate.
105 count: usize,
106 },
107 /// Translate query to English before retrieval.
108 TranslateToEnglish,
109}
110
111// === Structured Filter ===
112
113/// Structured filter for metadata-based retrieval.
114#[derive(Debug, Clone, Serialize, Deserialize)]
115pub enum StructuredFilter {
116 /// Field equals value.
117 MetadataEq {
118 /// Metadata key.
119 key: String,
120 /// Expected value.
121 value: String,
122 },
123 /// Field is one of the given values.
124 MetadataIn {
125 /// Metadata key.
126 key: String,
127 /// Allowed values.
128 values: Vec<String>,
129 },
130 /// Field does not equal value.
131 MetadataNe {
132 /// Metadata key.
133 key: String,
134 /// Excluded value.
135 value: String,
136 },
137 /// Field exists (has any value).
138 MetadataExists {
139 /// Metadata key.
140 key: String,
141 },
142 /// Raw SQL filter expression.
143 SqlFilter(String),
144 /// Logical AND of two filters.
145 And(Box<StructuredFilter>, Box<StructuredFilter>),
146 /// Logical OR of two filters.
147 Or(Box<StructuredFilter>, Box<StructuredFilter>),
148}
149
150// === Retrieve Result ===
151
152/// Result of a multi-channel retrieval operation.
153#[derive(Debug, Clone, Serialize, Deserialize)]
154pub struct RetrieveResult {
155 /// Ranked list of retrieved chunks.
156 pub hits: Vec<RetrievedChunk>,
157 /// Per-channel statistics.
158 pub channel_report: HashMap<String, ChannelStats>,
159 /// Total latency in milliseconds.
160 pub latency_ms: u64,
161 /// The query text actually used (after preprocessing).
162 pub effective_query: String,
163}
164
165/// A single retrieved chunk with score and metadata.
166#[derive(Debug, Clone, Serialize, Deserialize)]
167pub struct RetrievedChunk {
168 /// Chunk identifier.
169 pub chunk_id: String,
170 /// Parent document identifier.
171 pub document_id: String,
172 /// Chunk text content.
173 pub content: String,
174 /// Relevance score (post-fusion).
175 pub score: f32,
176 /// Channel that produced this chunk.
177 pub channel: String,
178 /// Original score from the channel (pre-fusion).
179 pub channel_score: f32,
180 /// Chunk metadata.
181 pub metadata: ChunkMetadata,
182 /// Optional embedding vector.
183 pub embedding: Option<Vec<f32>>,
184}
185
186/// Per-channel retrieval statistics.
187#[derive(Debug, Clone, Serialize, Deserialize)]
188pub struct ChannelStats {
189 /// Channel type identifier.
190 pub channel_type: String,
191 /// Number of hits from this channel.
192 pub hits: usize,
193 /// Latency in milliseconds.
194 pub latency_ms: u64,
195 /// Minimum score among hits.
196 pub min_score: f32,
197 /// Maximum score among hits.
198 pub max_score: f32,
199}