Skip to main content

locus_sdk/interface/
dto.rs

1use chrono::{DateTime, Utc};
2use locus_core_rs::domain::models::{AvecState, PsiRange, SttpNode};
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6use crate::application::memory_composition::{
7    CompositeInputItem, CompositeNodeFromTextOptions, CompositeNodeFromTextRequest,
8    CompositeNodeFromTextResult, CompositeRole, CompositeRoleAvecOverrides,
9    MemoryDailyRollupRequest, MemoryRecallWithExplainResult, MemoryTransformThenRecallRequest,
10    MemoryTransformThenRecallResult,
11};
12use crate::domain::memory::{
13    FallbackPolicy, MemoryAggregateRequest, MemoryAggregateResult, MemoryExplainRequest,
14    MemoryExplainResult, MemoryFilter, MemoryFindRequest, MemoryFindResult, MemoryGroupBy,
15    MemoryPage, MemoryRecallRequest, MemoryRecallResult, MemorySchemaResult, MemoryScope,
16    MemoryScoring, MemorySort, MemoryTransformOperation, MemoryTransformRequest,
17    MemoryTransformResult, MetricRange, NumericStats, RetrievalPath, StrictnessMode,
18};
19use crate::domain::reflex::{
20    MemoryAction, MemoryPersistHint, MemoryPropositions, MemoryReflex, MemoryReflexKind, ReflexGate,
21};
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
24#[serde(rename_all = "camelCase")]
25pub struct AvecStateDto {
26    pub stability: f32,
27    pub friction: f32,
28    pub logic: f32,
29    pub autonomy: f32,
30    pub psi: f32,
31}
32
33impl From<AvecStateDto> for AvecState {
34    fn from(value: AvecStateDto) -> Self {
35        Self {
36            stability: value.stability,
37            friction: value.friction,
38            logic: value.logic,
39            autonomy: value.autonomy,
40        }
41    }
42}
43
44impl From<AvecState> for AvecStateDto {
45    fn from(value: AvecState) -> Self {
46        Self {
47            stability: value.stability,
48            friction: value.friction,
49            logic: value.logic,
50            autonomy: value.autonomy,
51            psi: value.psi(),
52        }
53    }
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize)]
57#[serde(rename_all = "camelCase")]
58pub struct PsiRangeDto {
59    pub min: f32,
60    pub max: f32,
61    pub average: f32,
62}
63
64impl From<PsiRange> for PsiRangeDto {
65    fn from(value: PsiRange) -> Self {
66        Self {
67            min: value.min,
68            max: value.max,
69            average: value.average,
70        }
71    }
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
75#[serde(rename_all = "camelCase")]
76pub struct MemoryScopeDto {
77    pub tenant_id: Option<String>,
78    pub session_ids: Option<Vec<String>>,
79    pub tiers: Option<Vec<String>>,
80    pub from_utc: Option<DateTime<Utc>>,
81    pub to_utc: Option<DateTime<Utc>>,
82}
83
84impl From<MemoryScopeDto> for MemoryScope {
85    fn from(value: MemoryScopeDto) -> Self {
86        Self {
87            tenant_id: value.tenant_id,
88            session_ids: value.session_ids,
89            tiers: value.tiers,
90            from_utc: value.from_utc,
91            to_utc: value.to_utc,
92        }
93    }
94}
95
96impl From<MemoryScope> for MemoryScopeDto {
97    fn from(value: MemoryScope) -> Self {
98        Self {
99            tenant_id: value.tenant_id,
100            session_ids: value.session_ids,
101            tiers: value.tiers,
102            from_utc: value.from_utc,
103            to_utc: value.to_utc,
104        }
105    }
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize)]
109#[serde(rename_all = "camelCase")]
110pub struct MemoryFilterDto {
111    pub has_embedding: Option<bool>,
112    pub embedding_model: Option<String>,
113    pub psi: Option<MetricRange>,
114    pub rho: Option<MetricRange>,
115    pub kappa: Option<MetricRange>,
116    pub text_contains: Option<String>,
117    pub tags_contains: Option<Vec<String>>,
118    pub has_tag: Option<String>,
119    pub indexed_tags: Option<Vec<String>>,
120    pub tag_prefix: Option<String>,
121    pub has_semantic_links: Option<bool>,
122    pub link_rel: Option<String>,
123    pub link_target: Option<String>,
124    pub links_to_ref: Option<String>,
125}
126
127impl From<MemoryFilterDto> for MemoryFilter {
128    fn from(value: MemoryFilterDto) -> Self {
129        Self {
130            has_embedding: value.has_embedding,
131            embedding_model: value.embedding_model,
132            psi: value.psi,
133            rho: value.rho,
134            kappa: value.kappa,
135            text_contains: value.text_contains,
136            tags_contains: value.tags_contains,
137            has_tag: value.has_tag,
138            indexed_tags: value.indexed_tags,
139            tag_prefix: value.tag_prefix,
140            has_semantic_links: value.has_semantic_links,
141            link_rel: value.link_rel,
142            link_target: value.link_target,
143            links_to_ref: value.links_to_ref,
144        }
145    }
146}
147
148impl From<MemoryFilter> for MemoryFilterDto {
149    fn from(value: MemoryFilter) -> Self {
150        Self {
151            has_embedding: value.has_embedding,
152            embedding_model: value.embedding_model,
153            psi: value.psi,
154            rho: value.rho,
155            kappa: value.kappa,
156            text_contains: value.text_contains,
157            tags_contains: value.tags_contains,
158            has_tag: value.has_tag,
159            indexed_tags: value.indexed_tags,
160            tag_prefix: value.tag_prefix,
161            has_semantic_links: value.has_semantic_links,
162            link_rel: value.link_rel,
163            link_target: value.link_target,
164            links_to_ref: value.links_to_ref,
165        }
166    }
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize)]
170#[serde(rename_all = "camelCase")]
171pub struct MemoryPageDto {
172    pub limit: usize,
173    pub cursor: Option<String>,
174}
175
176impl From<MemoryPageDto> for MemoryPage {
177    fn from(value: MemoryPageDto) -> Self {
178        Self {
179            limit: value.limit,
180            cursor: value.cursor,
181        }
182    }
183}
184
185impl From<MemoryPage> for MemoryPageDto {
186    fn from(value: MemoryPage) -> Self {
187        Self {
188            limit: value.limit,
189            cursor: value.cursor,
190        }
191    }
192}
193
194#[derive(Debug, Clone, Serialize, Deserialize)]
195#[serde(rename_all = "camelCase")]
196pub struct MemoryScoringDto {
197    pub resonance_weight: f32,
198    pub semantic_weight: f32,
199    pub lexical_weight: f32,
200    pub alpha: f32,
201    pub beta: f32,
202    #[serde(default)]
203    pub gamma: f32,
204    pub fallback_policy: FallbackPolicy,
205    pub strictness: StrictnessMode,
206}
207
208impl From<MemoryScoringDto> for MemoryScoring {
209    fn from(value: MemoryScoringDto) -> Self {
210        Self {
211            resonance_weight: value.resonance_weight,
212            semantic_weight: value.semantic_weight,
213            lexical_weight: value.lexical_weight,
214            alpha: value.alpha,
215            beta: value.beta,
216            gamma: value.gamma,
217            fallback_policy: value.fallback_policy,
218            strictness: value.strictness,
219        }
220    }
221}
222
223impl From<MemoryScoring> for MemoryScoringDto {
224    fn from(value: MemoryScoring) -> Self {
225        Self {
226            resonance_weight: value.resonance_weight,
227            semantic_weight: value.semantic_weight,
228            lexical_weight: value.lexical_weight,
229            alpha: value.alpha,
230            beta: value.beta,
231            gamma: value.gamma,
232            fallback_policy: value.fallback_policy,
233            strictness: value.strictness,
234        }
235    }
236}
237
238#[derive(Debug, Clone, Serialize, Deserialize)]
239#[serde(rename_all = "camelCase")]
240pub struct MemoryFindRequestDto {
241    pub scope: MemoryScopeDto,
242    pub filter: MemoryFilterDto,
243    pub page: MemoryPageDto,
244    pub sort: MemorySort,
245}
246
247impl From<MemoryFindRequestDto> for MemoryFindRequest {
248    fn from(value: MemoryFindRequestDto) -> Self {
249        Self {
250            scope: value.scope.into(),
251            filter: value.filter.into(),
252            page: value.page.into(),
253            sort: value.sort,
254        }
255    }
256}
257
258#[derive(Debug, Clone, Serialize, Deserialize)]
259#[serde(rename_all = "camelCase")]
260pub struct MemoryRecallRequestDto {
261    pub scope: MemoryScopeDto,
262    pub filter: MemoryFilterDto,
263    pub page: MemoryPageDto,
264    pub scoring: MemoryScoringDto,
265    pub current_avec: Option<AvecStateDto>,
266    pub query_text: Option<String>,
267    pub query_embedding: Option<Vec<f32>>,
268    pub query_tag_embedding: Option<Vec<f32>>,
269}
270
271impl From<MemoryRecallRequestDto> for MemoryRecallRequest {
272    fn from(value: MemoryRecallRequestDto) -> Self {
273        Self {
274            scope: value.scope.into(),
275            filter: value.filter.into(),
276            page: value.page.into(),
277            scoring: value.scoring.into(),
278            current_avec: value.current_avec.map(Into::into),
279            query_text: value.query_text,
280            query_embedding: value.query_embedding,
281            query_tag_embedding: value.query_tag_embedding,
282        }
283    }
284}
285
286#[derive(Debug, Clone, Serialize)]
287#[serde(rename_all = "camelCase")]
288pub struct MemoryNodeDto {
289    pub raw: String,
290    pub session_id: String,
291    pub tier: String,
292    pub timestamp: DateTime<Utc>,
293    pub compression_depth: i32,
294    pub parent_node_id: Option<String>,
295    pub sync_key: String,
296    pub context_summary: Option<String>,
297    pub embedding_model: Option<String>,
298    pub embedding_dimensions: Option<usize>,
299    pub embedded_at: Option<DateTime<Utc>>,
300    pub rho: f32,
301    pub kappa: f32,
302    pub psi: f32,
303    pub user_avec: AvecStateDto,
304    pub model_avec: AvecStateDto,
305    pub compression_avec: Option<AvecStateDto>,
306    pub updated_at: DateTime<Utc>,
307}
308
309impl From<SttpNode> for MemoryNodeDto {
310    fn from(value: SttpNode) -> Self {
311        Self {
312            raw: value.raw,
313            session_id: value.session_id,
314            tier: value.tier,
315            timestamp: value.timestamp,
316            compression_depth: value.compression_depth,
317            parent_node_id: value.parent_node_id,
318            sync_key: value.sync_key,
319            context_summary: value.context_summary,
320            embedding_model: value.embedding_model,
321            embedding_dimensions: value.embedding_dimensions,
322            embedded_at: value.embedded_at,
323            rho: value.rho,
324            kappa: value.kappa,
325            psi: value.psi,
326            user_avec: value.user_avec.into(),
327            model_avec: value.model_avec.into(),
328            compression_avec: value.compression_avec.map(Into::into),
329            updated_at: value.updated_at,
330        }
331    }
332}
333
334#[derive(Debug, Clone, Serialize)]
335#[serde(rename_all = "camelCase")]
336pub struct MemoryFindResponseDto {
337    pub nodes: Vec<MemoryNodeDto>,
338    pub retrieved: usize,
339    pub has_more: bool,
340    pub next_cursor: Option<String>,
341}
342
343impl From<MemoryFindResult> for MemoryFindResponseDto {
344    fn from(value: MemoryFindResult) -> Self {
345        Self {
346            nodes: value.nodes.into_iter().map(Into::into).collect(),
347            retrieved: value.retrieved,
348            has_more: value.has_more,
349            next_cursor: value.next_cursor,
350        }
351    }
352}
353
354#[derive(Debug, Clone, Serialize)]
355#[serde(rename_all = "camelCase")]
356pub struct MemoryRecallResponseDto {
357    pub nodes: Vec<MemoryNodeDto>,
358    pub retrieved: usize,
359    pub psi_range: PsiRangeDto,
360    pub retrieval_path: RetrievalPath,
361    pub has_more: bool,
362    pub next_cursor: Option<String>,
363}
364
365impl From<MemoryRecallResult> for MemoryRecallResponseDto {
366    fn from(value: MemoryRecallResult) -> Self {
367        Self {
368            nodes: value.nodes.into_iter().map(Into::into).collect(),
369            retrieved: value.retrieved,
370            psi_range: value.psi_range.into(),
371            retrieval_path: value.retrieval_path,
372            has_more: value.has_more,
373            next_cursor: value.next_cursor,
374        }
375    }
376}
377
378#[derive(Debug, Clone, Serialize, Deserialize)]
379#[serde(rename_all = "camelCase")]
380pub struct NumericStatsDto {
381    pub min: f32,
382    pub max: f32,
383    pub average: f32,
384}
385
386impl From<NumericStats> for NumericStatsDto {
387    fn from(value: NumericStats) -> Self {
388        Self {
389            min: value.min,
390            max: value.max,
391            average: value.average,
392        }
393    }
394}
395
396#[derive(Debug, Clone, Serialize, Deserialize)]
397#[serde(rename_all = "camelCase")]
398pub struct MemoryAggregateRequestDto {
399    pub scope: MemoryScopeDto,
400    pub filter: MemoryFilterDto,
401    pub group_by: MemoryGroupBy,
402    pub max_groups: usize,
403    pub max_nodes: usize,
404}
405
406impl From<MemoryAggregateRequestDto> for MemoryAggregateRequest {
407    fn from(value: MemoryAggregateRequestDto) -> Self {
408        Self {
409            scope: value.scope.into(),
410            filter: value.filter.into(),
411            group_by: value.group_by,
412            max_groups: value.max_groups,
413            max_nodes: value.max_nodes,
414        }
415    }
416}
417
418#[derive(Debug, Clone, Serialize)]
419#[serde(rename_all = "camelCase")]
420pub struct MemoryAggregateGroupDto {
421    pub key: String,
422    pub node_count: usize,
423    pub embedding_coverage: f32,
424    pub avg_user_avec: AvecStateDto,
425    pub avg_model_avec: AvecStateDto,
426    pub avg_compression_avec: Option<AvecStateDto>,
427    pub psi_stats: NumericStatsDto,
428    pub rho_stats: NumericStatsDto,
429    pub kappa_stats: NumericStatsDto,
430}
431
432#[derive(Debug, Clone, Serialize)]
433#[serde(rename_all = "camelCase")]
434pub struct MemoryAggregateResponseDto {
435    pub groups: Vec<MemoryAggregateGroupDto>,
436    pub total_groups: usize,
437    pub scanned_nodes: usize,
438}
439
440impl From<MemoryAggregateResult> for MemoryAggregateResponseDto {
441    fn from(value: MemoryAggregateResult) -> Self {
442        Self {
443            groups: value
444                .groups
445                .into_iter()
446                .map(|group| MemoryAggregateGroupDto {
447                    key: group.key,
448                    node_count: group.node_count,
449                    embedding_coverage: group.embedding_coverage,
450                    avg_user_avec: group.avg_user_avec.into(),
451                    avg_model_avec: group.avg_model_avec.into(),
452                    avg_compression_avec: group.avg_compression_avec.map(Into::into),
453                    psi_stats: group.psi_stats.into(),
454                    rho_stats: group.rho_stats.into(),
455                    kappa_stats: group.kappa_stats.into(),
456                })
457                .collect(),
458            total_groups: value.total_groups,
459            scanned_nodes: value.scanned_nodes,
460        }
461    }
462}
463
464#[derive(Debug, Clone, Serialize, Deserialize)]
465#[serde(rename_all = "camelCase")]
466pub struct MemoryTransformRequestDto {
467    pub scope: MemoryScopeDto,
468    pub filter: MemoryFilterDto,
469    pub operation: MemoryTransformOperation,
470    pub dry_run: bool,
471    pub batch_size: usize,
472    pub max_nodes: usize,
473    pub provider_id: Option<String>,
474    pub model: Option<String>,
475}
476
477impl From<MemoryTransformRequestDto> for MemoryTransformRequest {
478    fn from(value: MemoryTransformRequestDto) -> Self {
479        Self {
480            scope: value.scope.into(),
481            filter: value.filter.into(),
482            operation: value.operation,
483            dry_run: value.dry_run,
484            batch_size: value.batch_size,
485            max_nodes: value.max_nodes,
486            provider_id: value.provider_id,
487            model: value.model,
488        }
489    }
490}
491
492#[derive(Debug, Clone, Serialize)]
493#[serde(rename_all = "camelCase")]
494pub struct MemoryTransformResponseDto {
495    pub scanned: usize,
496    pub selected: usize,
497    pub updated: usize,
498    pub skipped: usize,
499    pub failed: usize,
500    pub duplicate: usize,
501    pub started_at: DateTime<Utc>,
502    pub completed_at: DateTime<Utc>,
503    pub failures: Vec<String>,
504}
505
506impl From<MemoryTransformResult> for MemoryTransformResponseDto {
507    fn from(value: MemoryTransformResult) -> Self {
508        Self {
509            scanned: value.scanned,
510            selected: value.selected,
511            updated: value.updated,
512            skipped: value.skipped,
513            failed: value.failed,
514            duplicate: value.duplicate,
515            started_at: value.started_at,
516            completed_at: value.completed_at,
517            failures: value.failures,
518        }
519    }
520}
521
522#[derive(Debug, Clone, Serialize, Deserialize)]
523#[serde(rename_all = "camelCase")]
524pub struct MemoryDailyRollupRequestDto {
525    pub scope: MemoryScopeDto,
526    pub filter: MemoryFilterDto,
527    pub max_days: usize,
528    pub max_nodes: usize,
529}
530
531impl From<MemoryDailyRollupRequestDto> for MemoryDailyRollupRequest {
532    fn from(value: MemoryDailyRollupRequestDto) -> Self {
533        Self {
534            scope: value.scope.into(),
535            filter: value.filter.into(),
536            max_days: value.max_days,
537            max_nodes: value.max_nodes,
538        }
539    }
540}
541
542#[derive(Debug, Clone, Serialize)]
543#[serde(rename_all = "camelCase")]
544pub struct MemoryRecallWithExplainResponseDto {
545    pub recall: MemoryRecallResponseDto,
546    pub explain: MemoryExplainResponseDto,
547}
548
549impl From<MemoryRecallWithExplainResult> for MemoryRecallWithExplainResponseDto {
550    fn from(value: MemoryRecallWithExplainResult) -> Self {
551        Self {
552            recall: value.recall.into(),
553            explain: value.explain.into(),
554        }
555    }
556}
557
558#[derive(Debug, Clone, Serialize, Deserialize)]
559#[serde(rename_all = "camelCase")]
560pub struct MemoryTransformThenRecallRequestDto {
561    pub transform: MemoryTransformRequestDto,
562    pub recall: MemoryRecallRequestDto,
563}
564
565impl From<MemoryTransformThenRecallRequestDto> for MemoryTransformThenRecallRequest {
566    fn from(value: MemoryTransformThenRecallRequestDto) -> Self {
567        Self {
568            transform: value.transform.into(),
569            recall: value.recall.into(),
570        }
571    }
572}
573
574#[derive(Debug, Clone, Serialize)]
575#[serde(rename_all = "camelCase")]
576pub struct MemoryTransformThenRecallResponseDto {
577    pub transform: MemoryTransformResponseDto,
578    pub recall: MemoryRecallResponseDto,
579}
580
581impl From<MemoryTransformThenRecallResult> for MemoryTransformThenRecallResponseDto {
582    fn from(value: MemoryTransformThenRecallResult) -> Self {
583        Self {
584            transform: value.transform.into(),
585            recall: value.recall.into(),
586        }
587    }
588}
589
590#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
591#[serde(rename_all = "snake_case")]
592pub enum CompositeRoleDto {
593    User,
594    Model,
595    Document,
596    Conversation,
597}
598
599impl From<CompositeRoleDto> for CompositeRole {
600    fn from(value: CompositeRoleDto) -> Self {
601        match value {
602            CompositeRoleDto::User => CompositeRole::User,
603            CompositeRoleDto::Model => CompositeRole::Model,
604            CompositeRoleDto::Document => CompositeRole::Document,
605            CompositeRoleDto::Conversation => CompositeRole::Conversation,
606        }
607    }
608}
609
610impl From<CompositeRole> for CompositeRoleDto {
611    fn from(value: CompositeRole) -> Self {
612        match value {
613            CompositeRole::User => CompositeRoleDto::User,
614            CompositeRole::Model => CompositeRoleDto::Model,
615            CompositeRole::Document => CompositeRoleDto::Document,
616            CompositeRole::Conversation => CompositeRoleDto::Conversation,
617        }
618    }
619}
620
621#[derive(Debug, Clone, Serialize, Deserialize)]
622#[serde(rename_all = "camelCase")]
623pub struct CompositeInputItemDto {
624    pub role: CompositeRoleDto,
625    pub text: String,
626    pub avec_override: Option<AvecStateDto>,
627    #[serde(default)]
628    pub context: Vec<CompositeInputItemDto>,
629}
630
631impl From<CompositeInputItemDto> for CompositeInputItem {
632    fn from(value: CompositeInputItemDto) -> Self {
633        Self {
634            role: value.role.into(),
635            text: value.text,
636            avec_override: value.avec_override.map(Into::into),
637            context: value.context.into_iter().map(Into::into).collect(),
638        }
639    }
640}
641
642impl From<CompositeInputItem> for CompositeInputItemDto {
643    fn from(value: CompositeInputItem) -> Self {
644        Self {
645            role: value.role.into(),
646            text: value.text,
647            avec_override: value.avec_override.map(Into::into),
648            context: value.context.into_iter().map(Into::into).collect(),
649        }
650    }
651}
652
653#[derive(Debug, Clone, Default, Serialize, Deserialize)]
654#[serde(rename_all = "camelCase")]
655pub struct CompositeRoleAvecOverridesDto {
656    pub user: Option<AvecStateDto>,
657    pub model: Option<AvecStateDto>,
658    pub document: Option<AvecStateDto>,
659    pub conversation: Option<AvecStateDto>,
660}
661
662impl From<CompositeRoleAvecOverridesDto> for CompositeRoleAvecOverrides {
663    fn from(value: CompositeRoleAvecOverridesDto) -> Self {
664        Self {
665            user: value.user.map(Into::into),
666            model: value.model.map(Into::into),
667            document: value.document.map(Into::into),
668            conversation: value.conversation.map(Into::into),
669        }
670    }
671}
672
673impl From<CompositeRoleAvecOverrides> for CompositeRoleAvecOverridesDto {
674    fn from(value: CompositeRoleAvecOverrides) -> Self {
675        Self {
676            user: value.user.map(Into::into),
677            model: value.model.map(Into::into),
678            document: value.document.map(Into::into),
679            conversation: value.conversation.map(Into::into),
680        }
681    }
682}
683
684#[derive(Debug, Clone, Serialize, Deserialize)]
685#[serde(rename_all = "camelCase")]
686pub struct CompositeNodeFromTextOptionsDto {
687    pub role_avec: CompositeRoleAvecOverridesDto,
688    pub global_avec: Option<AvecStateDto>,
689    pub allow_llm_avec_fallback: bool,
690    pub max_recursion_depth: usize,
691}
692
693impl From<CompositeNodeFromTextOptionsDto> for CompositeNodeFromTextOptions {
694    fn from(value: CompositeNodeFromTextOptionsDto) -> Self {
695        Self {
696            role_avec: value.role_avec.into(),
697            global_avec: value.global_avec.map(Into::into),
698            allow_llm_avec_fallback: value.allow_llm_avec_fallback,
699            max_recursion_depth: value.max_recursion_depth,
700        }
701    }
702}
703
704impl From<CompositeNodeFromTextOptions> for CompositeNodeFromTextOptionsDto {
705    fn from(value: CompositeNodeFromTextOptions) -> Self {
706        Self {
707            role_avec: value.role_avec.into(),
708            global_avec: value.global_avec.map(Into::into),
709            allow_llm_avec_fallback: value.allow_llm_avec_fallback,
710            max_recursion_depth: value.max_recursion_depth,
711        }
712    }
713}
714
715#[derive(Debug, Clone, Serialize, Deserialize)]
716#[serde(rename_all = "camelCase")]
717pub struct CompositeNodeFromTextRequestDto {
718    pub items: Vec<CompositeInputItemDto>,
719    pub options: CompositeNodeFromTextOptionsDto,
720}
721
722impl From<CompositeNodeFromTextRequestDto> for CompositeNodeFromTextRequest {
723    fn from(value: CompositeNodeFromTextRequestDto) -> Self {
724        Self {
725            items: value.items.into_iter().map(Into::into).collect(),
726            options: value.options.into(),
727        }
728    }
729}
730
731#[derive(Debug, Clone, Serialize)]
732#[serde(rename_all = "camelCase")]
733pub struct CompositeNodeFromTextResponseDto {
734    pub content: Value,
735    pub resolved_avec_count: usize,
736    pub unresolved_avec_count: usize,
737    pub requires_llm_avec: bool,
738}
739
740impl From<CompositeNodeFromTextResult> for CompositeNodeFromTextResponseDto {
741    fn from(value: CompositeNodeFromTextResult) -> Self {
742        Self {
743            content: value.content,
744            resolved_avec_count: value.resolved_avec_count,
745            unresolved_avec_count: value.unresolved_avec_count,
746            requires_llm_avec: value.requires_llm_avec,
747        }
748    }
749}
750
751#[derive(Debug, Clone, Serialize, Deserialize)]
752#[serde(rename_all = "camelCase")]
753pub struct MemoryExplainRequestDto {
754    pub recall: MemoryRecallRequestDto,
755}
756
757impl From<MemoryExplainRequestDto> for MemoryExplainRequest {
758    fn from(value: MemoryExplainRequestDto) -> Self {
759        Self {
760            recall: value.recall.into(),
761        }
762    }
763}
764
765#[derive(Debug, Clone, Serialize)]
766#[serde(rename_all = "camelCase")]
767pub struct MemoryExplainStageDto {
768    pub stage: String,
769    pub count: usize,
770}
771
772#[derive(Debug, Clone, Serialize)]
773#[serde(rename_all = "camelCase")]
774pub struct MemoryExplainResponseDto {
775    pub retrieval_path: RetrievalPath,
776    pub fallback_triggered: bool,
777    pub fallback_reason: Option<String>,
778    pub stages: Vec<MemoryExplainStageDto>,
779    pub scoring: MemoryScoringDto,
780}
781
782impl From<MemoryExplainResult> for MemoryExplainResponseDto {
783    fn from(value: MemoryExplainResult) -> Self {
784        Self {
785            retrieval_path: value.retrieval_path,
786            fallback_triggered: value.fallback_triggered,
787            fallback_reason: value.fallback_reason,
788            stages: value
789                .stages
790                .into_iter()
791                .map(|stage| MemoryExplainStageDto {
792                    stage: stage.stage,
793                    count: stage.count,
794                })
795                .collect(),
796            scoring: value.scoring.into(),
797        }
798    }
799}
800
801#[derive(Debug, Clone, Serialize)]
802#[serde(rename_all = "camelCase")]
803pub struct MemorySchemaResponseDto {
804    pub schema_version: String,
805    pub sort_fields: Vec<String>,
806    pub filter_fields: Vec<String>,
807    pub group_by_fields: Vec<String>,
808    pub fallback_policies: Vec<String>,
809    pub strictness_modes: Vec<String>,
810    pub transform_operations: Vec<String>,
811    pub evict_operations: Vec<String>,
812    pub reflex_actions: Vec<String>,
813    pub decision_types: Vec<String>,
814}
815
816impl From<MemorySchemaResult> for MemorySchemaResponseDto {
817    fn from(value: MemorySchemaResult) -> Self {
818        Self {
819            schema_version: value.schema_version,
820            sort_fields: value.sort_fields,
821            filter_fields: value.filter_fields,
822            group_by_fields: value.group_by_fields,
823            fallback_policies: value.fallback_policies,
824            strictness_modes: value.strictness_modes,
825            transform_operations: value.transform_operations,
826            evict_operations: value.evict_operations,
827            reflex_actions: value.reflex_actions,
828            decision_types: value.decision_types,
829        }
830    }
831}
832
833impl From<MemoryRecallRequest> for MemoryRecallRequestDto {
834    fn from(value: MemoryRecallRequest) -> Self {
835        Self {
836            scope: value.scope.into(),
837            filter: value.filter.into(),
838            page: value.page.into(),
839            scoring: value.scoring.into(),
840            current_avec: value.current_avec.map(Into::into),
841            query_text: value.query_text,
842            query_embedding: value.query_embedding,
843            query_tag_embedding: value.query_tag_embedding,
844        }
845    }
846}
847
848impl From<MemoryFindRequest> for MemoryFindRequestDto {
849    fn from(value: MemoryFindRequest) -> Self {
850        Self {
851            scope: value.scope.into(),
852            filter: value.filter.into(),
853            page: value.page.into(),
854            sort: value.sort,
855        }
856    }
857}
858
859impl From<MemoryAggregateRequest> for MemoryAggregateRequestDto {
860    fn from(value: MemoryAggregateRequest) -> Self {
861        Self {
862            scope: value.scope.into(),
863            filter: value.filter.into(),
864            group_by: value.group_by,
865            max_groups: value.max_groups,
866            max_nodes: value.max_nodes,
867        }
868    }
869}
870
871/// Serializable reflex envelope. This is the message a host publishes.
872#[derive(Debug, Clone, Serialize, Deserialize)]
873#[serde(rename_all = "camelCase")]
874pub struct MemoryReflexResponseDto {
875    pub schema_version: String,
876    pub stimulus_id: String,
877    pub stimulus_text: String,
878    #[serde(default, skip_serializing_if = "Option::is_none")]
879    pub role: Option<String>,
880    pub scope: MemoryScopeDto,
881    pub kind: MemoryReflexKind,
882    pub action: MemoryAction,
883    pub topic: String,
884    pub salience: f32,
885    pub salience_label: String,
886    pub salience_confidence: f32,
887    pub confidence: f32,
888    pub propositions: MemoryPropositions,
889    pub gate: ReflexGate,
890    #[serde(default, skip_serializing_if = "Vec::is_empty")]
891    pub companions: Vec<MemoryAction>,
892    #[serde(default, skip_serializing_if = "Option::is_none")]
893    pub recall: Option<MemoryRecallRequestDto>,
894    #[serde(default, skip_serializing_if = "Option::is_none")]
895    pub find: Option<MemoryFindRequestDto>,
896    #[serde(default, skip_serializing_if = "Option::is_none")]
897    pub aggregate: Option<MemoryAggregateRequestDto>,
898    #[serde(default, skip_serializing_if = "Option::is_none")]
899    pub persist: Option<MemoryPersistHint>,
900    pub decider_id: String,
901    #[serde(default, skip_serializing_if = "Option::is_none")]
902    pub checkpoint: Option<String>,
903    #[serde(default)]
904    pub metadata: serde_json::Map<String, Value>,
905}
906
907impl From<MemoryReflex> for MemoryReflexResponseDto {
908    fn from(value: MemoryReflex) -> Self {
909        Self {
910            schema_version: value.schema_version,
911            stimulus_id: value.stimulus_id,
912            stimulus_text: value.stimulus_text,
913            role: value.role,
914            scope: value.scope.into(),
915            kind: value.kind,
916            action: value.action,
917            topic: value.topic,
918            salience: value.salience,
919            salience_label: value.salience_label,
920            salience_confidence: value.salience_confidence,
921            confidence: value.confidence,
922            propositions: value.propositions,
923            gate: value.gate,
924            companions: value.companions,
925            recall: value.recall.map(Into::into),
926            find: value.find.map(Into::into),
927            aggregate: value.aggregate.map(Into::into),
928            persist: value.persist,
929            decider_id: value.decider_id,
930            checkpoint: value.checkpoint,
931            metadata: value.metadata,
932        }
933    }
934}