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}