langgraphrb_rails 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.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +816 -0
  3. data/Rakefile +23 -0
  4. data/app/assets/javascripts/langgraphrb_rails.js +153 -0
  5. data/app/assets/stylesheets/langgraphrb_rails.css +95 -0
  6. data/lib/generators/langgraph_rb/compatibility.rb +71 -0
  7. data/lib/generators/langgraph_rb/controller/templates/controller.rb +54 -0
  8. data/lib/generators/langgraph_rb/controller/templates/view.html.erb +101 -0
  9. data/lib/generators/langgraph_rb/controller_generator.rb +39 -0
  10. data/lib/generators/langgraph_rb/graph/templates/graph.rb +68 -0
  11. data/lib/generators/langgraph_rb/graph_generator.rb +23 -0
  12. data/lib/generators/langgraph_rb/install/templates/README +45 -0
  13. data/lib/generators/langgraph_rb/install/templates/example_graph.rb +89 -0
  14. data/lib/generators/langgraph_rb/install/templates/initializer.rb +35 -0
  15. data/lib/generators/langgraph_rb/install/templates/langgraph_rb.yml +45 -0
  16. data/lib/generators/langgraph_rb/install_generator.rb +34 -0
  17. data/lib/generators/langgraph_rb/job/templates/job.rb +38 -0
  18. data/lib/generators/langgraph_rb/job_generator.rb +27 -0
  19. data/lib/generators/langgraph_rb/model/templates/migration.rb +12 -0
  20. data/lib/generators/langgraph_rb/model/templates/model.rb +15 -0
  21. data/lib/generators/langgraph_rb/model_generator.rb +34 -0
  22. data/lib/generators/langgraph_rb/task/templates/task.rake +58 -0
  23. data/lib/generators/langgraph_rb/task_generator.rb +23 -0
  24. data/lib/generators/langgraphrb_rails/compatibility.rb +71 -0
  25. data/lib/generators/langgraphrb_rails/controller/templates/controller.rb +30 -0
  26. data/lib/generators/langgraphrb_rails/controller/templates/view.html.erb +112 -0
  27. data/lib/generators/langgraphrb_rails/controller_generator.rb +29 -0
  28. data/lib/generators/langgraphrb_rails/graph/templates/graph.rb +14 -0
  29. data/lib/generators/langgraphrb_rails/graph/templates/node.rb +16 -0
  30. data/lib/generators/langgraphrb_rails/graph_generator.rb +48 -0
  31. data/lib/generators/langgraphrb_rails/install/templates/config.yml +30 -0
  32. data/lib/generators/langgraphrb_rails/install/templates/example_graph.rb +44 -0
  33. data/lib/generators/langgraphrb_rails/install/templates/initializer.rb +27 -0
  34. data/lib/generators/langgraphrb_rails/install_generator.rb +35 -0
  35. data/lib/generators/langgraphrb_rails/jobs/templates/run_job.rb +45 -0
  36. data/lib/generators/langgraphrb_rails/jobs_generator.rb +34 -0
  37. data/lib/generators/langgraphrb_rails/model/templates/migration.rb +20 -0
  38. data/lib/generators/langgraphrb_rails/model/templates/model.rb +14 -0
  39. data/lib/generators/langgraphrb_rails/model_generator.rb +30 -0
  40. data/lib/generators/langgraphrb_rails/persistence/templates/create_langgraph_runs.rb +18 -0
  41. data/lib/generators/langgraphrb_rails/persistence/templates/langgraph_run.rb +56 -0
  42. data/lib/generators/langgraphrb_rails/persistence_generator.rb +28 -0
  43. data/lib/generators/langgraphrb_rails/task/templates/task.rake +30 -0
  44. data/lib/generators/langgraphrb_rails/task_generator.rb +17 -0
  45. data/lib/generators/langgraphrb_rails/tracing/templates/traced.rb +45 -0
  46. data/lib/generators/langgraphrb_rails/tracing_generator.rb +63 -0
  47. data/lib/langgraphrb_rails/configuration.rb +47 -0
  48. data/lib/langgraphrb_rails/engine.rb +20 -0
  49. data/lib/langgraphrb_rails/helper.rb +141 -0
  50. data/lib/langgraphrb_rails/middleware/streaming.rb +77 -0
  51. data/lib/langgraphrb_rails/railtie.rb +55 -0
  52. data/lib/langgraphrb_rails/stores/active_record.rb +51 -0
  53. data/lib/langgraphrb_rails/stores/redis.rb +57 -0
  54. data/lib/langgraphrb_rails/test_helper.rb +126 -0
  55. data/lib/langgraphrb_rails/version.rb +28 -0
  56. data/lib/langgraphrb_rails.rb +111 -0
  57. data/lib/tasks/langgraphrb_rails_tasks.rake +62 -0
  58. metadata +217 -0
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:minitest) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ begin
11
+ require 'rspec/core/rake_task'
12
+ RSpec::Core::RakeTask.new(:spec) do |t|
13
+ t.pattern = 'spec/**/*_spec.rb'
14
+ end
15
+
16
+ # Run both test suites
17
+ task :test => [:minitest, :spec]
18
+ rescue LoadError
19
+ # RSpec not available, just use Minitest
20
+ task :test => :minitest
21
+ end
22
+
23
+ task default: :test
@@ -0,0 +1,153 @@
1
+ // LangGraphRB Rails JavaScript
2
+ // This file provides enhanced functionality for LangGraphRB Rails integration
3
+
4
+ document.addEventListener('DOMContentLoaded', function() {
5
+ // Initialize all LangGraphRB chat interfaces
6
+ initLangGraphChats();
7
+ });
8
+
9
+ // Initialize all chat interfaces
10
+ function initLangGraphChats() {
11
+ const chatForms = document.querySelectorAll('.langgraph-chat-form');
12
+
13
+ chatForms.forEach(function(form) {
14
+ initChatForm(form);
15
+ });
16
+ }
17
+
18
+ // Initialize a single chat form
19
+ function initChatForm(form) {
20
+ const container = form.closest('.langgraph-chat-container');
21
+ const messagesContainer = container.querySelector('.langgraph-messages');
22
+
23
+ form.addEventListener('submit', function(event) {
24
+ // Check if this is a streaming form with an EventSource
25
+ if (form.dataset.streaming !== 'true') {
26
+ // Handle regular AJAX form submission
27
+ handleRegularFormSubmit(event, form, messagesContainer);
28
+ }
29
+ });
30
+
31
+ // Auto-resize textarea
32
+ const textarea = form.querySelector('textarea');
33
+ if (textarea) {
34
+ textarea.addEventListener('input', function() {
35
+ this.style.height = 'auto';
36
+ this.style.height = (this.scrollHeight) + 'px';
37
+ });
38
+ }
39
+
40
+ // Scroll to bottom initially
41
+ if (messagesContainer) {
42
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
43
+ }
44
+ }
45
+
46
+ // Handle regular form submission via AJAX
47
+ function handleRegularFormSubmit(event, form, messagesContainer) {
48
+ event.preventDefault();
49
+
50
+ const messageInput = form.querySelector('textarea, input[name="message"]');
51
+ const userMessage = messageInput.value.trim();
52
+
53
+ if (!userMessage) return;
54
+
55
+ // Add user message to the UI
56
+ addMessageToUI(messagesContainer, userMessage, 'user');
57
+
58
+ // Clear the input
59
+ messageInput.value = '';
60
+ messageInput.style.height = 'auto';
61
+
62
+ // Prepare form data
63
+ const formData = new FormData(form);
64
+
65
+ // Show typing indicator if it exists
66
+ const typingIndicator = form.closest('.langgraph-chat-container').querySelector('.langgraph-typing-indicator');
67
+ if (typingIndicator) {
68
+ typingIndicator.style.display = 'block';
69
+ }
70
+
71
+ // Send the AJAX request
72
+ fetch(form.action, {
73
+ method: form.method || 'POST',
74
+ body: formData,
75
+ headers: {
76
+ 'Accept': 'application/json'
77
+ }
78
+ })
79
+ .then(response => response.json())
80
+ .then(data => {
81
+ // Hide typing indicator
82
+ if (typingIndicator) {
83
+ typingIndicator.style.display = 'none';
84
+ }
85
+
86
+ // Add assistant response to the UI
87
+ if (data.response) {
88
+ addMessageToUI(messagesContainer, data.response, 'assistant');
89
+ }
90
+ })
91
+ .catch(error => {
92
+ console.error('Error:', error);
93
+ // Hide typing indicator
94
+ if (typingIndicator) {
95
+ typingIndicator.style.display = 'none';
96
+ }
97
+
98
+ // Add error message
99
+ addMessageToUI(messagesContainer, 'Sorry, an error occurred. Please try again.', 'system');
100
+ });
101
+ }
102
+
103
+ // Add a message to the UI
104
+ function addMessageToUI(container, message, role) {
105
+ const messageDiv = document.createElement('div');
106
+ messageDiv.className = `langgraph-message ${role}`;
107
+
108
+ const messageP = document.createElement('p');
109
+ messageP.textContent = message;
110
+
111
+ messageDiv.appendChild(messageP);
112
+ container.appendChild(messageDiv);
113
+
114
+ // Scroll to bottom
115
+ container.scrollTop = container.scrollHeight;
116
+ }
117
+
118
+ // Setup streaming connection
119
+ function setupEventSource(url, messagesContainer, typingIndicator) {
120
+ const eventSource = new EventSource(url);
121
+
122
+ // Create a div for the assistant's response
123
+ const assistantDiv = document.createElement('div');
124
+ assistantDiv.className = 'langgraph-message assistant';
125
+ const assistantP = document.createElement('p');
126
+ assistantDiv.appendChild(assistantP);
127
+ messagesContainer.appendChild(assistantDiv);
128
+
129
+ // Handle incoming messages
130
+ eventSource.onmessage = function(event) {
131
+ const data = JSON.parse(event.data);
132
+
133
+ // Hide typing indicator when complete
134
+ if (data.completed) {
135
+ typingIndicator.style.display = 'none';
136
+ eventSource.close();
137
+ }
138
+
139
+ // Update the assistant's message if there's a response
140
+ if (data.state && data.state.response) {
141
+ assistantP.textContent = data.state.response;
142
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
143
+ }
144
+ };
145
+
146
+ // Handle errors
147
+ eventSource.onerror = function() {
148
+ typingIndicator.style.display = 'none';
149
+ eventSource.close();
150
+ };
151
+
152
+ return eventSource;
153
+ }
@@ -0,0 +1,95 @@
1
+ /* LangGraphRB Rails Chat Interface Styles */
2
+
3
+ .langgraph-chat-container {
4
+ max-width: 800px;
5
+ margin: 0 auto;
6
+ border: 1px solid #e0e0e0;
7
+ border-radius: 8px;
8
+ overflow: hidden;
9
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
10
+ background-color: #fff;
11
+ }
12
+
13
+ .langgraph-messages {
14
+ height: 400px;
15
+ overflow-y: auto;
16
+ padding: 16px;
17
+ background-color: #f9f9f9;
18
+ }
19
+
20
+ .langgraph-message {
21
+ margin-bottom: 16px;
22
+ padding: 12px;
23
+ border-radius: 8px;
24
+ max-width: 80%;
25
+ word-wrap: break-word;
26
+ }
27
+
28
+ .langgraph-message.user {
29
+ background-color: #e3f2fd;
30
+ margin-left: auto;
31
+ text-align: right;
32
+ }
33
+
34
+ .langgraph-message.assistant {
35
+ background-color: #f5f5f5;
36
+ }
37
+
38
+ .langgraph-message.system {
39
+ background-color: #fff3e0;
40
+ font-style: italic;
41
+ max-width: 100%;
42
+ margin-bottom: 24px;
43
+ border-left: 3px solid #ffb74d;
44
+ }
45
+
46
+ .langgraph-chat-form {
47
+ display: flex;
48
+ flex-direction: column;
49
+ padding: 16px;
50
+ border-top: 1px solid #e0e0e0;
51
+ }
52
+
53
+ .langgraph-chat-input {
54
+ width: 100%;
55
+ padding: 12px;
56
+ border: 1px solid #e0e0e0;
57
+ border-radius: 4px;
58
+ margin-bottom: 8px;
59
+ resize: none;
60
+ min-height: 80px;
61
+ }
62
+
63
+ .langgraph-chat-submit {
64
+ align-self: flex-end;
65
+ padding: 8px 16px;
66
+ background-color: #2196f3;
67
+ color: white;
68
+ border: none;
69
+ border-radius: 4px;
70
+ cursor: pointer;
71
+ font-weight: bold;
72
+ }
73
+
74
+ .langgraph-chat-submit:hover {
75
+ background-color: #1976d2;
76
+ }
77
+
78
+ .langgraph-typing-indicator {
79
+ display: none;
80
+ padding: 8px;
81
+ text-align: center;
82
+ font-style: italic;
83
+ color: #757575;
84
+ }
85
+
86
+ .langgraph-typing-indicator::after {
87
+ content: "...";
88
+ animation: typing-dots 1.5s infinite;
89
+ }
90
+
91
+ @keyframes typing-dots {
92
+ 0%, 20% { content: "."; }
93
+ 40% { content: ".."; }
94
+ 60%, 100% { content: "..."; }
95
+ }
@@ -0,0 +1,71 @@
1
+ module LanggraphRb
2
+ module Generators
3
+ module Compatibility
4
+ def self.included(base)
5
+ if defined?(Rails) && Gem::Version.new(Rails.version) >= Gem::Version.new('8.0.0')
6
+ # Explicitly require all necessary Rails generator components
7
+ begin
8
+ require 'rails/generators'
9
+ require 'rails/generators/actions'
10
+ require 'rails/generators/migration'
11
+ require 'rails/generators/active_model'
12
+ require 'rails/generators/base'
13
+ require 'rails/generators/named_base'
14
+
15
+ # Define Actions module if it doesn't exist
16
+ unless defined?(Rails::Generators::Actions)
17
+ module Rails
18
+ module Generators
19
+ module Actions
20
+ # Minimal implementation of required methods
21
+ def copy_file(source, destination, config = {})
22
+ say_status :copy, "#{source} to #{destination}", config.fetch(:verbose, true)
23
+ end
24
+
25
+ def template(source, destination, config = {})
26
+ say_status :template, "#{source} to #{destination}", config.fetch(:verbose, true)
27
+ end
28
+
29
+ def empty_directory(destination, config = {})
30
+ say_status :create, destination, config.fetch(:verbose, true)
31
+ end
32
+
33
+ def inject_into_file(destination, content, config = {})
34
+ say_status :inject, destination, config.fetch(:verbose, true)
35
+ end
36
+
37
+ def append_to_file(destination, content, config = {})
38
+ say_status :append, destination, config.fetch(:verbose, true)
39
+ end
40
+
41
+ def gsub_file(destination, flag, replacement, config = {})
42
+ say_status :gsub, destination, config.fetch(:verbose, true)
43
+ end
44
+
45
+ def application(data, config = {})
46
+ say_status :application, data, config.fetch(:verbose, true)
47
+ end
48
+
49
+ def readme(path)
50
+ say_status :readme, path, true
51
+ end
52
+
53
+ def say_status(status, message, log_status = true)
54
+ puts "[#{status}] #{message}" if log_status
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ # Include Actions module if it's now defined
62
+ base.include(Rails::Generators::Actions) if defined?(Rails::Generators::Actions)
63
+ rescue LoadError => e
64
+ # Fallback gracefully if we can't load all components
65
+ puts "Warning: Could not load all Rails generator components: #{e.message}"
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,54 @@
1
+ class <%= class_name %>Controller < ApplicationController
2
+ # GET /<%= file_name %>
3
+ def index
4
+ # You can render a view with a form to interact with the graph
5
+ end
6
+
7
+ # POST /<%= file_name %>
8
+ def create
9
+ # Process the input with the graph
10
+ result = <%= graph_class_name %>.invoke(input: params[:message])
11
+
12
+ respond_to do |format|
13
+ format.html { redirect_to <%= singular_table_name %>_path(id: result[:thread_id]), notice: "Processing complete" }
14
+ format.json { render json: { response: result[:response], thread_id: result[:thread_id] } }
15
+ end
16
+ end
17
+
18
+ # GET /<%= file_name %>/stream
19
+ def stream
20
+ response.headers['Content-Type'] = 'text/event-stream'
21
+ response.headers['Last-Modified'] = Time.now.httpdate
22
+
23
+ <%= graph_class_name %>.stream(input: params[:message]) do |step_result|
24
+ response.stream.write("data: #{step_result.to_json}\n\n")
25
+
26
+ if step_result[:completed]
27
+ response.stream.close
28
+ end
29
+ end
30
+ rescue ActionController::Live::ClientDisconnected
31
+ response.stream.close
32
+ end
33
+
34
+ # GET /<%= file_name %>/:id
35
+ def show
36
+ # Retrieve the thread state if needed
37
+ thread_id = params[:id]
38
+ store = LanggraphrbRails.create_store
39
+
40
+ if store.exists?(thread_id)
41
+ @state = store.get(thread_id)
42
+
43
+ respond_to do |format|
44
+ format.html
45
+ format.json { render json: @state }
46
+ end
47
+ else
48
+ respond_to do |format|
49
+ format.html { redirect_to <%= plural_table_name %>_path, alert: "Thread not found" }
50
+ format.json { render json: { error: "Thread not found" }, status: :not_found }
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,101 @@
1
+ <%% content_for :title, "<%= class_name %>" %>
2
+
3
+ <div class="container mt-4">
4
+ <div class="row">
5
+ <div class="col-md-8 offset-md-2">
6
+ <div class="card">
7
+ <div class="card-header">
8
+ <h2><%= class_name %></h2>
9
+ </div>
10
+ <div class="card-body">
11
+ <div id="messages-container" class="mb-4">
12
+ <div class="system-message">
13
+ <p>Welcome to <%= class_name %>. How can I help you today?</p>
14
+ </div>
15
+
16
+ <div id="message-history">
17
+ <%% if @state && @state[:messages] %>
18
+ <%% @state[:messages].each do |message| %>
19
+ <div class="message <%%= message[:role] %>">
20
+ <p><%%= message[:content] %></p>
21
+ </div>
22
+ <%% end %>
23
+ <%% end %>
24
+ </div>
25
+ </div>
26
+
27
+ <%%= form_with url: <%= plural_table_name %>_path, method: :post, id: "message-form", data: { remote: true } do |f| %>
28
+ <div class="form-group">
29
+ <%%= f.text_area :message, class: "form-control", placeholder: "Type your message here...", rows: 3 %>
30
+ </div>
31
+ <div class="form-group">
32
+ <%%= f.submit "Send", class: "btn btn-primary" %>
33
+ </div>
34
+ <%% end %>
35
+ </div>
36
+ </div>
37
+ </div>
38
+ </div>
39
+ </div>
40
+
41
+ <style>
42
+ #messages-container {
43
+ max-height: 400px;
44
+ overflow-y: auto;
45
+ border: 1px solid #e0e0e0;
46
+ border-radius: 5px;
47
+ padding: 10px;
48
+ }
49
+
50
+ .message {
51
+ margin-bottom: 10px;
52
+ padding: 10px;
53
+ border-radius: 5px;
54
+ }
55
+
56
+ .user {
57
+ background-color: #f0f0f0;
58
+ text-align: right;
59
+ }
60
+
61
+ .assistant {
62
+ background-color: #e3f2fd;
63
+ }
64
+
65
+ .system-message {
66
+ background-color: #f5f5f5;
67
+ padding: 10px;
68
+ border-radius: 5px;
69
+ margin-bottom: 10px;
70
+ font-style: italic;
71
+ }
72
+ </style>
73
+
74
+ <script>
75
+ document.addEventListener('DOMContentLoaded', function() {
76
+ const form = document.getElementById('message-form');
77
+ const messagesContainer = document.getElementById('message-history');
78
+
79
+ form.addEventListener('ajax:success', function(event) {
80
+ const [data, status, xhr] = event.detail;
81
+
82
+ // Add user message to the UI
83
+ const userMessage = document.createElement('div');
84
+ userMessage.className = 'message user';
85
+ userMessage.innerHTML = '<p>' + form.elements['message'].value + '</p>';
86
+ messagesContainer.appendChild(userMessage);
87
+
88
+ // Add assistant response to the UI
89
+ const assistantMessage = document.createElement('div');
90
+ assistantMessage.className = 'message assistant';
91
+ assistantMessage.innerHTML = '<p>' + data.response + '</p>';
92
+ messagesContainer.appendChild(assistantMessage);
93
+
94
+ // Clear the form
95
+ form.elements['message'].value = '';
96
+
97
+ // Scroll to bottom
98
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
99
+ });
100
+ });
101
+ </script>
@@ -0,0 +1,39 @@
1
+ require 'rails/generators/base'
2
+ require_relative 'compatibility'
3
+
4
+ module LanggraphRb
5
+ module Generators
6
+ class ControllerGenerator < Rails::Generators::NamedBase
7
+ include LanggraphRb::Generators::Compatibility
8
+ source_root File.expand_path('controller/templates', __dir__)
9
+
10
+ desc "Creates a new controller for LangGraphRB interactions"
11
+
12
+ argument :actions, type: :array, default: [], banner: "action action"
13
+
14
+ def create_controller_file
15
+ template "controller.rb", File.join("app/controllers", "#{file_name}_controller.rb")
16
+ end
17
+
18
+ def create_views
19
+ actions.each do |action|
20
+ template "view.html.erb", File.join("app/views", file_name, "#{action}.html.erb")
21
+ end
22
+ end
23
+
24
+ def add_routes
25
+ route "resources :#{file_name}, only: [:index, :create, :show] do\n" +
26
+ " collection do\n" +
27
+ " get :stream\n" +
28
+ " end\n" +
29
+ " end"
30
+ end
31
+
32
+ private
33
+
34
+ def graph_class_name
35
+ "#{class_name.singularize}Graph"
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,68 @@
1
+ # <%= graph_class_name %> - LangGraphRB graph
2
+ # Generated with langgraphrb_rails
3
+
4
+ class <%= graph_class_name %>
5
+ # Define a singleton instance for easy access
6
+ class << self
7
+ def instance
8
+ @instance ||= build_graph
9
+ end
10
+
11
+ def invoke(input = {}, context: nil)
12
+ instance.invoke(input, context: context)
13
+ end
14
+
15
+ def stream(input = {}, context: nil, &block)
16
+ instance.stream(input, context: context, &block)
17
+ end
18
+
19
+ private
20
+
21
+ def build_graph
22
+ # Define your state schema and reducers here
23
+ initial_state = LangGraphRB::State.new(
24
+ { messages: [] },
25
+ { messages: LangGraphRB::State.add_messages }
26
+ )
27
+
28
+ # Create the graph
29
+ graph = LangGraphRB::Graph.new(state_class: LangGraphRB::State) do
30
+ # Define your nodes here
31
+ node :receive_input do |state|
32
+ Rails.logger.info "Processing input in <%= graph_class_name %>"
33
+ {
34
+ messages: [{ role: 'user', content: state[:input] }],
35
+ input_processed: true
36
+ }
37
+ end
38
+
39
+ node :process do |state|
40
+ # Add your processing logic here
41
+ {
42
+ processed: true,
43
+ result: "Processed: #{state[:input]}"
44
+ }
45
+ end
46
+
47
+ node :respond do |state|
48
+ # Generate a response
49
+ response = "This is a response from <%= graph_class_name %>"
50
+
51
+ {
52
+ messages: [{ role: 'assistant', content: response }],
53
+ response: response
54
+ }
55
+ end
56
+
57
+ # Define your edges here
58
+ set_entry_point :receive_input
59
+ edge :receive_input, :process
60
+ edge :process, :respond
61
+ set_finish_point :respond
62
+ end
63
+
64
+ # Compile the graph
65
+ graph.compile!
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,23 @@
1
+ require 'rails/generators/base'
2
+ require_relative 'compatibility'
3
+
4
+ module LanggraphRb
5
+ module Generators
6
+ class GraphGenerator < Rails::Generators::NamedBase
7
+ include LanggraphRb::Generators::Compatibility
8
+ source_root File.expand_path('graph/templates', __dir__)
9
+
10
+ desc "Creates a new LangGraphRB graph in app/graphs"
11
+
12
+ def create_graph_file
13
+ template "graph.rb", File.join("app/graphs", "#{file_name}_graph.rb")
14
+ end
15
+
16
+ private
17
+
18
+ def graph_class_name
19
+ "#{class_name}Graph"
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,45 @@
1
+ ===============================================================================
2
+
3
+ LangGraphRB Rails Integration
4
+ ============================
5
+
6
+ Your LangGraphRB Rails integration has been successfully installed!
7
+
8
+ Next steps:
9
+
10
+ 1. Review the configuration in config/langgraph_rb.yml and adjust as needed
11
+
12
+ 2. Check out the example graph in app/graphs/example_graph.rb
13
+
14
+ 3. Create your own graphs in the app/graphs directory
15
+
16
+ 4. Use your graphs in your controllers like this:
17
+
18
+ ```ruby
19
+ # In a controller action
20
+ def chat
21
+ user_input = params[:message]
22
+ result = ExampleGraph.invoke(input: user_input)
23
+ render json: { response: result[:response] }
24
+ end
25
+ ```
26
+
27
+ 5. For streaming responses, use:
28
+
29
+ ```ruby
30
+ # In a controller action with Turbo Streams or ActionCable
31
+ def stream_chat
32
+ ExampleGraph.stream(input: params[:message]) do |step_result|
33
+ # Send step_result to the client via ActionCable or Turbo Streams
34
+ if step_result[:completed]
35
+ # Final result
36
+ else
37
+ # Intermediate result
38
+ end
39
+ end
40
+ end
41
+ ```
42
+
43
+ For more information, visit: https://github.com/cdaviis/langgraphrb_rails
44
+
45
+ ===============================================================================