deepstrike_core/mm/
mod.rs1use crate::context::manager::{KNOWLEDGE_TOOL_NAME, MEMORY_TOOL_NAME};
9use crate::context::pressure::PressureAction;
10use crate::types::message::ToolCall;
11use serde::{Deserialize, Serialize};
12
13pub mod handle;
14pub mod memory;
15
16pub use handle::{
17 plan_eviction, plan_spool, EvictionOp, EvictionPlan, Handle, HandleId, HandleKind, HandleTable,
18 Residency, SpoolDecision,
19};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
23#[serde(rename_all = "snake_case")]
24pub enum MemoryTierHint {
25 Durable,
27 Semantic,
29}
30
31impl MemoryTierHint {
32 pub fn label(self) -> &'static str {
33 match self {
34 Self::Durable => "durable",
35 Self::Semantic => "semantic",
36 }
37 }
38}
39
40pub fn tier_hint_for_compress(action: PressureAction) -> MemoryTierHint {
42 match action {
43 PressureAction::ContextCollapse | PressureAction::AutoCompact => MemoryTierHint::Semantic,
44 _ => MemoryTierHint::Durable,
45 }
46}
47
48pub fn is_page_in_tool(name: &str) -> bool {
50 name == MEMORY_TOOL_NAME || name == KNOWLEDGE_TOOL_NAME
51}
52
53#[derive(Debug, Clone, Default)]
55pub struct PageInRequest {
56 pub call_id: String,
57 pub tool: String,
58 pub query: String,
59 pub top_k: u32,
60}
61
62pub fn page_in_requests_from_calls(calls: &[ToolCall]) -> Vec<PageInRequest> {
64 let mut out = Vec::new();
65 for call in calls {
66 if !is_page_in_tool(call.name.as_str()) {
67 continue;
68 }
69 let args = &call.arguments;
70 let query = args
71 .get("query")
72 .and_then(|v| v.as_str())
73 .unwrap_or("")
74 .to_string();
75 let top_k = args
76 .get("top_k")
77 .and_then(|v| v.as_u64())
78 .unwrap_or(5) as u32;
79 out.push(PageInRequest {
80 call_id: call.id.to_string(),
81 tool: call.name.to_string(),
82 query,
83 top_k: top_k.max(1),
84 });
85 }
86 out
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct PageInEntry {
92 pub content: String,
93 #[serde(default, skip_serializing_if = "Option::is_none")]
94 pub tokens: Option<u32>,
95 #[serde(default, skip_serializing_if = "Option::is_none")]
96 pub source: Option<String>,
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102 use compact_str::CompactString;
103
104 #[test]
105 fn tier_hint_maps_auto_compact_to_semantic() {
106 assert_eq!(
107 tier_hint_for_compress(PressureAction::AutoCompact),
108 MemoryTierHint::Semantic
109 );
110 assert_eq!(
111 tier_hint_for_compress(PressureAction::SnipCompact),
112 MemoryTierHint::Durable
113 );
114 }
115
116 #[test]
117 fn page_in_requests_from_memory_call() {
118 use crate::types::message::ToolCall;
119 let calls = vec![ToolCall {
120 id: CompactString::new("c1"),
121 name: CompactString::new("memory"),
122 arguments: serde_json::json!({"query": "past bugs", "top_k": 3}),
123 }];
124 let reqs = page_in_requests_from_calls(&calls);
125 assert_eq!(reqs.len(), 1);
126 assert_eq!(reqs[0].query, "past bugs");
127 assert_eq!(reqs[0].top_k, 3);
128 }
129}