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