langgraph_rb 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d21ea984522f684793c24a7be4f2ccd5c90dfad98512647949e42b9e2b8c1dee
4
+ data.tar.gz: 99b3ebfdc7a42fb86cee874d3471f3dc21a728eba018497c695acc7f9af50611
5
+ SHA512:
6
+ metadata.gz: 94c90d4d802c354776a8440662bb9f50af883b78baacd9891bb6f43c76052540a0a7767facc238931c53c4752a0802787dd82ba277b370e19049a4939b7d5c0e
7
+ data.tar.gz: c12b92e058b0eccf5150d4491e603746abadb946386d7b0e51379d5ec481f8e1f01512023d8f0e492adf203355c5450a2b32aded362840efe4bb1be9ee11088c
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :development, :test do
6
+ gem 'rspec', '~> 3.0'
7
+ gem 'pry', '~> 0.14'
8
+ gem 'rubocop', '~> 1.0'
9
+ end
data/README.md ADDED
@@ -0,0 +1,350 @@
1
+ # LangGraphRB 🔄
2
+
3
+ A Ruby library for building stateful, multi-actor applications with directed graphs, inspired by [LangGraph](https://langchain-ai.github.io/langgraph/).
4
+
5
+ ## Overview
6
+
7
+ LangGraphRB models complex workflows as directed graphs where:
8
+ - **Nodes** are executable functions that process state and return updates
9
+ - **Edges** define the flow between nodes with support for conditional routing
10
+ - **State** is a centralized, typed object that flows through the entire execution
11
+ - **Commands** control execution flow with operations like `goto`, `send`, and `interrupt`
12
+
13
+ ## Key Features
14
+
15
+ - 🔄 **Graph-based Orchestration**: Model complex workflows as directed graphs
16
+ - 🏃 **Parallel Execution**: Execute multiple nodes simultaneously with thread-safe state management
17
+ - 💾 **Checkpointing**: Automatic state persistence and resumption support
18
+ - 🤖 **Human-in-the-Loop**: Built-in support for human intervention and approval workflows
19
+ - 🔀 **Map-Reduce Operations**: Fan-out to parallel processing and collect results
20
+ - 📊 **Visualization**: Generate Mermaid diagrams of your graphs
21
+ - 🎯 **Type-Safe State**: Centralized state with customizable reducers
22
+ - ⚡ **Streaming Execution**: Real-time progress monitoring
23
+
24
+ ## Installation
25
+
26
+ Add this line to your application's Gemfile:
27
+
28
+ ```ruby
29
+ gem 'langgraph_rb'
30
+ ```
31
+
32
+ And then execute:
33
+
34
+ ```bash
35
+ $ bundle install
36
+ ```
37
+
38
+ Or install it yourself as:
39
+
40
+ ```bash
41
+ $ gem install langgraph_rb
42
+ ```
43
+
44
+ ## Quick Start
45
+
46
+ Here's a simple example of building a chatbot with conditional routing:
47
+
48
+ ```ruby
49
+ require 'langgraph_rb'
50
+
51
+ # Create a graph with a DSL
52
+ graph = LangGraphRB::Graph.new do
53
+ # Define nodes
54
+ node :greet do |state|
55
+ { message: "Hello! How can I help you?" }
56
+ end
57
+
58
+ node :analyze_intent do |state|
59
+ user_input = state[:user_input].to_s.downcase
60
+ intent = user_input.include?('weather') ? 'weather' : 'general'
61
+ { intent: intent }
62
+ end
63
+
64
+ node :weather_response do |state|
65
+ { message: "The weather is sunny today!" }
66
+ end
67
+
68
+ node :general_response do |state|
69
+ { message: "That's interesting! Tell me more." }
70
+ end
71
+
72
+ # Define the flow
73
+ set_entry_point :greet
74
+ edge :greet, :analyze_intent
75
+
76
+ # Conditional routing based on intent
77
+ conditional_edge :analyze_intent, ->(state) { state[:intent] }, {
78
+ 'weather' => :weather_response,
79
+ 'general' => :general_response
80
+ }
81
+
82
+ # Both responses end the conversation
83
+ set_finish_point :weather_response
84
+ set_finish_point :general_response
85
+ end
86
+
87
+ # Compile and execute
88
+ graph.compile!
89
+ result = graph.invoke({ user_input: "How's the weather?" })
90
+ puts result[:message] # => "The weather is sunny today!"
91
+ ```
92
+
93
+ ## Core Concepts
94
+
95
+ ### State Management
96
+
97
+ State is centralized and flows through the entire graph. You can define reducers for specific keys to control how state updates are merged:
98
+
99
+ ```ruby
100
+ # State with custom reducers
101
+ state = LangGraphRB::State.new(
102
+ { messages: [], count: 0 },
103
+ {
104
+ messages: LangGraphRB::State.add_messages, # Append to array
105
+ count: ->(old, new) { (old || 0) + new } # Sum values
106
+ }
107
+ )
108
+ ```
109
+
110
+ ### Nodes
111
+
112
+ Nodes are the executable units of your graph. They receive the current state and return updates:
113
+
114
+ ```ruby
115
+ # Simple node
116
+ node :process_data do |state|
117
+ processed = state[:data].map(&:upcase)
118
+ { processed_data: processed, processing_complete: true }
119
+ end
120
+
121
+ # Node with context
122
+ node :call_api do |state, context|
123
+ api_client = context[:api_client]
124
+ result = api_client.call(state[:query])
125
+ { api_result: result }
126
+ end
127
+ ```
128
+
129
+ ### Edges and Routing
130
+
131
+ Define how execution flows between nodes:
132
+
133
+ ```ruby
134
+ # Simple edge
135
+ edge :node_a, :node_b
136
+
137
+ # Conditional edge with router function
138
+ conditional_edge :decision_node, ->(state) {
139
+ state[:condition] ? :path_a : :path_b
140
+ }
141
+
142
+ # Fan-out to multiple nodes
143
+ fan_out_edge :distributor, [:worker_1, :worker_2, :worker_3]
144
+ ```
145
+
146
+ ### Commands
147
+
148
+ Control execution flow with commands:
149
+
150
+ ```ruby
151
+ node :decision_node do |state|
152
+ if state[:should_continue]
153
+ LangGraphRB::Commands.update_and_goto(
154
+ { status: 'continuing' },
155
+ :next_node
156
+ )
157
+ else
158
+ LangGraphRB::Commands.end_execution({ status: 'stopped' })
159
+ end
160
+ end
161
+ ```
162
+
163
+ ### Parallel Processing with Send
164
+
165
+ Use `Send` commands for map-reduce operations:
166
+
167
+ ```ruby
168
+ node :fan_out do |state|
169
+ tasks = state[:tasks]
170
+
171
+ # Send each task to parallel processing
172
+ sends = tasks.map do |task|
173
+ LangGraphRB::Send.new(to: :process_task, payload: { task: task })
174
+ end
175
+
176
+ LangGraphRB::MultiSend.new(sends)
177
+ end
178
+ ```
179
+
180
+ ## Advanced Features
181
+
182
+ ### Checkpointing and Resumption
183
+
184
+ Persist execution state and resume from any point:
185
+
186
+ ```ruby
187
+ # Use a persistent store
188
+ store = LangGraphRB::Stores::FileStore.new('./checkpoints')
189
+ thread_id = 'my_workflow_123'
190
+
191
+ # Execute with checkpointing
192
+ result = graph.invoke(
193
+ { input: 'data' },
194
+ store: store,
195
+ thread_id: thread_id
196
+ )
197
+
198
+ # Resume later
199
+ resumed_result = graph.resume(
200
+ thread_id,
201
+ { additional_input: 'more_data' },
202
+ store: store
203
+ )
204
+ ```
205
+
206
+ ### Human-in-the-Loop
207
+
208
+ Pause execution for human input:
209
+
210
+ ```ruby
211
+ node :request_approval do |state|
212
+ LangGraphRB::Commands.interrupt(
213
+ message: "Please review and approve this action",
214
+ data: { action: state[:proposed_action] }
215
+ )
216
+ end
217
+
218
+ # Set up interrupt handler
219
+ runner = LangGraphRB::Runner.new(graph, store: store, thread_id: thread_id)
220
+ runner.on_interrupt do |interrupt|
221
+ puts interrupt.message
222
+ # Get user input (this would be from a UI in practice)
223
+ user_response = gets.chomp
224
+ { approval: user_response == 'approve' }
225
+ end
226
+ ```
227
+
228
+ ### Streaming Execution
229
+
230
+ Monitor execution progress in real-time:
231
+
232
+ ```ruby
233
+ graph.stream({ input: 'data' }) do |step_result|
234
+ puts "Step #{step_result[:step]}: #{step_result[:active_nodes]}"
235
+ puts "State keys: #{step_result[:state].keys}"
236
+ puts "Completed: #{step_result[:completed]}"
237
+ end
238
+ ```
239
+
240
+ ### Visualization
241
+
242
+ Generate Mermaid diagrams of your graphs:
243
+
244
+ ```ruby
245
+ graph.compile!
246
+ puts graph.to_mermaid
247
+
248
+ # Output:
249
+ # graph TD
250
+ # start((START))
251
+ # node_a["node_a"]
252
+ # node_b["node_b"]
253
+ # __end__((END))
254
+ # start --> node_a
255
+ # node_a --> node_b
256
+ # node_b --> __end__
257
+ ```
258
+
259
+ ## Storage Options
260
+
261
+ Choose from different storage backends:
262
+
263
+ ```ruby
264
+ # In-memory (default, not persistent)
265
+ store = LangGraphRB::Stores::InMemoryStore.new
266
+
267
+ # File-based with YAML
268
+ store = LangGraphRB::Stores::FileStore.new('./data/checkpoints')
269
+
270
+ # File-based with JSON
271
+ store = LangGraphRB::Stores::JsonStore.new('./data/checkpoints')
272
+ ```
273
+
274
+ ## Specialized Nodes
275
+
276
+ ### LLM Nodes
277
+
278
+ For LLM integration (bring your own client):
279
+
280
+ ```ruby
281
+ llm_node :chat, llm_client: my_llm_client, system_prompt: "You are a helpful assistant"
282
+
283
+ # Or with custom logic
284
+ llm_node :custom_chat, llm_client: my_llm_client do |state, context|
285
+ messages = prepare_messages(state[:conversation])
286
+ response = context[:llm_client].call(messages)
287
+ { messages: [{ role: 'assistant', content: response }] }
288
+ end
289
+ ```
290
+
291
+ ### Tool Nodes
292
+
293
+ For tool/function calls:
294
+
295
+ ```ruby
296
+ tool_node :search, tool: SearchTool.new
297
+
298
+ # The tool should respond to #call
299
+ class SearchTool
300
+ def call(args)
301
+ # Perform search and return results
302
+ search_api.query(args[:query])
303
+ end
304
+ end
305
+ ```
306
+
307
+ ## Examples
308
+
309
+ Check out the `examples/` directory for complete working examples:
310
+
311
+ - `basic_example.rb` - Simple chatbot with conditional routing
312
+ - `advanced_example.rb` - Research assistant with parallel processing and human-in-the-loop
313
+
314
+ Run them with:
315
+
316
+ ```bash
317
+ $ ruby examples/basic_example.rb
318
+ $ ruby examples/advanced_example.rb
319
+ ```
320
+
321
+ ## Comparison with LangGraph (Python)
322
+
323
+ | Feature | LangGraphRB | LangGraph |
324
+ |---------|-------------|-----------|
325
+ | Graph Definition | ✅ DSL + Builder | ✅ Builder Pattern |
326
+ | Parallel Execution | ✅ Thread-based | ✅ AsyncIO |
327
+ | Checkpointing | ✅ Multiple stores | ✅ Multiple stores |
328
+ | Human-in-the-loop | ✅ Interrupt system | ✅ Interrupt system |
329
+ | Map-Reduce | ✅ Send commands | ✅ Send API |
330
+ | Streaming | ✅ Block-based | ✅ AsyncIO streams |
331
+ | Visualization | ✅ Mermaid | ✅ Mermaid |
332
+ | State Management | ✅ Reducers | ✅ Reducers |
333
+
334
+ ## Contributing
335
+
336
+ Bug reports and pull requests are welcome on GitHub at https://github.com/yourusername/langgraph_rb.
337
+
338
+ ## License
339
+
340
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
341
+
342
+ ## Roadmap
343
+
344
+ - [ ] Redis-based checkpointing store
345
+ - [ ] Built-in LLM client integrations
346
+ - [ ] Web UI for monitoring executions
347
+ - [ ] Performance optimizations
348
+ - [ ] More comprehensive test suite
349
+ - [ ] Integration with Sidekiq for background processing
350
+ - [ ] Metrics and observability features
data/SUMMARY.md ADDED
@@ -0,0 +1,170 @@
1
+ # LangGraphRB - Summary of Implementation
2
+
3
+ ## Overview
4
+
5
+ Successfully created a Ruby library inspired by LangGraph (Python) that provides a framework for building stateful, multi-actor applications using directed graphs. The library models complex workflows as graphs where nodes are executable functions and edges define flow control.
6
+
7
+ ## Core Components Implemented
8
+
9
+ ### 1. State Management (`lib/langgraph_rb/state.rb`)
10
+ - **State Class**: Extends Hash with reducer support
11
+ - **Reducers**: Functions that define how state updates are merged
12
+ - **Built-in Reducers**:
13
+ - `add_messages` - Appends to arrays
14
+ - `append_string` - Concatenates strings
15
+ - `merge_hash` - Deep merges hashes
16
+
17
+ ### 2. Node System (`lib/langgraph_rb/node.rb`)
18
+ - **Base Node**: Callable functions that process state
19
+ - **LLMNode**: Specialized node for LLM integrations
20
+ - **ToolNode**: Specialized node for tool/function calls
21
+ - **Flexible Arity**: Supports 0, 1, or 2 parameter node functions
22
+
23
+ ### 3. Edge Routing (`lib/langgraph_rb/edge.rb`)
24
+ - **Simple Edges**: Direct node-to-node connections
25
+ - **Conditional Edges**: Router-function based routing with path mapping
26
+ - **Fan-out Edges**: Parallel execution to multiple destinations
27
+ - **Router Helper**: Builder pattern for complex conditional logic
28
+
29
+ ### 4. Execution Control (`lib/langgraph_rb/command.rb`)
30
+ - **Command**: Combines state update + routing decision
31
+ - **Send**: Creates parallel execution branches (map-reduce)
32
+ - **MultiSend**: Multiple parallel branches
33
+ - **Interrupt**: Human-in-the-loop pause points
34
+ - **Helper Methods**: Convenient command creation
35
+
36
+ ### 5. Graph Definition (`lib/langgraph_rb/graph.rb`)
37
+ - **DSL**: Clean syntax for defining workflows
38
+ - **Validation**: Compile-time graph validation
39
+ - **Mermaid Generation**: Automatic diagram creation
40
+ - **Entry/Exit Points**: START and FINISH node management
41
+
42
+ ### 6. Execution Engine (`lib/langgraph_rb/runner.rb`)
43
+ - **Parallel Execution**: Thread-based super-step processing
44
+ - **State Checkpointing**: Automatic state persistence
45
+ - **Streaming Support**: Real-time progress monitoring
46
+ - **Error Handling**: Safe node execution with error propagation
47
+ - **Resume Capability**: Restart from any checkpoint
48
+
49
+ ### 7. Persistence Layer (`lib/langgraph_rb/stores/memory.rb`)
50
+ - **In-Memory Store**: For testing and development
51
+ - **File Store**: YAML-based persistence
52
+ - **JSON Store**: JSON-based persistence
53
+ - **Extensible**: Abstract base for custom stores
54
+
55
+ ## Key Features Achieved
56
+
57
+ ✅ **Graph-based Orchestration**: Model workflows as directed graphs
58
+ ✅ **Parallel Execution**: Thread-safe super-step processing
59
+ ✅ **State Management**: Centralized state with reducers
60
+ ✅ **Conditional Routing**: Dynamic flow control
61
+ ✅ **Checkpointing**: Persistent execution state
62
+ ✅ **Human-in-the-Loop**: Interrupt-driven workflows
63
+ ✅ **Map-Reduce**: Fan-out parallel processing
64
+ ✅ **Streaming**: Real-time execution monitoring
65
+ ✅ **Visualization**: Mermaid diagram generation
66
+ ✅ **Error Handling**: Robust failure management
67
+
68
+ ## Architecture Highlights
69
+
70
+ ### Thread-Safe Execution
71
+ - Uses Ruby's Thread class for parallel node execution
72
+ - Mutex-protected result collection
73
+ - Super-step synchronization
74
+
75
+ ### State Flow
76
+ - Immutable state transitions
77
+ - Reducer-based merge operations
78
+ - Type-safe state management
79
+
80
+ ### Command System
81
+ - Explicit execution control
82
+ - Support for complex routing patterns
83
+ - Clean separation of concerns
84
+
85
+ ## Testing & Validation
86
+
87
+ Created comprehensive test suite covering:
88
+ - ✅ State management and reducers
89
+ - ✅ Basic graph execution
90
+ - ✅ Conditional routing
91
+ - ✅ Command-based flow control
92
+ - ✅ Checkpointing and resumption
93
+ - ✅ Streaming execution
94
+ - ✅ Error handling
95
+
96
+ ## Examples Provided
97
+
98
+ ### Basic Examples
99
+ - `examples/simple_test.rb` - Linear workflows and conditional routing
100
+ - `test_runner.rb` - Comprehensive feature testing
101
+
102
+ ### Advanced Examples
103
+ - `examples/basic_example.rb` - Chatbot with intent routing
104
+ - `examples/advanced_example.rb` - Research assistant with parallel processing
105
+
106
+ ## Comparison with LangGraph (Python)
107
+
108
+ | Feature | LangGraphRB | LangGraph (Python) |
109
+ |---------|-------------|-------------------|
110
+ | Graph Definition | ✅ Ruby DSL | ✅ Python Builder |
111
+ | Parallel Execution | ✅ Threads | ✅ AsyncIO |
112
+ | State Management | ✅ Reducers | ✅ Reducers |
113
+ | Checkpointing | ✅ Multiple stores | ✅ Multiple stores |
114
+ | Human-in-the-loop | ✅ Interrupts | ✅ Interrupts |
115
+ | Map-Reduce | ✅ Send commands | ✅ Send API |
116
+ | Streaming | ✅ Blocks | ✅ AsyncIO |
117
+ | Visualization | ✅ Mermaid | ✅ Mermaid |
118
+
119
+ ## Usage Patterns
120
+
121
+ ### Simple Linear Workflow
122
+ ```ruby
123
+ graph = LangGraphRB::Graph.new do
124
+ node :process { |state| { result: process(state[:input]) } }
125
+ node :validate { |state| { valid: validate(state[:result]) } }
126
+
127
+ set_entry_point :process
128
+ edge :process, :validate
129
+ set_finish_point :validate
130
+ end
131
+ ```
132
+
133
+ ### Conditional Routing
134
+ ```ruby
135
+ conditional_edge :router, ->(state) {
136
+ state[:priority] == 'high' ? :urgent_handler : :normal_handler
137
+ }
138
+ ```
139
+
140
+ ### Parallel Processing
141
+ ```ruby
142
+ node :fan_out do |state|
143
+ sends = state[:items].map do |item|
144
+ LangGraphRB::Send.new(to: :process_item, payload: { item: item })
145
+ end
146
+ LangGraphRB::MultiSend.new(sends)
147
+ end
148
+ ```
149
+
150
+ ## Ready for Production Use
151
+
152
+ The library provides:
153
+ - **Robust error handling** with try/catch in node execution
154
+ - **Persistent checkpointing** for durable workflows
155
+ - **Thread-safe operations** for concurrent execution
156
+ - **Memory management** with configurable stores
157
+ - **Comprehensive validation** at compile time
158
+ - **Clean abstractions** for extensibility
159
+
160
+ ## Next Steps for Enhancement
161
+
162
+ Potential improvements:
163
+ - Redis-based store implementation
164
+ - Built-in LLM client integrations
165
+ - Web UI for execution monitoring
166
+ - Metrics and observability
167
+ - Performance optimizations
168
+ - Background job integration (Sidekiq)
169
+
170
+ The LangGraphRB library successfully replicates the core concepts and capabilities of LangGraph in Ruby, providing a powerful framework for building complex, stateful workflows with parallel execution, persistence, and human-in-the-loop capabilities.