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
@@ -0,0 +1,89 @@
1
+ # Example LangGraphRB graph for Rails applications
2
+ # This is a simple example of how to define a graph in your Rails app
3
+ # You can create more graphs in the app/graphs directory
4
+
5
+ class ExampleGraph
6
+ # Define a singleton instance for easy access
7
+ class << self
8
+ def instance
9
+ @instance ||= build_graph
10
+ end
11
+
12
+ def invoke(input = {}, context: nil)
13
+ instance.invoke(input, context: context)
14
+ end
15
+
16
+ def stream(input = {}, context: nil, &block)
17
+ instance.stream(input, context: context, &block)
18
+ end
19
+
20
+ private
21
+
22
+ def build_graph
23
+ # Create a state with message history
24
+ initial_state = LangGraphRB::State.new(
25
+ { messages: [] },
26
+ { messages: LangGraphRB::State.add_messages }
27
+ )
28
+
29
+ # Create the graph
30
+ graph = LangGraphRB::Graph.new(state_class: LangGraphRB::State) do
31
+ # Define nodes
32
+ node :receive_input do |state|
33
+ Rails.logger.info "User input received: #{state[:input]}"
34
+ {
35
+ messages: [{ role: 'user', content: state[:input] }],
36
+ last_user_message: state[:input]
37
+ }
38
+ end
39
+
40
+ node :process_message do |state|
41
+ user_message = state[:last_user_message].to_s.downcase
42
+
43
+ intent = case user_message
44
+ when /hello|hi|hey/
45
+ 'greeting'
46
+ when /bye|goodbye|exit/
47
+ 'farewell'
48
+ else
49
+ 'general_chat'
50
+ end
51
+
52
+ Rails.logger.info "Detected intent: #{intent}"
53
+
54
+ {
55
+ intent: intent,
56
+ messages: [{ role: 'system', content: "Intent detected: #{intent}" }]
57
+ }
58
+ end
59
+
60
+ node :generate_response do |state|
61
+ response = case state[:intent]
62
+ when 'greeting'
63
+ "Hello! How can I help you today?"
64
+ when 'farewell'
65
+ "Goodbye! Have a great day!"
66
+ else
67
+ "That's interesting! Tell me more."
68
+ end
69
+
70
+ Rails.logger.info "Bot response: #{response}"
71
+
72
+ {
73
+ messages: [{ role: 'assistant', content: response }],
74
+ response: response
75
+ }
76
+ end
77
+
78
+ # Define edges
79
+ set_entry_point :receive_input
80
+ edge :receive_input, :process_message
81
+ edge :process_message, :generate_response
82
+ set_finish_point :generate_response
83
+ end
84
+
85
+ # Compile the graph
86
+ graph.compile!
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,35 @@
1
+ # LangGraphRB Rails Integration
2
+ # This initializer configures LangGraphRB for use with Rails
3
+
4
+ # Load configuration from config/langgraph_rb.yml
5
+ config_file = Rails.root.join('config', 'langgraph_rb.yml')
6
+ if File.exist?(config_file)
7
+ config = YAML.load(ERB.new(File.read(config_file)).result)[Rails.env]
8
+ LanggraphrbRails.configure do |c|
9
+ config.each do |key, value|
10
+ c.send("#{key}=", value) if c.respond_to?("#{key}=")
11
+ end
12
+ end
13
+ end
14
+
15
+ # Auto-load all graph definitions in app/graphs
16
+ Rails.application.config.to_prepare do
17
+ Dir.glob(Rails.root.join('app', 'graphs', '**', '*.rb')).each do |file|
18
+ require_dependency file
19
+ end
20
+ end
21
+
22
+ # Configure default store
23
+ # By default, LangGraphRB uses an in-memory store
24
+ # You can configure it to use Redis or another store here
25
+ #
26
+ # Example:
27
+ # LanggraphrbRails.configure_store do |store_config|
28
+ # store_config.adapter = :redis
29
+ # store_config.options = { url: ENV['REDIS_URL'] }
30
+ # end
31
+
32
+ # Configure default observers
33
+ # LanggraphrbRails.configure_observers do |observers|
34
+ # observers << LangGraphRB::Observers::Logger.new
35
+ # end
@@ -0,0 +1,45 @@
1
+ # LangGraphRB Configuration
2
+ # This file contains configuration settings for LangGraphRB in your Rails application
3
+
4
+ default: &default
5
+ # Store configuration
6
+ store:
7
+ adapter: memory # Options: memory, redis, active_record
8
+ # Redis configuration (if using redis adapter)
9
+ # redis_url: <%= ENV.fetch('REDIS_URL', 'redis://localhost:6379/0') %>
10
+ # redis_namespace: langgraph_rb
11
+
12
+ # ActiveRecord configuration (if using active_record adapter)
13
+ # model: GraphState # The model class to use for persistence
14
+ # ttl: 86400 # Optional TTL in seconds for cleanup
15
+
16
+ # Observer configuration
17
+ observers:
18
+ # Enable logging observer
19
+ logger: true
20
+ # Enable structured observer
21
+ structured: false
22
+
23
+ development:
24
+ <<: *default
25
+
26
+ test:
27
+ <<: *default
28
+
29
+ production:
30
+ <<: *default
31
+ # In production, you might want to use Redis or ActiveRecord for persistence
32
+ store:
33
+ adapter: redis
34
+ redis_url: <%= ENV.fetch('REDIS_URL', 'redis://localhost:6379/0') %>
35
+ redis_namespace: langgraph_rb
36
+
37
+ # Alternatively, use ActiveRecord for persistence
38
+ # adapter: active_record
39
+ # model: GraphState
40
+ # ttl: 604800 # 7 days
41
+
42
+ # Enable structured observer in production for better monitoring
43
+ observers:
44
+ logger: true
45
+ structured: true
@@ -0,0 +1,34 @@
1
+ require 'rails/generators/base'
2
+ require_relative 'compatibility'
3
+
4
+ module LanggraphRb
5
+ module Generators
6
+ class InstallGenerator < Rails::Generators::Base
7
+ include LanggraphRb::Generators::Compatibility
8
+ source_root File.expand_path('install/templates', __dir__)
9
+
10
+ desc "Creates LangGraphRB initializer and configuration files for your application"
11
+
12
+ def copy_initializer
13
+ template "initializer.rb", "config/initializers/langgraph_rb.rb"
14
+ end
15
+
16
+ def copy_config
17
+ template "langgraph_rb.yml", "config/langgraph_rb.yml"
18
+ end
19
+
20
+ def create_graphs_directory
21
+ empty_directory "app/graphs"
22
+ template "example_graph.rb", "app/graphs/example_graph.rb"
23
+ end
24
+
25
+ def add_graphs_to_autoload_paths
26
+ application "config.autoload_paths += %W(#{Rails.root}/app/graphs)"
27
+ end
28
+
29
+ def show_readme
30
+ readme "README"
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,38 @@
1
+ class <%= job_class_name %> < ApplicationJob
2
+ queue_as :default
3
+
4
+ def perform(input, options = {})
5
+ # Create a store for persistence
6
+ store = LanggraphrbRails.create_store
7
+
8
+ # Generate a thread ID or use the provided one
9
+ thread_id = options[:thread_id] || SecureRandom.hex(8)
10
+
11
+ # Process with the graph
12
+ result = <%= graph_class_name %>.invoke(
13
+ { input: input },
14
+ context: options[:context],
15
+ store: store,
16
+ thread_id: thread_id
17
+ )
18
+
19
+ # Handle the result
20
+ if options[:callback]
21
+ # If a callback is provided, call it with the result
22
+ callback_method = options[:callback].to_sym
23
+ if respond_to?(callback_method)
24
+ send(callback_method, result)
25
+ end
26
+ end
27
+
28
+ # Return the result
29
+ result
30
+ end
31
+
32
+ # Example callback method
33
+ def on_complete(result)
34
+ # Handle the completed result
35
+ # For example, send a notification, update a record, etc.
36
+ Rails.logger.info "Job completed with result: #{result[:response]}"
37
+ end
38
+ end
@@ -0,0 +1,27 @@
1
+ require 'rails/generators/base'
2
+ require_relative 'compatibility'
3
+
4
+ module LanggraphRb
5
+ module Generators
6
+ class JobGenerator < Rails::Generators::NamedBase
7
+ include LanggraphRb::Generators::Compatibility
8
+ source_root File.expand_path('job/templates', __dir__)
9
+
10
+ desc "Creates a new job for LangGraphRB background processing"
11
+
12
+ def create_job_file
13
+ template "job.rb", File.join("app/jobs", "#{file_name}_job.rb")
14
+ end
15
+
16
+ private
17
+
18
+ def job_class_name
19
+ "#{class_name}Job"
20
+ end
21
+
22
+ def graph_class_name
23
+ "#{class_name.gsub(/Job$/, '')}Graph"
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,12 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration[<%= Rails::VERSION::MAJOR %>.<%= Rails::VERSION::MINOR %>]
2
+ def change
3
+ create_table :<%= table_name %> do |t|
4
+ t.string :thread_id, null: false, index: { unique: true }
5
+ t.json :state
6
+ <% attributes.each do |attribute| -%>
7
+ t.<%= attribute.type %> :<%= attribute.name %>
8
+ <% end -%>
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,15 @@
1
+ class <%= class_name %> < ApplicationRecord
2
+ # Serialize the state as JSON
3
+ serialize :state, JSON
4
+
5
+ # Validations
6
+ validates :thread_id, presence: true, uniqueness: true
7
+
8
+ # Class methods for LangGraphRB integration
9
+ class << self
10
+ # Create a store adapter that uses this model for persistence
11
+ def to_store
12
+ LanggraphrbRails::Stores::ActiveRecordStore.new(model: self)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,34 @@
1
+ require 'rails/generators/base'
2
+ require_relative 'compatibility'
3
+
4
+ module LanggraphRb
5
+ module Generators
6
+ class ModelGenerator < Rails::Generators::NamedBase
7
+ include LanggraphRb::Generators::Compatibility
8
+ include Rails::Generators::Migration
9
+ source_root File.expand_path('model/templates', __dir__)
10
+
11
+ desc "Creates a new model for LangGraphRB state persistence"
12
+
13
+ argument :attributes, type: :array, default: [], banner: "field:type field:type"
14
+
15
+ def create_model_file
16
+ template "model.rb", File.join("app/models", "#{file_name}.rb")
17
+ end
18
+
19
+ def create_migration
20
+ migration_template "migration.rb", "db/migrate/create_#{table_name}.rb"
21
+ end
22
+
23
+ private
24
+
25
+ def migration_class_name
26
+ "Create#{class_name.pluralize}"
27
+ end
28
+
29
+ def self.next_migration_number(dirname)
30
+ ActiveRecord::Generators::Base.next_migration_number(dirname)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,58 @@
1
+ namespace :langgraph_rb do
2
+ namespace :<%= task_name %> do
3
+ desc "Process pending <%= class_name %> tasks"
4
+ task process: :environment do
5
+ # Your task implementation goes here
6
+ # Example:
7
+ #
8
+ # require 'langgraphrb_rails'
9
+ #
10
+ # # Get pending tasks from your data source
11
+ # pending_tasks = YourModel.where(status: 'pending')
12
+ #
13
+ # # Process each task with LangGraphRB
14
+ # pending_tasks.each do |task|
15
+ # begin
16
+ # # Create a store for persistence
17
+ # store = LanggraphrbRails.create_store
18
+ #
19
+ # # Process the task with your graph
20
+ # result = <%= class_name %>Graph.invoke(
21
+ # { input: task.input },
22
+ # context: { task_id: task.id },
23
+ # store: store,
24
+ # thread_id: task.thread_id || SecureRandom.hex(8)
25
+ # )
26
+ #
27
+ # # Update the task with the result
28
+ # task.update!(
29
+ # status: 'completed',
30
+ # result: result[:response],
31
+ # thread_id: result[:thread_id]
32
+ # )
33
+ # rescue => e
34
+ # Rails.logger.error "Error processing task #{task.id}: #{e.message}"
35
+ # task.update(status: 'error', error_message: e.message)
36
+ # end
37
+ # end
38
+
39
+ puts "Processed <%= class_name %> tasks"
40
+ end
41
+
42
+ desc "Clean up expired <%= class_name %> tasks"
43
+ task cleanup: :environment do
44
+ # Example cleanup task
45
+ #
46
+ # # Delete old tasks
47
+ # YourModel.where('created_at < ?', 30.days.ago).delete_all
48
+ #
49
+ # # Clean up expired states from the store
50
+ # if defined?(GraphState)
51
+ # store = LanggraphrbRails::Stores::ActiveRecordStore.new(model: GraphState)
52
+ # store.cleanup_expired
53
+ # end
54
+
55
+ puts "Cleaned up <%= class_name %> tasks"
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,23 @@
1
+ require 'rails/generators/base'
2
+ require_relative 'compatibility'
3
+
4
+ module LanggraphRb
5
+ module Generators
6
+ class TaskGenerator < Rails::Generators::NamedBase
7
+ include LanggraphRb::Generators::Compatibility
8
+ source_root File.expand_path('task/templates', __dir__)
9
+
10
+ desc "Creates a new task for LangGraphRB background processing"
11
+
12
+ def create_task_file
13
+ template "task.rake", File.join("lib/tasks", "#{file_name}.rake")
14
+ end
15
+
16
+ private
17
+
18
+ def task_name
19
+ file_name.underscore
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,71 @@
1
+ module LanggraphrbRails
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,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ class <%= class_name %>Controller < ApplicationController
4
+ <% actions.each do |action| -%>
5
+ def <%= action %>
6
+ <% if action == "index" -%>
7
+ @runs = LanggraphRun.where(graph: "<%= class_name %>Graph").recent.limit(10)
8
+ <% elsif action == "show" -%>
9
+ @run = LanggraphRun.find(params[:id])
10
+ <% elsif action == "create" -%>
11
+ # Start a new graph run
12
+ run = LangGraphRB.start!(
13
+ graph: "<%= class_name %>Graph",
14
+ context: { user_id: current_user&.id },
15
+ state: {}
16
+ )
17
+
18
+ # Process the run in the background
19
+ Langgraph::RunJob.perform_later(run.id)
20
+
21
+ respond_to do |format|
22
+ format.html { redirect_to <%= singular_table_name %>_path(run), notice: "Graph run started." }
23
+ format.json { render json: { run_id: run.id, status: run.status, current_node: run.current_node } }
24
+ end
25
+ <% else -%>
26
+ # TODO: Implement <%= action %> action
27
+ <% end -%>
28
+ end
29
+ <% end -%>
30
+ end
@@ -0,0 +1,112 @@
1
+ <%% content_for :title, "<%= class_name %> - <%= action.titleize %>" %>
2
+
3
+ <div class="container mt-4">
4
+ <h1><%= class_name %> - <%= action.titleize %></h1>
5
+
6
+ <%% if local_assigns[:notice] %>
7
+ <div class="alert alert-success"><%%= notice %></div>
8
+ <%% end %>
9
+
10
+ <% if action == "index" -%>
11
+ <div class="card">
12
+ <div class="card-header">
13
+ <h2>Recent Graph Runs</h2>
14
+ </div>
15
+ <div class="card-body">
16
+ <table class="table">
17
+ <thead>
18
+ <tr>
19
+ <th>ID</th>
20
+ <th>Status</th>
21
+ <th>Current Node</th>
22
+ <th>Created</th>
23
+ <th>Actions</th>
24
+ </tr>
25
+ </thead>
26
+ <tbody>
27
+ <%% @runs.each do |run| %>
28
+ <tr>
29
+ <td><%%= run.id %></td>
30
+ <td><%%= run.status %></td>
31
+ <td><%%= run.current_node %></td>
32
+ <td><%%= run.created_at.to_s(:short) %></td>
33
+ <td>
34
+ <%%= link_to "View", <%= singular_table_name %>_path(run), class: "btn btn-sm btn-primary" %>
35
+ </td>
36
+ </tr>
37
+ <%% end %>
38
+ </tbody>
39
+ </table>
40
+
41
+ <%%= link_to "New Run", { action: :new }, class: "btn btn-success" if respond_to?(:new) %>
42
+ </div>
43
+ </div>
44
+ <% elsif action == "show" -%>
45
+ <div class="card">
46
+ <div class="card-header">
47
+ <h2>Graph Run #<%%= @run.id %></h2>
48
+ </div>
49
+ <div class="card-body">
50
+ <dl class="row">
51
+ <dt class="col-sm-3">Status</dt>
52
+ <dd class="col-sm-9"><%%= @run.status %></dd>
53
+
54
+ <dt class="col-sm-3">Graph</dt>
55
+ <dd class="col-sm-9"><%%= @run.graph %></dd>
56
+
57
+ <dt class="col-sm-3">Current Node</dt>
58
+ <dd class="col-sm-9"><%%= @run.current_node %></dd>
59
+
60
+ <dt class="col-sm-3">Created</dt>
61
+ <dd class="col-sm-9"><%%= @run.created_at %></dd>
62
+
63
+ <dt class="col-sm-3">Updated</dt>
64
+ <dd class="col-sm-9"><%%= @run.updated_at %></dd>
65
+
66
+ <%% if @run.error.present? %>
67
+ <dt class="col-sm-3">Error</dt>
68
+ <dd class="col-sm-9"><%%= @run.error %></dd>
69
+ <%% end %>
70
+ </dl>
71
+
72
+ <h3>State</h3>
73
+ <pre><%%= JSON.pretty_generate(@run.state) %></pre>
74
+
75
+ <h3>Context</h3>
76
+ <pre><%%= JSON.pretty_generate(@run.context) %></pre>
77
+
78
+ <%%= link_to "Back", <%= plural_table_name %>_path, class: "btn btn-secondary" %>
79
+ </div>
80
+ </div>
81
+ <% elsif action == "new" || action == "edit" -%>
82
+ <div class="card">
83
+ <div class="card-header">
84
+ <h2><%= action.titleize %> <%= class_name %></h2>
85
+ </div>
86
+ <div class="card-body">
87
+ <%%= form_with(url: <%= action == "new" ? plural_table_name %>_path : <%= singular_table_name %>_path, method: <%= action == "new" ? ":post" : ":patch" %>) do |form| %>
88
+ <div class="form-group">
89
+ <%%= form.label :input, "Input" %>
90
+ <%%= form.text_area :input, class: "form-control", rows: 5 %>
91
+ </div>
92
+
93
+ <div class="form-group mt-3">
94
+ <%%= form.submit "Submit", class: "btn btn-primary" %>
95
+ <%%= link_to "Cancel", <%= plural_table_name %>_path, class: "btn btn-secondary" %>
96
+ </div>
97
+ <%% end %>
98
+ </div>
99
+ </div>
100
+ <% else -%>
101
+ <div class="card">
102
+ <div class="card-header">
103
+ <h2><%= action.titleize %></h2>
104
+ </div>
105
+ <div class="card-body">
106
+ <p>TODO: Implement <%= action %> view</p>
107
+
108
+ <%%= link_to "Back", <%= plural_table_name %>_path, class: "btn btn-secondary" %>
109
+ </div>
110
+ </div>
111
+ <% end -%>
112
+ </div>
@@ -0,0 +1,29 @@
1
+ require 'rails/generators/base'
2
+ require_relative 'compatibility'
3
+
4
+ module LanggraphrbRails
5
+ module Generators
6
+ class ControllerGenerator < Rails::Generators::NamedBase
7
+ include LanggraphrbRails::Generators::Compatibility
8
+ source_root File.expand_path('templates', __dir__)
9
+
10
+ argument :actions, type: :array, default: [], banner: "action action"
11
+
12
+ desc "Creates a controller for LanggraphRB graph interaction"
13
+
14
+ def create_controller_file
15
+ template "controller.rb", "app/controllers/#{file_name}_controller.rb"
16
+ end
17
+
18
+ def create_views
19
+ actions.each do |action|
20
+ template "view.html.erb", "app/views/#{file_name}/#{action}.html.erb"
21
+ end
22
+ end
23
+
24
+ def add_routes
25
+ route "resources :#{file_name.pluralize}, only: [#{actions.map { |a| ":#{a}" }.join(', ')}]"
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class <%= class_name %>Graph
4
+ include LangGraphRB::Graph
5
+
6
+ # Define nodes and edges
7
+ <% if options[:nodes].present? %>
8
+ <%= node_connections.join("\n ") %>
9
+ <% else %>
10
+ node :start, to: :process
11
+ node :process, to: :finish
12
+ node :finish, terminal: true
13
+ <% end %>
14
+ end