rrag_graph/lib.rs
1//! # RGraph - Graph-based Agent Orchestration System
2//!
3//! RGraph is a powerful graph-based workflow orchestration system designed for building
4//! sophisticated AI agent applications. Inspired by LangGraph, it provides a declarative
5//! way to define complex agent workflows with state management, conditional execution,
6//! and seamless integration with the RRAG framework.
7//!
8//! ## Key Features
9//!
10//! - **Graph-Based Workflows**: Define agent behavior as directed graphs
11//! - **State Management**: Persistent state across node executions
12//! - **Conditional Routing**: Dynamic workflow paths based on execution results
13//! - **Agent Orchestration**: Coordinate multiple AI agents and tools
14//! - **RRAG Integration**: Built-in support for RAG-powered agents
15//! - **Async Execution**: High-performance concurrent execution
16//! - **Observability**: Comprehensive monitoring and debugging
17//! - **Persistence**: Durable workflow state and history
18//!
19//! ## Quick Start
20//!
21//! ```rust
22//! use rgraph::prelude::*;
23//!
24//! # async fn example() -> RGraphResult<()> {
25//! // Define a simple agent workflow
26//! let mut graph = WorkflowGraph::new("research_assistant");
27//!
28//! // Add nodes
29//! graph.add_node("understand_query", QueryAnalysisNode::new()).await?;
30//! graph.add_node("search_knowledge", RagSearchNode::new()).await?;
31//! graph.add_node("synthesize_response", ResponseGenerationNode::new()).await?;
32//!
33//! // Define edges
34//! graph.add_edge("understand_query", "search_knowledge")?;
35//! graph.add_edge("search_knowledge", "synthesize_response")?;
36//!
37//! // Execute the workflow
38//! let initial_state = GraphState::new()
39//! .with_input("user_query", "What is machine learning?");
40//!
41//! let result = graph.execute(initial_state).await?;
42//! println!("Response: {}", result.get_output("final_response")?);
43//! # Ok(())
44//! # }
45//! ```
46//!
47//! ## Advanced Examples
48//!
49//! ### Multi-Agent Collaboration
50//! ```rust
51//! use rgraph::prelude::*;
52//!
53//! # async fn example() -> RGraphResult<()> {
54//! let mut graph = WorkflowGraph::new("multi_agent_system");
55//!
56//! // Research agent
57//! graph.add_node("researcher",
58//! AgentNode::new("research_agent")
59//! .with_system_prompt("You are a research specialist...")
60//! .with_tools(vec![web_search_tool(), database_query_tool()])
61//! ).await?;
62//!
63//! // Analysis agent
64//! graph.add_node("analyst",
65//! AgentNode::new("analysis_agent")
66//! .with_system_prompt("You analyze research data...")
67//! .with_tools(vec![data_analysis_tool(), visualization_tool()])
68//! ).await?;
69//!
70//! // Writer agent
71//! graph.add_node("writer",
72//! AgentNode::new("writing_agent")
73//! .with_system_prompt("You write comprehensive reports...")
74//! ).await?;
75//!
76//! // Conditional routing based on research results
77//! graph.add_conditional_edge("researcher", |state: &GraphState| {
78//! if state.get("research_quality_score")? > 0.8 {
79//! Ok("analyst".to_string())
80//! } else {
81//! Ok("researcher".to_string()) // Loop back for more research
82//! }
83//! })?;
84//!
85//! graph.add_edge("analyst", "writer")?;
86//!
87//! let result = graph.execute(
88//! GraphState::new().with_input("research_topic", "Climate Change Impact")
89//! ).await?;
90//! # Ok(())
91//! # }
92//! ```
93//!
94//! ### RAG-Powered Knowledge Agent
95//! ```rust
96//! use rgraph::prelude::*;
97//! # #[cfg(feature = "rrag-integration")]
98//! # async fn example() -> RGraphResult<()> {
99//! let mut graph = WorkflowGraph::new("knowledge_agent");
100//!
101//! // RAG retrieval node
102//! graph.add_node("retrieve_context",
103//! RagRetrievalNode::new()
104//! .with_rrag_system(rrag_system)
105//! .with_top_k(5)
106//! .with_score_threshold(0.7)
107//! ).await?;
108//!
109//! // Context evaluation node
110//! graph.add_node("evaluate_context",
111//! ContextEvaluationNode::new()
112//! .with_relevance_threshold(0.6)
113//! ).await?;
114//!
115//! // Response generation with context
116//! graph.add_node("generate_response",
117//! ContextualGenerationNode::new()
118//! .with_context_window(4096)
119//! ).await?;
120//!
121//! // Conditional routing based on context quality
122//! graph.add_conditional_edge("evaluate_context", |state: &GraphState| {
123//! let context_score: f32 = state.get("context_relevance_score")?;
124//! if context_score > 0.6 {
125//! Ok("generate_response".to_string())
126//! } else {
127//! Ok("retrieve_context".to_string()) // Retry with different strategy
128//! }
129//! })?;
130//!
131//! let result = graph.execute(
132//! GraphState::new().with_input("query", "Explain quantum computing")
133//! ).await?;
134//! # Ok(())
135//! # }
136//! ```
137//!
138//! ## Architecture
139//!
140//! RGraph is built around several core concepts:
141//!
142//! ### Workflow Graph
143//! A directed graph representing the agent workflow, where nodes are execution units
144//! and edges define the flow of control and data.
145//!
146//! ### Graph State
147//! A shared state object that flows through the graph, accumulating results and
148//! providing context for decision-making.
149//!
150//! ### Nodes
151//! Execution units that perform specific tasks:
152//! - **Agent Nodes**: LLM-powered agents with tools
153//! - **Tool Nodes**: Direct tool execution
154//! - **RAG Nodes**: Retrieval-augmented generation
155//! - **Condition Nodes**: Decision points in the workflow
156//! - **Transform Nodes**: Data transformation and processing
157//!
158//! ### Execution Engine
159//! The runtime system that executes graphs with support for:
160//! - Parallel execution where possible
161//! - State management and persistence
162//! - Error handling and recovery
163//! - Observability and debugging
164//!
165//! ## Integration with RRAG
166//!
167//! RGraph seamlessly integrates with the RRAG framework to provide:
168//! - RAG-powered agent nodes
169//! - Knowledge retrieval capabilities
170//! - Document processing workflows
171//! - Embedding-based routing decisions
172//! - Multi-modal processing support
173
174pub mod agents;
175pub mod core;
176pub mod execution;
177pub mod nodes;
178pub mod observability;
179pub mod prelude;
180pub mod routing;
181pub mod state;
182pub mod tools;
183
184#[cfg(feature = "rrag-integration")]
185pub mod rrag_integration;
186
187// Re-export core types for easy access
188pub use crate::core::{
189 Edge, EdgeId, ExecutionContext, ExecutionResult, GraphBuilder, Node, NodeId, WorkflowGraph,
190};
191pub use crate::execution::{
192 ExecutionConfig, ExecutionEngine, ExecutionError, ExecutionMetrics, ExecutionMode,
193 ExecutionResults,
194};
195pub use crate::nodes::{AgentNode, ConditionNode, ToolNode, TransformNode};
196pub use crate::state::{GraphState, StatePath, StateValue};
197
198#[cfg(feature = "rrag-integration")]
199pub use crate::rrag_integration::{
200 ContextEvaluationConfig, ContextEvaluationNode, RagGenerationConfig, RagGenerationNode,
201 RagRetrievalConfig, RagRetrievalNode, RagWorkflowBuilder,
202};
203
204// Error handling
205use thiserror::Error;
206
207/// Result type for RGraph operations
208pub type RGraphResult<T> = Result<T, RGraphError>;
209
210/// Error types for RGraph operations
211#[derive(Debug, Error)]
212pub enum RGraphError {
213 #[error("Graph execution error: {message}")]
214 Execution { message: String },
215
216 #[error("Node error in '{node_id}': {message}")]
217 Node { node_id: String, message: String },
218
219 #[error("State error: {message}")]
220 State { message: String },
221
222 #[error("Graph validation error: {message}")]
223 Validation { message: String },
224
225 #[error("Configuration error: {message}")]
226 Config { message: String },
227
228 #[error("Agent error: {message}")]
229 Agent { message: String },
230
231 #[error("Tool error: {message}")]
232 Tool { message: String },
233
234 #[error("Routing error: {message}")]
235 Routing { message: String },
236
237 #[cfg(feature = "rrag-integration")]
238 #[error("RRAG integration error: {0}")]
239 Rrag(#[from] rrag::RragError),
240
241 #[error("Serialization error: {0}")]
242 Serialization(#[from] serde_json::Error),
243
244 #[error("IO error: {0}")]
245 Io(#[from] std::io::Error),
246
247 #[error("Other error: {0}")]
248 Other(#[from] anyhow::Error),
249}
250
251impl RGraphError {
252 /// Create an execution error
253 pub fn execution(message: impl Into<String>) -> Self {
254 Self::Execution {
255 message: message.into(),
256 }
257 }
258
259 /// Create a node error
260 pub fn node(node_id: impl Into<String>, message: impl Into<String>) -> Self {
261 Self::Node {
262 node_id: node_id.into(),
263 message: message.into(),
264 }
265 }
266
267 /// Create a state error
268 pub fn state(message: impl Into<String>) -> Self {
269 Self::State {
270 message: message.into(),
271 }
272 }
273
274 /// Create a validation error
275 pub fn validation(message: impl Into<String>) -> Self {
276 Self::Validation {
277 message: message.into(),
278 }
279 }
280
281 /// Create a config error
282 pub fn config(message: impl Into<String>) -> Self {
283 Self::Config {
284 message: message.into(),
285 }
286 }
287
288 /// Create a configuration error (alias for config)
289 pub fn configuration(message: impl Into<String>) -> Self {
290 Self::Config {
291 message: message.into(),
292 }
293 }
294
295 /// Create an agent error
296 pub fn agent(message: impl Into<String>) -> Self {
297 Self::Agent {
298 message: message.into(),
299 }
300 }
301
302 /// Create a tool error
303 pub fn tool(message: impl Into<String>) -> Self {
304 Self::Tool {
305 message: message.into(),
306 }
307 }
308
309 /// Create a routing error
310 pub fn routing(message: impl Into<String>) -> Self {
311 Self::Routing {
312 message: message.into(),
313 }
314 }
315}
316
317/// Framework constants
318pub const VERSION: &str = env!("CARGO_PKG_VERSION");
319pub const NAME: &str = "RGraph";
320pub const DESCRIPTION: &str = "Graph-based Agent Orchestration System";
321
322#[cfg(test)]
323mod tests {
324 use super::*;
325
326 #[test]
327 fn test_error_creation() {
328 let exec_err = RGraphError::execution("test execution error");
329 assert!(matches!(exec_err, RGraphError::Execution { .. }));
330
331 let node_err = RGraphError::node("test_node", "test node error");
332 assert!(matches!(node_err, RGraphError::Node { .. }));
333 }
334
335 #[test]
336 fn test_constants() {
337 assert!(!VERSION.is_empty());
338 assert_eq!(NAME, "RGraph");
339 assert!(!DESCRIPTION.is_empty());
340 }
341}