Skip to main content

locus_sdk/interface/
dto.rs

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