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.
- checksums.yaml +7 -0
- data/README.md +816 -0
- data/Rakefile +23 -0
- data/app/assets/javascripts/langgraphrb_rails.js +153 -0
- data/app/assets/stylesheets/langgraphrb_rails.css +95 -0
- data/lib/generators/langgraph_rb/compatibility.rb +71 -0
- data/lib/generators/langgraph_rb/controller/templates/controller.rb +54 -0
- data/lib/generators/langgraph_rb/controller/templates/view.html.erb +101 -0
- data/lib/generators/langgraph_rb/controller_generator.rb +39 -0
- data/lib/generators/langgraph_rb/graph/templates/graph.rb +68 -0
- data/lib/generators/langgraph_rb/graph_generator.rb +23 -0
- data/lib/generators/langgraph_rb/install/templates/README +45 -0
- data/lib/generators/langgraph_rb/install/templates/example_graph.rb +89 -0
- data/lib/generators/langgraph_rb/install/templates/initializer.rb +35 -0
- data/lib/generators/langgraph_rb/install/templates/langgraph_rb.yml +45 -0
- data/lib/generators/langgraph_rb/install_generator.rb +34 -0
- data/lib/generators/langgraph_rb/job/templates/job.rb +38 -0
- data/lib/generators/langgraph_rb/job_generator.rb +27 -0
- data/lib/generators/langgraph_rb/model/templates/migration.rb +12 -0
- data/lib/generators/langgraph_rb/model/templates/model.rb +15 -0
- data/lib/generators/langgraph_rb/model_generator.rb +34 -0
- data/lib/generators/langgraph_rb/task/templates/task.rake +58 -0
- data/lib/generators/langgraph_rb/task_generator.rb +23 -0
- data/lib/generators/langgraphrb_rails/compatibility.rb +71 -0
- data/lib/generators/langgraphrb_rails/controller/templates/controller.rb +30 -0
- data/lib/generators/langgraphrb_rails/controller/templates/view.html.erb +112 -0
- data/lib/generators/langgraphrb_rails/controller_generator.rb +29 -0
- data/lib/generators/langgraphrb_rails/graph/templates/graph.rb +14 -0
- data/lib/generators/langgraphrb_rails/graph/templates/node.rb +16 -0
- data/lib/generators/langgraphrb_rails/graph_generator.rb +48 -0
- data/lib/generators/langgraphrb_rails/install/templates/config.yml +30 -0
- data/lib/generators/langgraphrb_rails/install/templates/example_graph.rb +44 -0
- data/lib/generators/langgraphrb_rails/install/templates/initializer.rb +27 -0
- data/lib/generators/langgraphrb_rails/install_generator.rb +35 -0
- data/lib/generators/langgraphrb_rails/jobs/templates/run_job.rb +45 -0
- data/lib/generators/langgraphrb_rails/jobs_generator.rb +34 -0
- data/lib/generators/langgraphrb_rails/model/templates/migration.rb +20 -0
- data/lib/generators/langgraphrb_rails/model/templates/model.rb +14 -0
- data/lib/generators/langgraphrb_rails/model_generator.rb +30 -0
- data/lib/generators/langgraphrb_rails/persistence/templates/create_langgraph_runs.rb +18 -0
- data/lib/generators/langgraphrb_rails/persistence/templates/langgraph_run.rb +56 -0
- data/lib/generators/langgraphrb_rails/persistence_generator.rb +28 -0
- data/lib/generators/langgraphrb_rails/task/templates/task.rake +30 -0
- data/lib/generators/langgraphrb_rails/task_generator.rb +17 -0
- data/lib/generators/langgraphrb_rails/tracing/templates/traced.rb +45 -0
- data/lib/generators/langgraphrb_rails/tracing_generator.rb +63 -0
- data/lib/langgraphrb_rails/configuration.rb +47 -0
- data/lib/langgraphrb_rails/engine.rb +20 -0
- data/lib/langgraphrb_rails/helper.rb +141 -0
- data/lib/langgraphrb_rails/middleware/streaming.rb +77 -0
- data/lib/langgraphrb_rails/railtie.rb +55 -0
- data/lib/langgraphrb_rails/stores/active_record.rb +51 -0
- data/lib/langgraphrb_rails/stores/redis.rb +57 -0
- data/lib/langgraphrb_rails/test_helper.rb +126 -0
- data/lib/langgraphrb_rails/version.rb +28 -0
- data/lib/langgraphrb_rails.rb +111 -0
- data/lib/tasks/langgraphrb_rails_tasks.rake +62 -0
- 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
|