Skip to main content

graphrag_core/graphrag/
lifecycle.rs

1#![allow(unused_imports)]
2
3use crate::config::Config;
4use crate::core::{
5    ChunkId, Document, DocumentId, Entity, EntityId, GraphRAGError, KnowledgeGraph, Relationship,
6    Result, TextChunk,
7};
8use crate::{critic, ollama, persistence, query, retrieval};
9
10#[cfg(feature = "parallel-processing")]
11#[allow(unused_imports)]
12use crate::parallel;
13
14use super::GraphRAG;
15
16impl GraphRAG {
17    /// Create a new GraphRAG instance with the given configuration
18    pub fn new(config: Config) -> Result<Self> {
19        Ok(Self {
20            config,
21            knowledge_graph: None,
22            retrieval_system: None,
23            query_planner: None,
24            critic: None,
25            #[cfg(feature = "parallel-processing")]
26            parallel_processor: None,
27        })
28    }
29
30    /// Create a Zero-Config local GraphRAG instance
31    /// Uses: Candle (MiniLM) for embeddings, Memory/LanceDB for storage, Ollama for LLM
32    pub fn default_local() -> Result<Self> {
33        let mut config = Config::default();
34        // Configure for local use
35        config.ollama.enabled = true;
36        // config.storage.type = StorageType::LanceDB; // Future
37
38        Self::new(config)
39    }
40
41    /// Create a builder for configuring GraphRAG
42    ///
43    /// # Example
44    /// ```no_run
45    /// use graphrag_core::GraphRAG;
46    ///
47    /// # fn example() -> graphrag_core::Result<()> {
48    /// let graphrag = GraphRAG::builder()
49    ///     .with_output_dir("./workspace")
50    ///     .with_chunk_size(512)
51    ///     .build()?;
52    /// # Ok(())
53    /// # }
54    /// ```
55    pub fn builder() -> crate::builder::GraphRAGBuilder {
56        crate::builder::GraphRAGBuilder::new()
57    }
58
59    /// Initialize the GraphRAG system.
60    ///
61    /// When `auto_save.enabled = true` and a `base_dir` is configured, attempts to
62    /// load an existing graph from the workspace on disk before starting fresh.
63    /// This means a second run reuses the previously built graph automatically.
64    pub fn initialize(&mut self) -> Result<()> {
65        // Try to restore from workspace if persistent storage is configured
66        let loaded = self.try_load_from_workspace();
67
68        if !loaded {
69            self.knowledge_graph = Some(KnowledgeGraph::new());
70        }
71
72        self.retrieval_system = Some(retrieval::RetrievalSystem::new(&self.config)?);
73
74        if self.config.ollama.enabled {
75            let client = ollama::OllamaClient::new(self.config.ollama.clone());
76            self.query_planner = Some(query::planner::QueryPlanner::new(client));
77        }
78
79        Ok(())
80    }
81
82    /// Attempt to load the knowledge graph from a workspace on disk.
83    /// Returns `true` if the graph was loaded successfully, `false` otherwise.
84    fn try_load_from_workspace(&mut self) -> bool {
85        if !self.config.auto_save.enabled {
86            return false;
87        }
88        let base_dir = match &self.config.auto_save.base_dir {
89            Some(d) => d.clone(),
90            None => return false,
91        };
92        let workspace_name = self
93            .config
94            .auto_save
95            .workspace_name
96            .as_deref()
97            .unwrap_or("default");
98
99        let manager = match persistence::WorkspaceManager::new(&base_dir) {
100            Ok(m) => m,
101            Err(_e) => {
102                #[cfg(feature = "tracing")]
103                tracing::warn!("Could not open workspace base dir '{}': {}", base_dir, _e);
104                return false;
105            },
106        };
107
108        if !manager.workspace_exists(workspace_name) {
109            return false;
110        }
111
112        match manager.load_graph(workspace_name) {
113            Ok(graph) => {
114                #[cfg(feature = "tracing")]
115                tracing::info!(
116                    "Loaded graph from workspace '{}' ({} entities, {} relationships)",
117                    workspace_name,
118                    graph.entity_count(),
119                    graph.relationship_count(),
120                );
121                self.knowledge_graph = Some(graph);
122                true
123            },
124            Err(_e) => {
125                #[cfg(feature = "tracing")]
126                tracing::warn!(
127                    "Failed to load graph from workspace '{}': {}",
128                    workspace_name,
129                    _e
130                );
131                false
132            },
133        }
134    }
135
136    /// Save the current knowledge graph to the configured workspace on disk.
137    /// No-op when `auto_save.enabled = false` or `base_dir` is not set.
138    pub fn save_to_workspace(&self) -> Result<()> {
139        if !self.config.auto_save.enabled {
140            return Ok(());
141        }
142        let base_dir = match &self.config.auto_save.base_dir {
143            Some(d) => d,
144            None => return Ok(()),
145        };
146        let workspace_name = self
147            .config
148            .auto_save
149            .workspace_name
150            .as_deref()
151            .unwrap_or("default");
152
153        let graph = self
154            .knowledge_graph
155            .as_ref()
156            .ok_or_else(|| GraphRAGError::Config {
157                message: "Knowledge graph not initialized".to_string(),
158            })?;
159
160        let manager = persistence::WorkspaceManager::new(base_dir)?;
161        manager.save_graph(graph, workspace_name)?;
162
163        #[cfg(feature = "tracing")]
164        tracing::info!(
165            "Saved graph to workspace '{}' in '{}' ({} entities, {} relationships)",
166            workspace_name,
167            base_dir,
168            graph.entity_count(),
169            graph.relationship_count(),
170        );
171        Ok(())
172    }
173
174    /// Clear all entities and relationships from the knowledge graph
175    ///
176    /// This method preserves documents and text chunks but removes all extracted entities and relationships.
177    /// Useful for rebuilding the graph from scratch without reloading documents.
178    pub fn clear_graph(&mut self) -> Result<()> {
179        let graph = self
180            .knowledge_graph
181            .as_mut()
182            .ok_or_else(|| GraphRAGError::Config {
183                message: "Knowledge graph not initialized".to_string(),
184            })?;
185
186        #[cfg(feature = "tracing")]
187        tracing::info!("Clearing knowledge graph (preserving documents and chunks)");
188
189        graph.clear_entities_and_relationships();
190        Ok(())
191    }
192}