Skip to main content

stasis/application/runtime/
memory_persistence_helpers.rs

1use std::{
2    collections::hash_map::DefaultHasher,
3    hash::{Hash, Hasher},
4};
5
6use chrono::Utc;
7
8use crate::application::orchestration::runtime_job_payloads::{
9    MemoryPolicyPayload, MemoryStoreModePayload,
10};
11use crate::ports::outbound::memory::memory_models::{MemoryRecallRequest, MemoryStoreResponse};
12
13pub enum SttpPromptNodeFormat {
14    TaggedSchema,
15    UntaggedNoSchema,
16}
17
18pub fn should_store(memory_policy: Option<&MemoryPolicyPayload>) -> bool {
19    !matches!(
20        memory_policy.and_then(|policy| policy.store_mode.clone()),
21        Some(MemoryStoreModePayload::Disabled)
22    )
23}
24
25pub fn memory_scope_hash(correlation_id: &str, memory_policy: Option<&MemoryPolicyPayload>) -> String {
26    let basis = format!(
27        "corr={correlation_id}|sessions={:?}|tiers={:?}|from={:?}|to={:?}",
28        memory_policy.and_then(|policy| policy.session_ids.clone()),
29        memory_policy.and_then(|policy| policy.tiers.clone()),
30        memory_policy.and_then(|policy| policy.from_utc),
31        memory_policy.and_then(|policy| policy.to_utc),
32    );
33    let mut hasher = DefaultHasher::new();
34    basis.hash(&mut hasher);
35    format!("{:x}", hasher.finish())
36}
37
38pub fn memory_query_id(correlation_id: &str, request: &MemoryRecallRequest) -> String {
39    let basis = format!(
40        "corr={correlation_id}|query={:?}|sessions={:?}|tiers={:?}|from={:?}|to={:?}|limit={}|alpha={}|beta={}|fallback={:?}|strictness={:?}|include_explain={}",
41        request.query_text,
42        request.scope.session_ids,
43        request.scope.tiers,
44        request.scope.from_utc,
45        request.scope.to_utc,
46        request.limit,
47        request.alpha,
48        request.beta,
49        request.fallback_policy,
50        request.strictness,
51        request.include_explain,
52    );
53    let mut hasher = DefaultHasher::new();
54    basis.hash(&mut hasher);
55    format!("mq:{:x}", hasher.finish())
56}
57
58pub fn memory_query_fingerprint(request: &MemoryRecallRequest) -> String {
59    format!(
60        "sessions={:?}|tiers={:?}|from={:?}|to={:?}|limit={}|alpha={}|beta={}|fallback={:?}|strictness={:?}|include_explain={}",
61        request.scope.session_ids,
62        request.scope.tiers,
63        request.scope.from_utc,
64        request.scope.to_utc,
65        request.limit,
66        request.alpha,
67        request.beta,
68        request.fallback_policy,
69        request.strictness,
70        request.include_explain,
71    )
72}
73
74pub fn resolve_sttp_output_node_id(memory_store: Option<&MemoryStoreResponse>, fallback: String) -> String {
75    memory_store
76        .map(|stored| stored.node_id.clone())
77        .filter(|node_id| !node_id.trim().is_empty())
78        .unwrap_or(fallback)
79}
80
81pub fn render_prompt_response_sttp_node(
82    session_id: &str,
83    user_prompt: &str,
84    output_text: &str,
85    format: SttpPromptNodeFormat,
86) -> String {
87    let escaped_summary = output_text.replace('"', "\\\"");
88    let escaped_prompt = user_prompt.replace('"', "\\\"");
89    let timestamp = Utc::now().to_rfc3339();
90
91    match format {
92        SttpPromptNodeFormat::TaggedSchema => format!(
93            "⊕⟨ ⏣0{{ trigger: manual, response_format: temporal_node, origin_session: \"{session_id}\", compression_depth: 1, parent_node: null, prime: {{ attractor_config: {{ stability: 0.80, friction: 0.20, logic: 0.85, autonomy: 0.75 }}, context_summary: \"{escaped_summary}\", relevant_tier: raw, retrieval_budget: 10 }} }} ⟩\n\
94⦿⟨ ⏣0{{ timestamp: \"{timestamp}\", tier: raw, session_id: \"{session_id}\", schema_version: \"sttp-1.0\", user_avec: {{ stability: 0.80, friction: 0.20, logic: 0.85, autonomy: 0.75, psi: 2.60 }}, model_avec: {{ stability: 0.82, friction: 0.18, logic: 0.84, autonomy: 0.74, psi: 2.58 }} }} ⟩\n\
95◈⟨ ⏣0{{ prompt(.88): \"{escaped_prompt}\", response(.95): \"{escaped_summary}\" }} ⟩\n\
96⍉⟨ ⏣0{{ rho: 0.96, kappa: 0.94, psi: 2.60, compression_avec: {{ stability: 0.81, friction: 0.19, logic: 0.84, autonomy: 0.74, psi: 2.58 }} }} ⟩"
97        ),
98        SttpPromptNodeFormat::UntaggedNoSchema => format!(
99            "⊕⟨ {{ trigger: manual, response_format: temporal_node, origin_session: \"{session_id}\", compression_depth: 1, parent_node: null, prime: {{ attractor_config: {{ stability: 0.80, friction: 0.20, logic: 0.85, autonomy: 0.75 }}, context_summary: \"{escaped_summary}\", relevant_tier: raw, retrieval_budget: 10 }} }} ⟩\n\
100⦿⟨ {{ timestamp: \"{timestamp}\", tier: raw, session_id: \"{session_id}\", user_avec: {{ stability: 0.80, friction: 0.20, logic: 0.85, autonomy: 0.75, psi: 2.60 }}, model_avec: {{ stability: 0.82, friction: 0.18, logic: 0.84, autonomy: 0.74, psi: 2.58 }} }} ⟩\n\
101◈⟨ {{ prompt(.88): \"{escaped_prompt}\", response(.95): \"{escaped_summary}\" }} ⟩\n\
102⍉⟨ {{ rho: 0.96, kappa: 0.94, psi: 2.60, compression_avec: {{ stability: 0.81, friction: 0.19, logic: 0.84, autonomy: 0.74, psi: 2.58 }} }} ⟩"
103        ),
104    }
105}
106
107pub fn render_session_summary_sttp_node(session_id: &str, summary_text: &str) -> String {
108    let escaped_summary = summary_text.replace('"', "\\\"");
109    let timestamp = Utc::now().to_rfc3339();
110
111    format!(
112        "⊕⟨ ⏣0{{ trigger: manual, response_format: temporal_node, origin_session: \"{session_id}\", compression_depth: 1, parent_node: null, prime: {{ attractor_config: {{ stability: 0.80, friction: 0.20, logic: 0.85, autonomy: 0.75 }}, context_summary: \"{escaped_summary}\", relevant_tier: raw, retrieval_budget: 10 }} }} ⟩\n\
113⦿⟨ ⏣0{{ timestamp: \"{timestamp}\", tier: raw, session_id: \"{session_id}\", schema_version: \"sttp-1.0\", user_avec: {{ stability: 0.80, friction: 0.20, logic: 0.85, autonomy: 0.75, psi: 2.60 }}, model_avec: {{ stability: 0.82, friction: 0.18, logic: 0.84, autonomy: 0.74, psi: 2.58 }} }} ⟩\n\
114◈⟨ ⏣0{{ session_summary(.95): \"{escaped_summary}\" }} ⟩\n\
115⍉⟨ ⏣0{{ rho: 0.96, kappa: 0.94, psi: 2.60, compression_avec: {{ stability: 0.81, friction: 0.19, logic: 0.84, autonomy: 0.74, psi: 2.58 }} }} ⟩"
116    )
117}