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,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Nodes::<%= node_class_name(@node_name) %> < LangGraphRB::Node
4
+ # Include LangSmith tracing if available
5
+ include LangsmithTraced if defined?(LangsmithTraced)
6
+
7
+ def call(context:, state:)
8
+ # TODO: Implement <%= @node_name %> logic
9
+
10
+ # Example:
11
+ # result = "Processed by <%= @node_name %>"
12
+
13
+ # Return the updated state
14
+ ok state: state.merge("<%= @node_name %>_result" => "Processed")
15
+ end
16
+ end
@@ -0,0 +1,48 @@
1
+ require 'rails/generators/base'
2
+ require_relative 'compatibility'
3
+
4
+ module LanggraphrbRails
5
+ module Generators
6
+ class GraphGenerator < Rails::Generators::NamedBase
7
+ include LanggraphrbRails::Generators::Compatibility
8
+ source_root File.expand_path('templates', __dir__)
9
+
10
+ class_option :nodes, type: :string, desc: "Comma-separated list of node names to create"
11
+
12
+ desc "Creates a new LanggraphRB graph and associated nodes"
13
+
14
+ def create_graph_file
15
+ template "graph.rb", "app/langgraphs/#{file_name}_graph.rb"
16
+ end
17
+
18
+ def create_node_files
19
+ return unless options[:nodes].present?
20
+
21
+ nodes = options[:nodes].split(',').map(&:strip)
22
+ nodes.each do |node_name|
23
+ @node_name = node_name
24
+ template "node.rb", "app/langgraphs/nodes/#{node_name}.rb"
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def node_class_name(node_name)
31
+ node_name.camelize
32
+ end
33
+
34
+ def node_connections
35
+ return [] unless options[:nodes].present?
36
+
37
+ nodes = options[:nodes].split(',').map(&:strip)
38
+ nodes.map.with_index do |node, index|
39
+ if index == nodes.length - 1
40
+ "node :#{node}, terminal: true"
41
+ else
42
+ "node :#{node}, to: :#{nodes[index + 1]}"
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,30 @@
1
+ default: &default
2
+ store:
3
+ adapter: memory
4
+ options:
5
+ ttl: 3600
6
+ job:
7
+ queue: langgraphrb
8
+ max_retries: 3
9
+ error:
10
+ policy: retry
11
+ max_retries: 3
12
+
13
+ development:
14
+ <<: *default
15
+ store:
16
+ adapter: memory
17
+
18
+ test:
19
+ <<: *default
20
+ store:
21
+ adapter: memory
22
+
23
+ production:
24
+ <<: *default
25
+ store:
26
+ adapter: redis
27
+ options:
28
+ url: <%= ENV.fetch('REDIS_URL', 'redis://localhost:6379/0') %>
29
+ namespace: langgraphrb
30
+ ttl: 86400 # 24 hours
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Example graph demonstrating LanggraphRB integration with Rails
4
+ class ExampleGraph
5
+ include LangGraphRB::Graph
6
+
7
+ # Define nodes and edges
8
+ node :start, to: :process
9
+ node :process, to: :finish
10
+ node :finish, terminal: true
11
+
12
+ # You can also define conditional edges:
13
+ # node :process do |state|
14
+ # if state["needs_review"]
15
+ # :review
16
+ # else
17
+ # :finish
18
+ # end
19
+ # end
20
+ end
21
+
22
+ # Define your nodes in app/langgraphs/nodes/
23
+ # Example:
24
+ #
25
+ # class Nodes::Start < LangGraphRB::Node
26
+ # def call(context:, state:)
27
+ # # Initialize state with input
28
+ # ok state: state.merge("message" => "Processing started")
29
+ # end
30
+ # end
31
+ #
32
+ # class Nodes::Process < LangGraphRB::Node
33
+ # def call(context:, state:)
34
+ # # Process the input
35
+ # ok state: state.merge("processed" => true)
36
+ # end
37
+ # end
38
+ #
39
+ # class Nodes::Finish < LangGraphRB::Node
40
+ # def call(context:, state:)
41
+ # # Finalize and return result
42
+ # ok state: state.merge("completed" => true), terminal: true
43
+ # end
44
+ # end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ # LanggraphrbRails configuration
4
+ LanggraphrbRails.configure do |config|
5
+ # Configure the store adapter (memory, redis, active_record)
6
+ # config.store_adapter = Rails.env.production? ? :redis : :memory
7
+
8
+ # Configure the store options
9
+ # config.store_options = {
10
+ # ttl: 3600, # TTL in seconds (for Redis and ActiveRecord stores)
11
+ # namespace: 'langgraphrb' # Namespace for Redis keys
12
+ # }
13
+
14
+ # Configure error handling
15
+ # config.on_error = :retry # Options: :retry, :fail
16
+ # config.max_retries = 3
17
+
18
+ # Configure job queue
19
+ # config.job_queue = :langgraphrb
20
+ end
21
+
22
+ # Load environment-specific configuration
23
+ config_file = Rails.root.join('config', 'langgraphrb_rails.yml')
24
+ if File.exist?(config_file)
25
+ config = YAML.load(ERB.new(File.read(config_file)).result)[Rails.env]
26
+ LanggraphrbRails.configure_from_hash(config) if config
27
+ end
@@ -0,0 +1,35 @@
1
+ require 'rails/generators/base'
2
+ require_relative 'compatibility'
3
+
4
+ module LanggraphrbRails
5
+ module Generators
6
+ class InstallGenerator < Rails::Generators::Base
7
+ include LanggraphrbRails::Generators::Compatibility
8
+ source_root File.expand_path('templates', __dir__)
9
+
10
+ desc "Creates a LanggraphRB initializer and configuration file for your application"
11
+
12
+ def create_initializer_file
13
+ template "initializer.rb", "config/initializers/langgraphrb_rails.rb"
14
+ end
15
+
16
+ def create_config_file
17
+ template "config.yml", "config/langgraphrb_rails.yml"
18
+ end
19
+
20
+ def create_graphs_directory
21
+ empty_directory "app/langgraphs"
22
+ empty_directory "app/langgraphs/nodes"
23
+ end
24
+
25
+ def add_graphs_to_autoload_paths
26
+ application "config.autoload_paths += %W(#{Rails.root}/app/langgraphs)"
27
+ application "config.autoload_paths += %W(#{Rails.root}/app/langgraphs/nodes)"
28
+ end
29
+
30
+ def create_example_graph
31
+ template "example_graph.rb", "app/langgraphs/example_graph.rb"
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Langgraph
4
+ class RunJob < ApplicationJob
5
+ queue_as { LanggraphrbRails.configuration.job_queue || :default }
6
+
7
+ # Include LangSmith tracing if available
8
+ include LangsmithTracedJob if defined?(LangsmithTracedJob)
9
+
10
+ # Retry options
11
+ retry_on StandardError, wait: :exponentially_longer, attempts: 3
12
+
13
+ def perform(run_id)
14
+ # Find the run record
15
+ run = LanggraphRun.find_by(id: run_id)
16
+ return unless run
17
+
18
+ # Mark as running
19
+ run.running!
20
+
21
+ begin
22
+ # Resume the graph execution
23
+ result = LangGraphRB.resume!(run_id)
24
+
25
+ # Update the run record with the result
26
+ if result[:terminal]
27
+ run.succeeded!(result[:state])
28
+ else
29
+ # If not terminal, update state and re-queue
30
+ run.update(
31
+ state: result[:state],
32
+ current_node: result[:current_node]
33
+ )
34
+
35
+ # Re-queue the job to continue processing
36
+ self.class.perform_later(run_id)
37
+ end
38
+ rescue => e
39
+ # Handle errors
40
+ run.failed!(e.message)
41
+ raise # Re-raise to trigger retry mechanism
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,34 @@
1
+ require 'rails/generators/base'
2
+ require_relative 'compatibility'
3
+
4
+ module LanggraphrbRails
5
+ module Generators
6
+ class JobsGenerator < Rails::Generators::NamedBase
7
+ include LanggraphrbRails::Generators::Compatibility
8
+ source_root File.expand_path('templates', __dir__)
9
+
10
+ desc "Creates background jobs for LanggraphRB graph processing"
11
+
12
+ def create_run_job_file
13
+ template "run_job.rb", "app/jobs/langgraph/run_job.rb"
14
+ end
15
+
16
+ def create_jobs_directory
17
+ empty_directory "app/jobs/langgraph"
18
+ end
19
+
20
+ def add_job_configuration
21
+ initializer_path = "config/initializers/langgraphrb_rails.rb"
22
+
23
+ if File.exist?(Rails.root.join(initializer_path))
24
+ inject_into_file initializer_path, after: "LanggraphrbRails.configure do |config|\n" do
25
+ <<-RUBY
26
+ # Configure job queue for LanggraphRB jobs
27
+ config.job_queue = :langgraphrb
28
+ RUBY
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,20 @@
1
+ class Create<%= table_name.camelize %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
+ def change
3
+ create_table :<%= table_name %> do |t|
4
+ <% attributes.each do |attribute| -%>
5
+ <% if attribute.type == :references -%>
6
+ t.<%= attribute.type %> :<%= attribute.name %><%= attribute.inject_options %>
7
+ <% elsif attribute.type == :json || attribute.type == :jsonb -%>
8
+ t.<%= attribute.type %> :<%= attribute.name %>, default: {}<%= attribute.inject_options %>
9
+ <% else -%>
10
+ t.<%= attribute.type %> :<%= attribute.name %><%= attribute.inject_options %>
11
+ <% end -%>
12
+ <% end -%>
13
+ t.timestamps
14
+ end
15
+ <% attributes_with_index = attributes.select { |a| a.has_index? || a.reference? } -%>
16
+ <% attributes_with_index.each do |attribute| -%>
17
+ add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>
18
+ <% end -%>
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class <%= class_name %> < ApplicationRecord
4
+ # Add validations, associations, and methods as needed
5
+ <% if attributes.map(&:name).include?('thread_id') -%>
6
+ validates :thread_id, presence: true, uniqueness: true
7
+ <% end -%>
8
+ <% if attributes.map(&:name).include?('state') -%>
9
+ # Ensure state is always a hash
10
+ def state
11
+ super || {}
12
+ end
13
+ <% end -%>
14
+ end
@@ -0,0 +1,30 @@
1
+ require 'rails/generators/active_record'
2
+ require 'rails/generators/base'
3
+ require_relative 'compatibility'
4
+
5
+ module LanggraphrbRails
6
+ module Generators
7
+ class ModelGenerator < Rails::Generators::NamedBase
8
+ include Rails::Generators::Migration
9
+ include LanggraphrbRails::Generators::Compatibility
10
+
11
+ source_root File.expand_path('templates', __dir__)
12
+
13
+ argument :attributes, type: :array, default: [], banner: "field:type field:type"
14
+
15
+ desc "Creates a model for LanggraphRB state persistence"
16
+
17
+ def create_model_file
18
+ template "model.rb", "app/models/#{file_name}.rb"
19
+ end
20
+
21
+ def create_migration_file
22
+ migration_template "migration.rb", "db/migrate/create_#{table_name}.rb"
23
+ end
24
+
25
+ def self.next_migration_number(dirname)
26
+ ActiveRecord::Generators::Base.next_migration_number(dirname)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,18 @@
1
+ class CreateLanggraphRuns < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
+ def change
3
+ create_table :langgraph_runs do |t|
4
+ t.string :thread_id, null: false, index: true
5
+ t.string :graph, null: false
6
+ t.string :current_node
7
+ t.integer :status, default: 0, null: false
8
+ t.jsonb :state, default: {}
9
+ t.jsonb :context, default: {}
10
+ t.text :error
11
+ t.datetime :expires_at
12
+ t.timestamps
13
+ end
14
+
15
+ add_index :langgraph_runs, :status
16
+ add_index :langgraph_runs, :expires_at
17
+ end
18
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ class LanggraphRun < ApplicationRecord
4
+ # Status enum
5
+ enum status: {
6
+ queued: 0,
7
+ running: 1,
8
+ succeeded: 2,
9
+ failed: 3,
10
+ canceled: 4
11
+ }
12
+
13
+ # Validations
14
+ validates :thread_id, presence: true
15
+ validates :graph, presence: true
16
+ validates :status, presence: true
17
+
18
+ # Scopes
19
+ scope :active, -> { where(status: [:queued, :running]) }
20
+ scope :completed, -> { where(status: [:succeeded, :failed, :canceled]) }
21
+ scope :recent, -> { order(created_at: :desc) }
22
+
23
+ # Callbacks
24
+ before_create :set_default_status
25
+
26
+ # Methods
27
+ def running!
28
+ update(status: :running)
29
+ end
30
+
31
+ def succeeded!(final_state = nil)
32
+ update(status: :succeeded, state: final_state || state)
33
+ end
34
+
35
+ def failed!(error_message = nil)
36
+ update(status: :failed, error: error_message)
37
+ end
38
+
39
+ def canceled!
40
+ update(status: :canceled)
41
+ end
42
+
43
+ def active?
44
+ queued? || running?
45
+ end
46
+
47
+ def completed?
48
+ succeeded? || failed? || canceled?
49
+ end
50
+
51
+ private
52
+
53
+ def set_default_status
54
+ self.status ||= :queued
55
+ end
56
+ end
@@ -0,0 +1,28 @@
1
+ require 'rails/generators/base'
2
+ require 'rails/generators/active_record'
3
+ require_relative 'compatibility'
4
+
5
+ module LanggraphrbRails
6
+ module Generators
7
+ class PersistenceGenerator < Rails::Generators::Base
8
+ include Rails::Generators::Migration
9
+ include LanggraphrbRails::Generators::Compatibility
10
+
11
+ source_root File.expand_path('templates', __dir__)
12
+
13
+ desc "Creates a LanggraphRun model and migration for persistence"
14
+
15
+ def create_model_file
16
+ template "langgraph_run.rb", "app/models/langgraph_run.rb"
17
+ end
18
+
19
+ def create_migration_file
20
+ migration_template "create_langgraph_runs.rb", "db/migrate/create_langgraph_runs.rb"
21
+ end
22
+
23
+ def self.next_migration_number(dirname)
24
+ ActiveRecord::Generators::Base.next_migration_number(dirname)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ namespace :langgraph_<%= file_name %> do
2
+ desc "Process pending <%= class_name %> graph runs"
3
+ task process: :environment do
4
+ # Find queued runs for this graph
5
+ runs = LanggraphRun.where(graph: "<%= class_name %>Graph", status: :queued)
6
+
7
+ puts "Processing #{runs.count} pending <%= class_name %> graph runs..."
8
+
9
+ runs.each do |run|
10
+ puts "Queueing run ##{run.id}..."
11
+ Langgraph::RunJob.perform_later(run.id)
12
+ end
13
+
14
+ puts "Done."
15
+ end
16
+
17
+ desc "Clean up expired <%= class_name %> graph runs"
18
+ task cleanup: :environment do
19
+ # Find expired runs
20
+ expired = LanggraphRun.where(graph: "<%= class_name %>Graph")
21
+ .where("expires_at < ?", Time.current)
22
+
23
+ count = expired.count
24
+ puts "Cleaning up #{count} expired <%= class_name %> graph runs..."
25
+
26
+ expired.destroy_all
27
+
28
+ puts "Removed #{count} expired runs."
29
+ end
30
+ end
@@ -0,0 +1,17 @@
1
+ require 'rails/generators/base'
2
+ require_relative 'compatibility'
3
+
4
+ module LanggraphrbRails
5
+ module Generators
6
+ class TaskGenerator < Rails::Generators::NamedBase
7
+ include LanggraphrbRails::Generators::Compatibility
8
+ source_root File.expand_path('templates', __dir__)
9
+
10
+ desc "Creates rake tasks for LanggraphRB graph processing"
11
+
12
+ def create_task_file
13
+ template "task.rake", "lib/tasks/langgraph_#{file_name}.rake"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Include this concern in your LangGraphRB nodes to enable LangSmith tracing
4
+ module LangsmithTraced
5
+ extend ActiveSupport::Concern
6
+
7
+ # Trace a block of code with LangSmith
8
+ def trace(name:, type: "chain", meta: {})
9
+ return yield unless defined?(LangsmithrbRails) && LangsmithrbRails.configured?
10
+
11
+ tracer = LangsmithrbRails.tracer
12
+
13
+ # Create a new trace
14
+ tracer.trace(name: name, type: type, meta: meta) do |run|
15
+ # Execute the block and capture the result
16
+ result = yield
17
+
18
+ # Add the output to the trace
19
+ run.add_output(result)
20
+
21
+ # Return the result
22
+ result
23
+ end
24
+ end
25
+
26
+ # Override the call method to automatically trace node execution
27
+ def call_with_tracing(context:, state:)
28
+ return call_without_tracing(context: context, state: state) unless defined?(LangsmithrbRails) && LangsmithrbRails.configured?
29
+
30
+ # Get the node name from the class
31
+ node_name = self.class.name.demodulize.underscore
32
+
33
+ # Create a trace for this node
34
+ trace(name: node_name, type: "node", meta: { context: context, input_state: state }) do
35
+ # Call the original method
36
+ call_without_tracing(context: context, state: state)
37
+ end
38
+ end
39
+
40
+ included do
41
+ # Alias the original call method
42
+ alias_method :call_without_tracing, :call
43
+ alias_method :call, :call_with_tracing
44
+ end
45
+ end
@@ -0,0 +1,63 @@
1
+ require 'rails/generators/base'
2
+ require_relative 'compatibility'
3
+
4
+ module LanggraphrbRails
5
+ module Generators
6
+ class TracingGenerator < Rails::Generators::Base
7
+ include LanggraphrbRails::Generators::Compatibility
8
+ source_root File.expand_path('templates', __dir__)
9
+
10
+ desc "Creates LangSmith tracing integration for LanggraphRB"
11
+
12
+ def check_langsmithrb_rails
13
+ unless gem_available?('langsmithrb_rails')
14
+ say "The langsmithrb_rails gem is required for tracing. Please add it to your Gemfile.", :red
15
+ exit 1
16
+ end
17
+ end
18
+
19
+ def create_concern_file
20
+ template "tracing/traced.rb", "app/models/concerns/langsmith_traced.rb"
21
+ end
22
+
23
+ def update_initializer
24
+ if File.exist?('config/initializers/langgraphrb_rails.rb')
25
+ inject_into_file 'config/initializers/langgraphrb_rails.rb', after: "LanggraphrbRails.configure do |config|\n" do
26
+ <<-RUBY
27
+ # Configure LangSmith tracing
28
+ if defined?(LangsmithrbRails) && ENV['LANGSMITH_API_KEY'].present?
29
+ config.observers << LangsmithrbRails::Observer.new(
30
+ api_key: ENV['LANGSMITH_API_KEY'],
31
+ project_name: ENV.fetch('LANGSMITH_PROJECT', 'langgraph-rails')
32
+ )
33
+ end
34
+ RUBY
35
+ end
36
+ else
37
+ say "Initializer not found. Please run the install generator first.", :red
38
+ end
39
+ end
40
+
41
+ def add_environment_variables
42
+ append_to_file '.env.sample' do
43
+ <<-ENV
44
+
45
+ # LangSmith Tracing (optional)
46
+ # LANGSMITH_API_KEY=your_api_key_here
47
+ # LANGSMITH_PROJECT=your_project_name
48
+ ENV
49
+ end unless File.exist?('.env.sample')
50
+ end
51
+
52
+ private
53
+
54
+ def gem_available?(name)
55
+ Gem::Specification.find_by_name(name)
56
+ rescue Gem::LoadError
57
+ false
58
+ rescue
59
+ Gem.available?(name)
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,47 @@
1
+ module LanggraphrbRails
2
+ class Configuration
3
+ attr_accessor :store_adapter, :store_options
4
+ attr_accessor :observers
5
+ attr_accessor :job_queue, :on_error, :max_retries
6
+
7
+ def initialize
8
+ @store_adapter = :memory
9
+ @store_options = {}
10
+ @observers = []
11
+ @job_queue = :default
12
+ @on_error = :retry
13
+ @max_retries = 3
14
+ end
15
+ end
16
+
17
+ class << self
18
+ def configuration
19
+ @configuration ||= Configuration.new
20
+ end
21
+
22
+ def configure
23
+ yield(configuration) if block_given?
24
+ end
25
+
26
+ def configure_store
27
+ yield(store_config = {}) if block_given?
28
+
29
+ configuration.store_adapter = store_config[:adapter] if store_config[:adapter]
30
+ configuration.store_options = store_config[:options] if store_config[:options]
31
+ end
32
+
33
+ def configure_observers
34
+ yield(configuration.observers) if block_given?
35
+ end
36
+
37
+ def store
38
+ case configuration.store_adapter.to_sym
39
+ when :redis
40
+ require_relative 'stores/redis'
41
+ LanggraphrbRails::Stores::RedisStore.new(configuration.store_options)
42
+ else
43
+ LangGraphRB::Stores::InMemoryStore.new
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,20 @@
1
+ module LanggraphrbRails
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace LanggraphrbRails
4
+
5
+ # Add generators
6
+ config.app_generators do |g|
7
+ g.templates.unshift File.expand_path('../../generators/langgraph_rb/templates', __dir__)
8
+ end
9
+
10
+ initializer "langgraphrb_rails.setup" do |app|
11
+ # Load any initializers or configuration
12
+ end
13
+
14
+ initializer "langgraphrb_rails.helpers" do |app|
15
+ ActiveSupport.on_load(:action_view) do
16
+ include LanggraphrbRails::Helper
17
+ end
18
+ end
19
+ end
20
+ end