1use std::collections::BTreeMap;
4
5use serde::{Deserialize, Serialize};
6
7use crate::contracts::{ContainerPath, VaultPath};
8
9use super::related::RelationKind;
10
11#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
13#[serde(rename_all = "camelCase")]
14pub struct AskSource {
15 pub vault_path: VaultPath,
17 pub title: String,
19 pub snippet: String,
21 pub score: f64,
23}
24
25#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
27#[serde(rename_all = "camelCase")]
28pub struct AskDiagnostics {
29 pub endpoint: String,
31 pub model: String,
33 pub planning: AskLlmStageDiagnostics,
35 pub search: AskSearchDiagnostics,
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub synthesis: Option<AskLlmStageDiagnostics>,
40}
41
42#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
44#[serde(rename_all = "camelCase")]
45pub struct AskLlmStageDiagnostics {
46 pub duration_ms: u64,
48 pub content: String,
50}
51
52#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
54#[serde(rename_all = "camelCase")]
55pub struct AskSearchDiagnostics {
56 pub duration_ms: u64,
58 pub total: u32,
60}
61
62#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
64#[serde(rename_all = "camelCase")]
65pub struct AskResponse {
66 #[serde(skip_serializing_if = "Option::is_none")]
68 pub vault: Option<ContainerPath>,
69 pub question: String,
71 pub answer: String,
73 pub queries: Vec<String>,
75 pub sources: Vec<AskSource>,
77 #[serde(skip_serializing_if = "Option::is_none")]
79 pub diagnostics: Option<AskDiagnostics>,
80}
81
82#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
84#[serde(rename_all = "camelCase")]
85pub struct ReadResult {
86 pub found: bool,
88 pub vault_path: VaultPath,
90 #[serde(skip_serializing_if = "Option::is_none")]
92 pub title: Option<String>,
93 #[serde(skip_serializing_if = "Option::is_none")]
95 pub content: Option<String>,
96 #[serde(skip_serializing_if = "Option::is_none")]
98 pub section: Option<ReadSection>,
99 #[serde(default)]
101 pub links: Vec<String>,
102 #[serde(default)]
104 pub backlinks: Vec<String>,
105 #[serde(default)]
107 pub tags: Vec<String>,
108 #[serde(default)]
110 pub aliases: Vec<String>,
111}
112
113#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
115#[serde(rename_all = "camelCase")]
116pub struct ReadSection {
117 pub heading: String,
119 pub from_line: u32,
121 pub to_line: u32,
123 pub obsidian_ref: String,
125}
126
127#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
129#[serde(rename_all = "camelCase")]
130pub struct ReadResponse {
131 #[serde(skip_serializing_if = "Option::is_none")]
133 pub vault: Option<ContainerPath>,
134 pub results: Vec<ReadResult>,
136}
137
138impl ReadResponse {
139 #[must_use]
141 pub const fn stub() -> Self {
142 Self {
143 vault: None,
144 results: Vec::new(),
145 }
146 }
147}
148
149#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
151#[serde(rename_all = "camelCase")]
152pub struct VaultRecall {
153 pub active_notes: Vec<NoteExcerpt>,
155 pub linked_context: Vec<LinkedNote>,
157}
158
159#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
161#[serde(rename_all = "camelCase")]
162pub struct NoteExcerpt {
163 pub vault_path: VaultPath,
165 pub title: String,
167 pub snippet: String,
169 pub score: f64,
171 pub rank: u32,
173 pub mtime: String,
175}
176
177#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
179#[serde(rename_all = "camelCase")]
180pub struct LinkedNote {
181 pub vault_path: VaultPath,
183 pub title: String,
185 pub link_text: String,
187 pub relation: RelationKind,
189 pub hops: u8,
191 pub aggregated_score: f64,
194 pub source_notes: Vec<(VaultPath, f64)>,
198}
199
200#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
202#[serde(rename_all = "camelCase")]
203pub struct RecallResponse {
204 #[serde(skip_serializing_if = "Option::is_none")]
206 pub vault: Option<ContainerPath>,
207 pub vault_recall: Option<VaultRecall>,
209 pub evidence_score: f64,
211 pub tokens_used: u32,
213 pub excluded: Vec<String>,
215 pub excluded_by_budget: Vec<String>,
217 pub skipped: bool,
219 #[serde(skip_serializing_if = "Option::is_none")]
221 pub diagnostics: Option<RecallDiagnostics>,
222}
223
224#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
226#[serde(rename_all = "camelCase")]
227pub struct RecallDiagnostics {
228 pub input_tokens: usize,
230 pub query_tokens: usize,
232 pub query_count: usize,
234 pub phrase_count: usize,
236 #[serde(skip_serializing_if = "Option::is_none")]
238 pub distillation_input_tokens: Option<usize>,
239 pub distillation_ran: bool,
241 pub distillation_ms: Option<u64>,
243 pub distillation_succeeded: bool,
245 #[serde(skip_serializing_if = "Option::is_none")]
247 pub distillation_fallback_reason: Option<String>,
248 pub retrieval_ms: u64,
250 pub embed_batches: u32,
252 pub rerank_candidates: Option<u32>,
254 pub rerank_ms: Option<u64>,
256 pub total_ms: u64,
258}
259
260#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
262#[serde(rename_all = "camelCase")]
263pub struct MetaEntry {
264 pub path: VaultPath,
266 pub frontmatter: BTreeMap<String, serde_json::Value>,
268 #[serde(skip_serializing_if = "Option::is_none")]
270 pub scope: Option<String>,
271 #[serde(skip_serializing_if = "Option::is_none")]
273 pub mtime: Option<String>,
274}
275
276#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
278#[serde(rename_all = "camelCase")]
279pub struct MetaResponse {
280 #[serde(skip_serializing_if = "Option::is_none")]
282 pub vault: Option<ContainerPath>,
283 pub entries: Vec<MetaEntry>,
285 #[serde(skip_serializing_if = "Option::is_none")]
287 pub tag_counts: Option<BTreeMap<String, u32>>,
288}
289
290#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
292#[serde(rename_all = "camelCase")]
293pub struct ChangeEntry {
294 pub path: VaultPath,
296 pub indexed_at: String,
298}
299
300#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
302#[serde(rename_all = "camelCase")]
303pub struct TombstoneEntry {
304 pub path: VaultPath,
306 pub deleted_at: String,
308}
309
310#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
312#[serde(rename_all = "camelCase")]
313pub struct ChangesResponse {
314 #[serde(skip_serializing_if = "Option::is_none")]
316 pub vault: Option<ContainerPath>,
317 pub added: Vec<ChangeEntry>,
319 pub modified: Vec<ChangeEntry>,
321 pub deleted: Vec<TombstoneEntry>,
323}