1use axum::Json;
2use axum::http::HeaderValue;
3use chrono::{DateTime, Utc};
4use locus_core_rs::SemanticLink;
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Serialize)]
8#[serde(rename_all = "camelCase")]
9pub(crate) struct ErrorResponse {
10 pub(crate) error: String,
11}
12
13#[derive(Debug, Deserialize)]
14#[serde(rename_all = "camelCase")]
15pub(crate) struct CalibrateSessionHttpRequest {
16 pub(crate) session_id: String,
17 pub(crate) tenant_id: Option<String>,
18 pub(crate) stability: f32,
19 pub(crate) friction: f32,
20 pub(crate) logic: f32,
21 pub(crate) autonomy: f32,
22 pub(crate) trigger: Option<String>,
23}
24
25#[derive(Debug, Deserialize)]
26#[serde(rename_all = "camelCase")]
27pub(crate) struct StoreContextHttpRequest {
28 pub(crate) node: String,
29 pub(crate) session_id: String,
30 pub(crate) tenant_id: Option<String>,
31}
32
33#[derive(Debug, Deserialize)]
34#[serde(rename_all = "camelCase")]
35pub(crate) struct ScoreAvecHttpRequest {
36 pub(crate) text: String,
37 pub(crate) tenant_id: Option<String>,
38}
39
40#[derive(Debug, Deserialize)]
41#[serde(rename_all = "camelCase")]
42pub(crate) struct GetContextHttpRequest {
43 pub(crate) session_id: String,
44 pub(crate) tenant_id: Option<String>,
45 pub(crate) stability: f32,
46 pub(crate) friction: f32,
47 pub(crate) logic: f32,
48 pub(crate) autonomy: f32,
49 pub(crate) limit: Option<usize>,
50 pub(crate) from_utc: Option<DateTime<Utc>>,
51 pub(crate) to_utc: Option<DateTime<Utc>>,
52 pub(crate) tiers: Option<Vec<String>>,
53 pub(crate) query_text: Option<String>,
54 pub(crate) query_embedding: Option<Vec<f32>>,
55 pub(crate) alpha: Option<f32>,
56 pub(crate) beta: Option<f32>,
57 pub(crate) gamma: Option<f32>,
58 pub(crate) semantic_tags: Option<Vec<String>>,
59 pub(crate) tags_contains: Option<Vec<String>>,
60 pub(crate) link_rel: Option<String>,
61 pub(crate) link_target: Option<String>,
62 pub(crate) links_to_ref: Option<String>,
63 pub(crate) tag_prefix: Option<String>,
64 pub(crate) has_semantic_links: Option<bool>,
65}
66
67#[derive(Debug, Deserialize)]
68#[serde(rename_all = "camelCase")]
69pub(crate) struct GetEmbeddingContextHttpRequest {
70 pub(crate) session_id: String,
71 pub(crate) tenant_id: Option<String>,
72 pub(crate) stability: f32,
73 pub(crate) friction: f32,
74 pub(crate) logic: f32,
75 pub(crate) autonomy: f32,
76 pub(crate) limit: Option<usize>,
77 pub(crate) from_utc: Option<DateTime<Utc>>,
78 pub(crate) to_utc: Option<DateTime<Utc>>,
79 pub(crate) tiers: Option<Vec<String>>,
80 pub(crate) rag_query_text: Option<String>,
81 pub(crate) rag_embedding: Option<Vec<f32>>,
82 pub(crate) avec_query_text: Option<String>,
83 pub(crate) avec_embedding: Option<Vec<f32>>,
84 pub(crate) rag_weight: Option<f32>,
85 pub(crate) avec_weight: Option<f32>,
86 pub(crate) alpha: Option<f32>,
87 pub(crate) beta: Option<f32>,
88 pub(crate) tag_weight: Option<f32>,
89 pub(crate) semantic_tags: Option<Vec<String>>,
90 pub(crate) link_rel: Option<String>,
91 pub(crate) link_target: Option<String>,
92}
93
94#[derive(Debug, Deserialize)]
95#[serde(rename_all = "camelCase")]
96pub(crate) struct CreateMonthlyRollupHttpRequest {
97 pub(crate) session_id: String,
98 pub(crate) tenant_id: Option<String>,
99 pub(crate) start_date_utc: DateTime<Utc>,
100 pub(crate) end_date_utc: DateTime<Utc>,
101 pub(crate) source_session_id: Option<String>,
102 pub(crate) parent_node_id: Option<String>,
103 pub(crate) persist: Option<bool>,
104 pub(crate) limit: Option<usize>,
105}
106
107#[derive(Debug, Deserialize)]
108#[serde(rename_all = "camelCase")]
109pub(crate) struct BatchRekeyHttpRequest {
110 pub(crate) node_ids: Vec<String>,
111 pub(crate) target_session_id: String,
112 pub(crate) target_tenant_id: Option<String>,
113 pub(crate) dry_run: Option<bool>,
114 pub(crate) allow_merge: Option<bool>,
115}
116
117#[derive(Debug, Clone, Deserialize)]
118#[serde(rename_all = "camelCase")]
119pub(crate) struct EmbeddingMigrationFilterHttp {
120 pub(crate) session_id: Option<String>,
121 pub(crate) from_utc: Option<DateTime<Utc>>,
122 pub(crate) to_utc: Option<DateTime<Utc>>,
123 pub(crate) tiers: Option<Vec<String>>,
124 pub(crate) has_embedding: Option<bool>,
125 pub(crate) embedding_model: Option<String>,
126 pub(crate) sync_keys: Option<Vec<String>>,
127}
128
129#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
130#[serde(rename_all = "snake_case")]
131pub(crate) enum EmbeddingMigrationModeHttp {
132 MissingOnly,
133 ReindexAll,
134 Tags,
135 Both,
136}
137
138#[derive(Debug, Deserialize)]
139#[serde(rename_all = "camelCase")]
140pub(crate) struct EmbeddingMigrationPreviewHttpRequest {
141 pub(crate) tenant_id: Option<String>,
142 pub(crate) filter: Option<EmbeddingMigrationFilterHttp>,
143 pub(crate) sample_limit: Option<usize>,
144 pub(crate) max_nodes: Option<usize>,
145}
146
147#[derive(Debug, Deserialize)]
148#[serde(rename_all = "camelCase")]
149pub(crate) struct EmbeddingMigrationRunHttpRequest {
150 pub(crate) tenant_id: Option<String>,
151 pub(crate) filter: Option<EmbeddingMigrationFilterHttp>,
152 pub(crate) mode: Option<EmbeddingMigrationModeHttp>,
153 pub(crate) dry_run: Option<bool>,
154 pub(crate) batch_size: Option<usize>,
155 pub(crate) max_nodes: Option<usize>,
156}
157
158#[derive(Debug, Deserialize)]
159#[serde(rename_all = "camelCase")]
160pub(crate) struct ListNodesQuery {
161 pub(crate) limit: Option<usize>,
162 pub(crate) session_id: Option<String>,
163 pub(crate) tenant_id: Option<String>,
164 pub(crate) semantic_tags: Option<Vec<String>>,
165 pub(crate) tags_contains: Option<Vec<String>>,
166 pub(crate) link_rel: Option<String>,
167 pub(crate) link_target: Option<String>,
168 pub(crate) links_to_ref: Option<String>,
169 pub(crate) tag_prefix: Option<String>,
170 pub(crate) has_semantic_links: Option<bool>,
171}
172
173#[derive(Debug, Deserialize)]
174#[serde(rename_all = "camelCase")]
175pub(crate) struct GetMoodsQuery {
176 pub(crate) target_mood: Option<String>,
177 pub(crate) blend: Option<f32>,
178 pub(crate) current_stability: Option<f32>,
179 pub(crate) current_friction: Option<f32>,
180 pub(crate) current_logic: Option<f32>,
181 pub(crate) current_autonomy: Option<f32>,
182}
183
184#[derive(Debug, Deserialize)]
185#[serde(rename_all = "camelCase")]
186pub(crate) struct GraphQuery {
187 pub(crate) limit: Option<usize>,
188 pub(crate) session_id: Option<String>,
189 pub(crate) tenant_id: Option<String>,
190 pub(crate) rel: Option<String>,
191 pub(crate) target_prefix: Option<String>,
192 pub(crate) semantic_tags: Option<Vec<String>>,
193 pub(crate) link_rel: Option<String>,
194 pub(crate) link_target: Option<String>,
195 pub(crate) links_to_ref: Option<String>,
196 pub(crate) tag_prefix: Option<String>,
197 pub(crate) has_semantic_links: Option<bool>,
198}
199
200#[derive(Debug, Deserialize)]
201#[serde(rename_all = "camelCase")]
202pub(crate) struct RenameSessionHttpRequest {
203 pub(crate) source_session_id: String,
204 pub(crate) target_session_id: String,
205 pub(crate) tenant_id: Option<String>,
206 pub(crate) allow_merge: Option<bool>,
207}
208
209#[derive(Debug, Serialize)]
210#[serde(rename_all = "camelCase")]
211pub(crate) struct AvecStateDto {
212 pub(crate) stability: f32,
213 pub(crate) friction: f32,
214 pub(crate) logic: f32,
215 pub(crate) autonomy: f32,
216 pub(crate) psi: f32,
217}
218
219#[derive(Debug, Serialize)]
220#[serde(rename_all = "camelCase")]
221pub(crate) struct SttpNodeDto {
222 pub(crate) raw: String,
223 pub(crate) session_id: String,
224 pub(crate) tier: String,
225 pub(crate) timestamp: DateTime<Utc>,
226 pub(crate) compression_depth: i32,
227 pub(crate) parent_node_id: Option<String>,
228 pub(crate) semantic_tags: Option<Vec<String>>,
229 pub(crate) semantic_links: Option<Vec<SemanticLink>>,
230 pub(crate) user_avec: AvecStateDto,
231 pub(crate) model_avec: AvecStateDto,
232 pub(crate) compression_avec: Option<AvecStateDto>,
233 pub(crate) rho: f32,
234 pub(crate) kappa: f32,
235 pub(crate) psi: f32,
236 pub(crate) sync_key: String,
237 pub(crate) synthetic_id: String,
238}
239
240#[derive(Debug, Serialize)]
241#[serde(rename_all = "camelCase")]
242pub(crate) struct PsiRangeDto {
243 pub(crate) min: f32,
244 pub(crate) max: f32,
245 pub(crate) average: f32,
246}
247
248#[derive(Debug, Serialize)]
249#[serde(rename_all = "camelCase")]
250pub(crate) struct NumericRangeDto {
251 pub(crate) min: f32,
252 pub(crate) max: f32,
253 pub(crate) average: f32,
254}
255
256#[derive(Debug, Serialize)]
257#[serde(rename_all = "camelCase")]
258pub(crate) struct ConfidenceBandSummaryDto {
259 pub(crate) low: usize,
260 pub(crate) medium: usize,
261 pub(crate) high: usize,
262}
263
264#[derive(Debug, Serialize)]
265#[serde(rename_all = "camelCase")]
266pub(crate) struct CalibrationResultDto {
267 pub(crate) previous_avec: AvecStateDto,
268 pub(crate) delta: f32,
269 pub(crate) drift_classification: String,
270 pub(crate) trigger: String,
271 pub(crate) trigger_history: Vec<String>,
272 pub(crate) is_first_calibration: bool,
273}
274
275#[derive(Debug, Serialize)]
276#[serde(rename_all = "camelCase")]
277pub(crate) struct StoreResultDto {
278 pub(crate) node_id: String,
279 pub(crate) psi: f32,
280 pub(crate) valid: bool,
281 pub(crate) validation_error: Option<String>,
282 pub(crate) duplicate_skipped: bool,
283 pub(crate) upsert_status: String,
284}
285
286#[derive(Debug, Serialize)]
287#[serde(rename_all = "camelCase")]
288pub(crate) struct ScoreAvecResultDto {
289 pub(crate) provider: String,
290 pub(crate) model: String,
291 pub(crate) avec: AvecStateDto,
292}
293
294#[derive(Debug, Serialize)]
295#[serde(rename_all = "camelCase")]
296pub(crate) struct RenameSessionResultDto {
297 pub(crate) source_session_id: String,
298 pub(crate) target_session_id: String,
299 pub(crate) moved_nodes: usize,
300 pub(crate) moved_calibrations: usize,
301 pub(crate) scopes_applied: usize,
302}
303
304#[derive(Debug, Serialize)]
305#[serde(rename_all = "camelCase")]
306pub(crate) struct RetrieveResultDto {
307 pub(crate) nodes: Vec<SttpNodeDto>,
308 pub(crate) retrieved: usize,
309 pub(crate) psi_range: PsiRangeDto,
310}
311
312#[derive(Debug, Serialize)]
313#[serde(rename_all = "camelCase")]
314pub(crate) struct ListNodesResultDto {
315 pub(crate) nodes: Vec<SttpNodeDto>,
316 pub(crate) retrieved: usize,
317}
318
319#[derive(Debug, Serialize)]
320#[serde(rename_all = "camelCase")]
321pub(crate) struct MoodPresetDto {
322 pub(crate) name: String,
323 pub(crate) description: String,
324 pub(crate) avec: AvecStateDto,
325}
326
327#[derive(Debug, Serialize)]
328#[serde(rename_all = "camelCase")]
329pub(crate) struct MoodSwapPreviewDto {
330 pub(crate) target_mood: String,
331 pub(crate) blend: f32,
332 pub(crate) current: AvecStateDto,
333 pub(crate) target: AvecStateDto,
334 pub(crate) blended: AvecStateDto,
335}
336
337#[derive(Debug, Serialize)]
338#[serde(rename_all = "camelCase")]
339pub(crate) struct MoodCatalogResultDto {
340 pub(crate) presets: Vec<MoodPresetDto>,
341 pub(crate) apply_guide: String,
342 pub(crate) swap_preview: Option<MoodSwapPreviewDto>,
343}
344
345#[derive(Debug, Serialize)]
346#[serde(rename_all = "camelCase")]
347pub(crate) struct MonthlyRollupResultDto {
348 pub(crate) success: bool,
349 pub(crate) node_id: String,
350 pub(crate) raw_node: String,
351 pub(crate) error: Option<String>,
352 pub(crate) source_nodes: usize,
353 pub(crate) parent_reference: Option<String>,
354 pub(crate) user_average: AvecStateDto,
355 pub(crate) model_average: AvecStateDto,
356 pub(crate) compression_average: AvecStateDto,
357 pub(crate) rho_range: NumericRangeDto,
358 pub(crate) kappa_range: NumericRangeDto,
359 pub(crate) psi_range: NumericRangeDto,
360 pub(crate) rho_bands: ConfidenceBandSummaryDto,
361 pub(crate) kappa_bands: ConfidenceBandSummaryDto,
362}
363
364#[derive(Debug, Serialize)]
365#[serde(rename_all = "camelCase")]
366pub(crate) struct ScopeRekeyResultDto {
367 pub(crate) source_tenant_id: String,
368 pub(crate) source_session_id: String,
369 pub(crate) target_tenant_id: String,
370 pub(crate) target_session_id: String,
371 pub(crate) temporal_nodes: usize,
372 pub(crate) calibrations: usize,
373 pub(crate) target_temporal_nodes: usize,
374 pub(crate) target_calibrations: usize,
375 pub(crate) applied: bool,
376 pub(crate) conflict: bool,
377 pub(crate) message: Option<String>,
378}
379
380#[derive(Debug, Serialize)]
381#[serde(rename_all = "camelCase")]
382pub(crate) struct BatchRekeyResultDto {
383 pub(crate) dry_run: bool,
384 pub(crate) requested_node_ids: usize,
385 pub(crate) resolved_node_ids: usize,
386 pub(crate) missing_node_ids: Vec<String>,
387 pub(crate) scopes: Vec<ScopeRekeyResultDto>,
388 pub(crate) temporal_nodes_updated: usize,
389 pub(crate) calibrations_updated: usize,
390 pub(crate) updated_scopes: usize,
391 pub(crate) conflict_scopes: usize,
392}
393
394#[derive(Debug, Serialize)]
395#[serde(rename_all = "camelCase")]
396pub(crate) struct EmbeddingMigrationSampleDto {
397 pub(crate) sync_key: String,
398 pub(crate) session_id: String,
399 pub(crate) tier: String,
400 pub(crate) has_embedding: bool,
401 pub(crate) embedding_model: Option<String>,
402 pub(crate) embedding_dimensions: Option<usize>,
403 pub(crate) embedded_at: Option<DateTime<Utc>>,
404 pub(crate) updated_at: DateTime<Utc>,
405 pub(crate) context_summary: Option<String>,
406}
407
408#[derive(Debug, Serialize)]
409#[serde(rename_all = "camelCase")]
410pub(crate) struct EmbeddingMigrationPreviewResultDto {
411 pub(crate) total_candidates: usize,
412 pub(crate) sample: Vec<EmbeddingMigrationSampleDto>,
413 pub(crate) provider_available: bool,
414 pub(crate) provider_model: Option<String>,
415}
416
417#[derive(Debug, Serialize)]
418#[serde(rename_all = "camelCase")]
419pub(crate) struct EmbeddingMigrationRunResultDto {
420 pub(crate) scanned: usize,
421 pub(crate) selected: usize,
422 pub(crate) updated: usize,
423 pub(crate) skipped: usize,
424 pub(crate) failed: usize,
425 pub(crate) duplicate: usize,
426 pub(crate) started_at: DateTime<Utc>,
427 pub(crate) completed_at: DateTime<Utc>,
428 pub(crate) provider_model: Option<String>,
429 pub(crate) dry_run: bool,
430 pub(crate) mode: String,
431 pub(crate) failure_reasons: Vec<String>,
432}
433
434#[derive(Debug, Serialize)]
435pub(crate) struct GraphResponse {
436 pub(crate) sessions: Vec<serde_json::Value>,
437 pub(crate) nodes: Vec<serde_json::Value>,
438 pub(crate) edges: Vec<serde_json::Value>,
439 pub(crate) retrieved: usize,
440}
441
442#[derive(Debug, Deserialize)]
443#[serde(rename_all = "camelCase")]
444pub(crate) struct EvictHttpRequest {
445 pub(crate) tenant_id: Option<String>,
446 pub(crate) session_id: String,
447 pub(crate) mode: Option<String>,
448 pub(crate) sync_keys: Option<Vec<String>>,
449 pub(crate) node_ids: Option<Vec<String>>,
450 pub(crate) semantic_tags: Option<Vec<String>>,
451 pub(crate) tags_contains: Option<Vec<String>>,
452 pub(crate) link_rel: Option<String>,
453 pub(crate) link_target: Option<String>,
454 pub(crate) links_to_ref: Option<String>,
455 pub(crate) tag_prefix: Option<String>,
456 pub(crate) has_semantic_links: Option<bool>,
457 pub(crate) purge_session: Option<bool>,
458 pub(crate) dry_run: Option<bool>,
459 pub(crate) force: Option<bool>,
460 pub(crate) max_nodes: Option<usize>,
461 pub(crate) include_calibration: Option<bool>,
462 pub(crate) include_checkpoints: Option<bool>,
463 pub(crate) tiers: Option<Vec<String>>,
464}
465
466#[derive(Debug, Serialize)]
467#[serde(rename_all = "camelCase")]
468pub(crate) struct EvictHttpResultDto {
469 pub(crate) dry_run: bool,
470 pub(crate) deleted: usize,
471 pub(crate) blocked: usize,
472 pub(crate) not_found: usize,
473 pub(crate) skipped: usize,
474 pub(crate) would_delete: Vec<String>,
475 pub(crate) calibrations_deleted: usize,
476 pub(crate) checkpoints_deleted: usize,
477 pub(crate) records: Vec<serde_json::Value>,
478}
479
480pub(crate) type ApiResult<T> = Result<Json<T>, (axum::http::StatusCode, Json<ErrorResponse>)>;
481
482pub(crate) enum CorsAllowedOrigins {
483 Any,
484 Explicit(Vec<HeaderValue>),
485}