Skip to main content

cognee_lib/api/
prune.rs

1//! Selective backend cleanup -- `prune_data()` and `prune_system()`.
2//!
3//! Equivalent to Python's `cognee.api.v1.prune.prune`.
4
5use cognee_graph::GraphDBTrait;
6use cognee_session::SessionStore;
7use cognee_storage::StorageTrait;
8use cognee_vector::VectorDB;
9use tracing::info;
10
11use super::error::ApiError;
12
13/// Granular flags controlling which backends `prune_system()` wipes.
14#[derive(Debug, Clone)]
15pub struct PruneTarget {
16    /// Wipe all graph nodes and edges.
17    pub graph: bool,
18    /// Wipe all vector collections.
19    pub vector: bool,
20    /// Drop the relational metadata database.
21    ///
22    /// NOT IMPLEMENTED (audit B6.1 / task 09 Option A) — setting this has no
23    /// effect and `result.metadata_pruned` stays `false`. Python's
24    /// `prune_system(metadata=True)` physically drops the DB file; that
25    /// capability is deferred to Option A of task 09.
26    pub metadata: bool,
27    /// Wipe session cache.
28    pub cache: bool,
29}
30
31impl PruneTarget {
32    /// Everything except metadata (matches Python defaults).
33    pub fn default_system() -> Self {
34        Self {
35            graph: true,
36            vector: true,
37            metadata: false,
38            cache: true,
39        }
40    }
41
42    /// All backends Rust currently supports wiping (graph, vector, cache).
43    ///
44    /// `metadata` is intentionally `false`: dropping the relational DB is not
45    /// yet implemented (Python's `prune_system(metadata=True)` drops it; the
46    /// Rust public `prune` default is `metadata=False`, which this matches).
47    /// Tracked as audit B6.1 / task 09 Option A.
48    pub fn all() -> Self {
49        Self {
50            graph: true,
51            vector: true,
52            metadata: false,
53            cache: true,
54        }
55    }
56}
57
58impl Default for PruneTarget {
59    fn default() -> Self {
60        Self::default_system()
61    }
62}
63
64/// Summary of a prune operation.
65#[derive(Debug, Clone, Default)]
66pub struct PruneResult {
67    pub data_pruned: bool,
68    pub graph_pruned: bool,
69    pub vector_pruned: bool,
70    pub metadata_pruned: bool,
71    pub cache_pruned: bool,
72}
73
74/// Remove all files from data storage.
75///
76/// Equivalent to Python's `prune.prune_data()`.
77pub async fn prune_data(storage: &dyn StorageTrait) -> Result<(), ApiError> {
78    storage.remove_all().await?;
79    info!("prune_data: all storage files removed");
80    Ok(())
81}
82
83/// Selective backend cleanup.
84///
85/// Equivalent to Python's `prune.prune_system(graph, vector, metadata, cache)`.
86///
87/// # Arguments
88/// * `target` - Which backends to wipe.
89/// * `graph_db` - Graph database (required if `target.graph` is true).
90/// * `vector_db` - Vector database (required if `target.vector` is true).
91/// * `session_store` - Session store (required if `target.cache` is true).
92///
93/// # Notes
94/// - `target.metadata` is accepted but currently a no-op (logged as a
95///   warning). Dropping and recreating the relational database is deferred.
96pub async fn prune_system(
97    target: &PruneTarget,
98    graph_db: Option<&dyn GraphDBTrait>,
99    vector_db: Option<&dyn VectorDB>,
100    session_store: Option<&dyn SessionStore>,
101) -> Result<PruneResult, ApiError> {
102    let mut result = PruneResult::default();
103
104    if target.graph {
105        if let Some(gdb) = graph_db {
106            gdb.delete_graph().await?;
107            result.graph_pruned = true;
108            info!("prune_system: graph wiped");
109        } else {
110            tracing::warn!("prune_system: graph=true but no graph_db provided; skipping");
111        }
112    }
113
114    if target.vector {
115        if let Some(vdb) = vector_db {
116            vdb.prune().await?;
117            result.vector_pruned = true;
118            info!("prune_system: vector collections wiped");
119        } else {
120            tracing::warn!("prune_system: vector=true but no vector_db provided; skipping");
121        }
122    }
123
124    if target.metadata {
125        // NOT IMPLEMENTED: dropping the relational DB is deferred (audit B6.1 /
126        // task 09 Option A). We intentionally do NOT set result.metadata_pruned,
127        // so the returned PruneResult truthfully reports metadata_pruned=false
128        // even when a caller forced target.metadata=true by hand.
129        tracing::warn!(
130            "prune_system: metadata pruning is NOT implemented; the relational \
131             database was NOT dropped (result.metadata_pruned stays false)"
132        );
133    }
134
135    if target.cache {
136        if let Some(store) = session_store {
137            store.prune().await?;
138            result.cache_pruned = true;
139            info!("prune_system: session cache wiped");
140        } else {
141            tracing::warn!("prune_system: cache=true but no session_store provided; skipping");
142        }
143    }
144
145    Ok(result)
146}
147
148#[cfg(test)]
149#[allow(
150    clippy::unwrap_used,
151    clippy::expect_used,
152    reason = "test code — panics are acceptable failures"
153)]
154mod tests {
155    use super::*;
156
157    #[test]
158    fn prune_target_defaults_match_python() {
159        let target = PruneTarget::default();
160        assert!(target.graph);
161        assert!(target.vector);
162        assert!(!target.metadata, "metadata defaults to false like Python");
163        assert!(target.cache);
164    }
165
166    #[test]
167    fn prune_target_all_does_not_advertise_metadata() {
168        // Option B (audit B6.1): all() must not claim metadata will be wiped
169        // because the relational-drop is not yet implemented.
170        let target = PruneTarget::all();
171        assert!(target.graph);
172        assert!(target.vector);
173        assert!(
174            !target.metadata,
175            "all() must not advertise metadata=true until Option A is implemented"
176        );
177        assert!(target.cache);
178    }
179
180    #[tokio::test]
181    async fn prune_system_metadata_pruned_stays_false_when_not_implemented() {
182        // Even when a caller forces target.metadata=true, the returned
183        // PruneResult must report metadata_pruned=false (not yet implemented).
184        let target = PruneTarget {
185            graph: false,
186            vector: false,
187            metadata: true,
188            cache: false,
189        };
190        let result = prune_system(&target, None, None, None)
191            .await
192            .expect("prune_system must not error");
193        assert!(
194            !result.metadata_pruned,
195            "metadata_pruned must be false when metadata drop is not implemented"
196        );
197    }
198}