rcrewai-rails 0.1.1
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/MIT-LICENSE +20 -0
- data/README.md +222 -0
- data/Rakefile +6 -0
- data/app/controllers/rcrewai/rails/application_controller.rb +17 -0
- data/app/controllers/rcrewai/rails/crews_controller.rb +67 -0
- data/app/controllers/rcrewai/rails/executions_controller.rb +65 -0
- data/app/jobs/rcrewai/rails/crew_execution_job.rb +82 -0
- data/app/jobs/rcrewai/rails/task_execution_job.rb +63 -0
- data/app/models/rcrewai/rails/agent.rb +58 -0
- data/app/models/rcrewai/rails/application_record.rb +8 -0
- data/app/models/rcrewai/rails/crew.rb +65 -0
- data/app/models/rcrewai/rails/execution.rb +98 -0
- data/app/models/rcrewai/rails/execution_log.rb +18 -0
- data/app/models/rcrewai/rails/task.rb +69 -0
- data/app/models/rcrewai/rails/task_assignment.rb +12 -0
- data/app/models/rcrewai/rails/task_dependency.rb +42 -0
- data/app/views/layouts/rcrewai/rails/application.html.erb +37 -0
- data/app/views/rcrewai/rails/crews/index.html.erb +42 -0
- data/app/views/rcrewai/rails/crews/show.html.erb +95 -0
- data/app/views/rcrewai/rails/executions/show.html.erb +92 -0
- data/config/routes.rb +50 -0
- data/lib/generators/rcrew_a_i/rails/crew/crew_generator.rb +42 -0
- data/lib/generators/rcrew_a_i/rails/crew/templates/agent.rb.erb +45 -0
- data/lib/generators/rcrew_a_i/rails/crew/templates/crew.rb.erb +72 -0
- data/lib/generators/rcrew_a_i/rails/install/install_generator.rb +40 -0
- data/lib/generators/rcrew_a_i/rails/install/templates/create_rcrewai_tables.rb +113 -0
- data/lib/generators/rcrew_a_i/rails/install/templates/rcrewai.rb +53 -0
- data/lib/generators/rcrewai/rails/crew/crew_generator.rb +42 -0
- data/lib/generators/rcrewai/rails/crew/templates/agent.rb.erb +45 -0
- data/lib/generators/rcrewai/rails/crew/templates/crew.rb.erb +72 -0
- data/lib/generators/rcrewai/rails/install/install_generator.rb +40 -0
- data/lib/generators/rcrewai/rails/install/templates/create_rcrewai_tables.rb +113 -0
- data/lib/generators/rcrewai/rails/install/templates/rcrewai.rb +53 -0
- data/lib/rcrewai/rails/agent_builder.rb +123 -0
- data/lib/rcrewai/rails/configuration.rb +22 -0
- data/lib/rcrewai/rails/crew_builder.rb +112 -0
- data/lib/rcrewai/rails/engine.rb +38 -0
- data/lib/rcrewai/rails/tools/action_mailer_tool.rb +60 -0
- data/lib/rcrewai/rails/tools/active_record_tool.rb +67 -0
- data/lib/rcrewai/rails/tools/active_storage_tool.rb +122 -0
- data/lib/rcrewai/rails/tools/rails_cache_tool.rb +69 -0
- data/lib/rcrewai/rails/tools/rails_logger_tool.rb +57 -0
- data/lib/rcrewai/rails/version.rb +5 -0
- data/lib/rcrewai/rails.rb +31 -0
- data/lib/rcrewai-rails.rb +1 -0
- data/rcrewai-rails.gemspec +48 -0
- metadata +261 -0
@@ -0,0 +1,123 @@
|
|
1
|
+
module RcrewAI
|
2
|
+
module Rails
|
3
|
+
module AgentBuilder
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
attr_reader :agent
|
8
|
+
end
|
9
|
+
|
10
|
+
class_methods do
|
11
|
+
def agent_role(role = nil)
|
12
|
+
@agent_role = role if role
|
13
|
+
@agent_role || "AI Assistant"
|
14
|
+
end
|
15
|
+
|
16
|
+
def agent_goal(goal = nil)
|
17
|
+
@agent_goal = goal if goal
|
18
|
+
@agent_goal || "Complete assigned tasks efficiently"
|
19
|
+
end
|
20
|
+
|
21
|
+
def agent_backstory(backstory = nil)
|
22
|
+
@agent_backstory = backstory if backstory
|
23
|
+
@agent_backstory || "You are a helpful AI assistant."
|
24
|
+
end
|
25
|
+
|
26
|
+
def memory_enabled(enabled = nil)
|
27
|
+
@memory_enabled = enabled unless enabled.nil?
|
28
|
+
@memory_enabled || true
|
29
|
+
end
|
30
|
+
|
31
|
+
def allow_delegation(allowed = nil)
|
32
|
+
@allow_delegation = allowed unless allowed.nil?
|
33
|
+
@allow_delegation || false
|
34
|
+
end
|
35
|
+
|
36
|
+
def max_iterations(iterations = nil)
|
37
|
+
@max_iterations = iterations if iterations
|
38
|
+
@max_iterations || 25
|
39
|
+
end
|
40
|
+
|
41
|
+
def tools(*tool_classes)
|
42
|
+
@tools ||= []
|
43
|
+
@tools.concat(tool_classes) if tool_classes.any?
|
44
|
+
@tools
|
45
|
+
end
|
46
|
+
|
47
|
+
def llm_config(config = nil)
|
48
|
+
@llm_config = config if config
|
49
|
+
@llm_config || default_llm_config
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def default_llm_config
|
55
|
+
{
|
56
|
+
provider: RcrewAI::Rails.config.default_llm_provider,
|
57
|
+
model: RcrewAI::Rails.config.default_llm_model
|
58
|
+
}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def initialize(attributes = {})
|
63
|
+
@attributes = attributes
|
64
|
+
@agent = build_agent
|
65
|
+
setup_tools
|
66
|
+
configure_agent
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_agent
|
70
|
+
@agent
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_rcrew_agent
|
74
|
+
@agent
|
75
|
+
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
|
79
|
+
def build_agent
|
80
|
+
RCrewAI::Agent.new(
|
81
|
+
role: @attributes[:role] || self.class.agent_role,
|
82
|
+
goal: @attributes[:goal] || self.class.agent_goal,
|
83
|
+
backstory: @attributes[:backstory] || self.class.agent_backstory,
|
84
|
+
memory: @attributes[:memory] || self.class.memory_enabled,
|
85
|
+
verbose: verbose?,
|
86
|
+
allow_delegation: @attributes[:allow_delegation] || self.class.allow_delegation,
|
87
|
+
max_iter: @attributes[:max_iterations] || self.class.max_iterations,
|
88
|
+
llm: @attributes[:llm_config] || self.class.llm_config
|
89
|
+
)
|
90
|
+
end
|
91
|
+
|
92
|
+
def setup_tools
|
93
|
+
tools = instantiate_tools
|
94
|
+
@agent.tools = tools if tools.any?
|
95
|
+
end
|
96
|
+
|
97
|
+
def instantiate_tools
|
98
|
+
tool_classes = @attributes[:tools] || self.class.tools
|
99
|
+
tool_classes.map do |tool_class|
|
100
|
+
case tool_class
|
101
|
+
when Class
|
102
|
+
tool_class.new
|
103
|
+
when Hash
|
104
|
+
klass = tool_class[:class] || tool_class["class"]
|
105
|
+
params = tool_class[:params] || tool_class["params"] || {}
|
106
|
+
klass = klass.constantize if klass.is_a?(String)
|
107
|
+
klass.new(**params.symbolize_keys)
|
108
|
+
else
|
109
|
+
tool_class
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def configure_agent
|
115
|
+
# Override in subclass for additional configuration
|
116
|
+
end
|
117
|
+
|
118
|
+
def verbose?
|
119
|
+
::Rails.env.development? || ENV['RCREWAI_VERBOSE'] == 'true'
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module RcrewAI
|
2
|
+
module Rails
|
3
|
+
class Configuration
|
4
|
+
attr_accessor :job_queue_name, :enable_web_ui, :persistence_backend,
|
5
|
+
:default_llm_provider, :default_llm_model, :max_retries,
|
6
|
+
:timeout, :enable_logging, :log_level, :async_execution
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@job_queue_name = "default"
|
10
|
+
@enable_web_ui = true
|
11
|
+
@persistence_backend = :active_record
|
12
|
+
@default_llm_provider = "openai"
|
13
|
+
@default_llm_model = "gpt-4"
|
14
|
+
@max_retries = 3
|
15
|
+
@timeout = 300 # 5 minutes
|
16
|
+
@enable_logging = true
|
17
|
+
@log_level = :info
|
18
|
+
@async_execution = true # Use ActiveJob for async by default
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module RcrewAI
|
2
|
+
module Rails
|
3
|
+
module CrewBuilder
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
attr_reader :crew
|
8
|
+
end
|
9
|
+
|
10
|
+
class_methods do
|
11
|
+
def crew_name(name = nil)
|
12
|
+
if name
|
13
|
+
@crew_name = name
|
14
|
+
else
|
15
|
+
@crew_name || self.name.underscore.gsub(/_crew$/, '')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def crew_description(description = nil)
|
20
|
+
@crew_description = description if description
|
21
|
+
@crew_description
|
22
|
+
end
|
23
|
+
|
24
|
+
def process_type(type = nil)
|
25
|
+
@process_type = type if type
|
26
|
+
@process_type || :sequential
|
27
|
+
end
|
28
|
+
|
29
|
+
def memory_enabled(enabled = nil)
|
30
|
+
@memory_enabled = enabled unless enabled.nil?
|
31
|
+
@memory_enabled || false
|
32
|
+
end
|
33
|
+
|
34
|
+
def cache_enabled(enabled = nil)
|
35
|
+
@cache_enabled = enabled unless enabled.nil?
|
36
|
+
@cache_enabled || true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def initialize
|
41
|
+
@crew = find_or_create_crew
|
42
|
+
setup_agents
|
43
|
+
setup_tasks
|
44
|
+
setup_callbacks
|
45
|
+
end
|
46
|
+
|
47
|
+
def execute(inputs = {})
|
48
|
+
if RcrewAI::Rails.config.async_execution
|
49
|
+
@crew.execute_async(inputs)
|
50
|
+
else
|
51
|
+
@crew.execute_sync(inputs)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def execute_async(inputs = {})
|
56
|
+
@crew.execute_async(inputs)
|
57
|
+
end
|
58
|
+
|
59
|
+
def execute_sync(inputs = {})
|
60
|
+
@crew.execute_sync(inputs)
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
|
65
|
+
def find_or_create_crew
|
66
|
+
RcrewAI::Rails::Crew.find_or_create_by(name: self.class.crew_name) do |crew|
|
67
|
+
crew.description = self.class.crew_description
|
68
|
+
crew.process_type = self.class.process_type.to_s
|
69
|
+
crew.memory_enabled = self.class.memory_enabled
|
70
|
+
crew.cache_enabled = self.class.cache_enabled
|
71
|
+
crew.verbose = verbose?
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def setup_agents
|
76
|
+
# Override in subclass
|
77
|
+
end
|
78
|
+
|
79
|
+
def setup_tasks
|
80
|
+
# Override in subclass
|
81
|
+
end
|
82
|
+
|
83
|
+
def setup_callbacks
|
84
|
+
# Override in subclass
|
85
|
+
end
|
86
|
+
|
87
|
+
def verbose?
|
88
|
+
::Rails.env.development? || ENV['RCREWAI_VERBOSE'] == 'true'
|
89
|
+
end
|
90
|
+
|
91
|
+
def create_agent(name, attributes = {})
|
92
|
+
@crew.agents.find_or_create_by(name: name) do |agent|
|
93
|
+
agent.assign_attributes(attributes)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def create_task(description, attributes = {})
|
98
|
+
@crew.tasks.find_or_create_by(description: description) do |task|
|
99
|
+
task.assign_attributes(attributes)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def assign_agent_to_task(agent, task)
|
104
|
+
task.agents << agent unless task.agents.include?(agent)
|
105
|
+
end
|
106
|
+
|
107
|
+
def add_task_dependency(task, dependency)
|
108
|
+
task.add_dependency(dependency)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module RcrewAI
|
2
|
+
module Rails
|
3
|
+
class Engine < ::Rails::Engine
|
4
|
+
isolate_namespace RcrewAI::Rails
|
5
|
+
|
6
|
+
config.generators do |g|
|
7
|
+
g.test_framework :rspec
|
8
|
+
g.fixture_replacement :factory_bot
|
9
|
+
g.factory_bot dir: 'spec/factories'
|
10
|
+
end
|
11
|
+
|
12
|
+
initializer "rcrewai_rails.assets" do |app|
|
13
|
+
app.config.assets.paths << root.join("app/assets/stylesheets")
|
14
|
+
app.config.assets.paths << root.join("app/assets/javascripts")
|
15
|
+
end
|
16
|
+
|
17
|
+
initializer "rcrewai_rails.load_models" do
|
18
|
+
ActiveSupport.on_load(:active_record) do
|
19
|
+
Dir[Engine.root.join("app/models/rcrewai/rails/*.rb")].each { |f| require f }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
initializer "rcrewai_rails.load_jobs" do
|
24
|
+
ActiveSupport.on_load(:active_job) do
|
25
|
+
Dir[Engine.root.join("app/jobs/rcrewai/rails/*.rb")].each { |f| require f }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
initializer "rcrewai_rails.configure" do |app|
|
30
|
+
RcrewAI::Rails.configure do |config|
|
31
|
+
config.job_queue_name = ENV.fetch("RCREWAI_QUEUE", "default")
|
32
|
+
config.enable_web_ui = ENV.fetch("RCREWAI_WEB_UI", "true") == "true"
|
33
|
+
config.async_execution = ENV.fetch("RCREWAI_ASYNC", "true") == "true"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module RcrewAI
|
2
|
+
module Rails
|
3
|
+
module Tools
|
4
|
+
class ActionMailerTool < RCrewAI::Tools::Base
|
5
|
+
def initialize(mailer_class: nil, allowed_methods: [])
|
6
|
+
@mailer_class = mailer_class
|
7
|
+
@allowed_methods = allowed_methods
|
8
|
+
super(
|
9
|
+
name: "Action Mailer Tool",
|
10
|
+
description: "Send emails using Rails Action Mailer"
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
def execute(mailer_method, params = {}, deliver_method = :deliver_later)
|
15
|
+
validate_mailer_method!(mailer_method)
|
16
|
+
|
17
|
+
mailer = get_mailer_class(params.delete(:mailer) || @mailer_class)
|
18
|
+
|
19
|
+
# Build the mail object
|
20
|
+
mail = mailer.send(mailer_method, **params)
|
21
|
+
|
22
|
+
# Deliver based on specified method
|
23
|
+
case deliver_method.to_sym
|
24
|
+
when :deliver_now
|
25
|
+
mail.deliver_now
|
26
|
+
{ status: "sent", method: mailer_method, delivered_at: Time.current }
|
27
|
+
when :deliver_later
|
28
|
+
job = mail.deliver_later
|
29
|
+
{ status: "queued", method: mailer_method, job_id: job.job_id }
|
30
|
+
when :deliver_later_at
|
31
|
+
delivery_time = params.delete(:at) || 1.hour.from_now
|
32
|
+
job = mail.deliver_later(wait_until: delivery_time)
|
33
|
+
{ status: "scheduled", method: mailer_method, job_id: job.job_id, scheduled_for: delivery_time }
|
34
|
+
else
|
35
|
+
{ error: "Unknown delivery method: #{deliver_method}" }
|
36
|
+
end
|
37
|
+
rescue => e
|
38
|
+
{ error: "Failed to send email", message: e.message }
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def validate_mailer_method!(method)
|
44
|
+
if @allowed_methods.any? && !@allowed_methods.include?(method.to_sym)
|
45
|
+
raise ArgumentError, "Mailer method '#{method}' is not allowed"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def get_mailer_class(mailer_name)
|
50
|
+
return @mailer_class if @mailer_class && mailer_name.nil?
|
51
|
+
|
52
|
+
mailer_name = "#{mailer_name.to_s.camelize}Mailer" unless mailer_name.to_s.end_with?("Mailer")
|
53
|
+
mailer_name.constantize
|
54
|
+
rescue NameError
|
55
|
+
raise ArgumentError, "Mailer class '#{mailer_name}' not found"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module RcrewAI
|
2
|
+
module Rails
|
3
|
+
module Tools
|
4
|
+
class ActiveRecordTool < RCrewAI::Tools::Base
|
5
|
+
def initialize(model_class: nil, allowed_methods: [:find, :where, :count])
|
6
|
+
@model_class = model_class
|
7
|
+
@allowed_methods = allowed_methods
|
8
|
+
super(
|
9
|
+
name: "ActiveRecord Query Tool",
|
10
|
+
description: "Query Rails database using ActiveRecord"
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
def execute(query_type, conditions = {})
|
15
|
+
validate_query_type!(query_type)
|
16
|
+
|
17
|
+
model = get_model_class(conditions.delete(:model) || @model_class)
|
18
|
+
|
19
|
+
case query_type.to_sym
|
20
|
+
when :find
|
21
|
+
model.find(conditions[:id])
|
22
|
+
when :find_by
|
23
|
+
model.find_by(conditions)
|
24
|
+
when :where
|
25
|
+
model.where(conditions)
|
26
|
+
when :count
|
27
|
+
conditions.empty? ? model.count : model.where(conditions).count
|
28
|
+
when :pluck
|
29
|
+
field = conditions.delete(:field)
|
30
|
+
model.where(conditions).pluck(field)
|
31
|
+
when :exists?
|
32
|
+
model.exists?(conditions)
|
33
|
+
when :first
|
34
|
+
model.where(conditions).first
|
35
|
+
when :last
|
36
|
+
model.where(conditions).last
|
37
|
+
when :all
|
38
|
+
model.where(conditions).to_a
|
39
|
+
else
|
40
|
+
raise ArgumentError, "Unsupported query type: #{query_type}"
|
41
|
+
end
|
42
|
+
rescue ActiveRecord::RecordNotFound => e
|
43
|
+
{ error: "Record not found", message: e.message }
|
44
|
+
rescue => e
|
45
|
+
{ error: "Query failed", message: e.message }
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def validate_query_type!(query_type)
|
51
|
+
unless @allowed_methods.include?(query_type.to_sym)
|
52
|
+
raise ArgumentError, "Query type '#{query_type}' is not allowed"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_model_class(model_name)
|
57
|
+
return @model_class if @model_class && model_name.nil?
|
58
|
+
|
59
|
+
model_name = model_name.to_s.camelize
|
60
|
+
model_name.constantize
|
61
|
+
rescue NameError
|
62
|
+
raise ArgumentError, "Model class '#{model_name}' not found"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
module RcrewAI
|
2
|
+
module Rails
|
3
|
+
module Tools
|
4
|
+
class ActiveStorageTool < RCrewAI::Tools::Base
|
5
|
+
def initialize(allowed_operations: [:attach, :download, :analyze])
|
6
|
+
@allowed_operations = allowed_operations
|
7
|
+
super(
|
8
|
+
name: "Active Storage Tool",
|
9
|
+
description: "Manage file attachments using Rails Active Storage"
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute(operation, params = {})
|
14
|
+
validate_operation!(operation)
|
15
|
+
|
16
|
+
case operation.to_sym
|
17
|
+
when :attach
|
18
|
+
attach_file(params)
|
19
|
+
when :download
|
20
|
+
download_file(params)
|
21
|
+
when :analyze
|
22
|
+
analyze_file(params)
|
23
|
+
when :delete
|
24
|
+
delete_file(params)
|
25
|
+
when :url
|
26
|
+
get_url(params)
|
27
|
+
else
|
28
|
+
{ error: "Unknown operation: #{operation}" }
|
29
|
+
end
|
30
|
+
rescue => e
|
31
|
+
{ error: "Active Storage operation failed", message: e.message }
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def validate_operation!(operation)
|
37
|
+
unless @allowed_operations.include?(operation.to_sym)
|
38
|
+
raise ArgumentError, "Operation '#{operation}' is not allowed"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def attach_file(params)
|
43
|
+
record = find_record(params[:model], params[:id])
|
44
|
+
attachment_name = params[:attachment_name] || :file
|
45
|
+
|
46
|
+
file = params[:file] || params[:io]
|
47
|
+
filename = params[:filename] || "attachment"
|
48
|
+
content_type = params[:content_type] || "application/octet-stream"
|
49
|
+
|
50
|
+
record.send(attachment_name).attach(
|
51
|
+
io: file,
|
52
|
+
filename: filename,
|
53
|
+
content_type: content_type
|
54
|
+
)
|
55
|
+
|
56
|
+
{ attached: true, filename: filename, record_id: record.id }
|
57
|
+
end
|
58
|
+
|
59
|
+
def download_file(params)
|
60
|
+
blob = find_blob(params)
|
61
|
+
|
62
|
+
{
|
63
|
+
filename: blob.filename.to_s,
|
64
|
+
content_type: blob.content_type,
|
65
|
+
byte_size: blob.byte_size,
|
66
|
+
data: blob.download
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
def analyze_file(params)
|
71
|
+
blob = find_blob(params)
|
72
|
+
blob.analyze unless blob.analyzed?
|
73
|
+
|
74
|
+
{
|
75
|
+
filename: blob.filename.to_s,
|
76
|
+
content_type: blob.content_type,
|
77
|
+
byte_size: blob.byte_size,
|
78
|
+
metadata: blob.metadata,
|
79
|
+
analyzed: blob.analyzed?
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
def delete_file(params)
|
84
|
+
blob = find_blob(params)
|
85
|
+
filename = blob.filename.to_s
|
86
|
+
blob.purge
|
87
|
+
|
88
|
+
{ deleted: true, filename: filename }
|
89
|
+
end
|
90
|
+
|
91
|
+
def get_url(params)
|
92
|
+
blob = find_blob(params)
|
93
|
+
expires_in = params[:expires_in] || 5.minutes
|
94
|
+
|
95
|
+
{
|
96
|
+
url: ::Rails.application.routes.url_helpers.rails_blob_url(blob, expires_in: expires_in),
|
97
|
+
expires_at: expires_in.from_now
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
def find_record(model_name, id)
|
102
|
+
model_class = model_name.to_s.camelize.constantize
|
103
|
+
model_class.find(id)
|
104
|
+
end
|
105
|
+
|
106
|
+
def find_blob(params)
|
107
|
+
if params[:blob_id]
|
108
|
+
ActiveStorage::Blob.find(params[:blob_id])
|
109
|
+
elsif params[:signed_id]
|
110
|
+
ActiveStorage::Blob.find_signed(params[:signed_id])
|
111
|
+
elsif params[:key]
|
112
|
+
ActiveStorage::Blob.find_by!(key: params[:key])
|
113
|
+
else
|
114
|
+
record = find_record(params[:model], params[:id])
|
115
|
+
attachment_name = params[:attachment_name] || :file
|
116
|
+
record.send(attachment_name).blob
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module RcrewAI
|
2
|
+
module Rails
|
3
|
+
module Tools
|
4
|
+
class RailsCacheTool < RCrewAI::Tools::Base
|
5
|
+
def initialize
|
6
|
+
super(
|
7
|
+
name: "Rails Cache Tool",
|
8
|
+
description: "Read and write to Rails cache"
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(action, key, value = nil, options = {})
|
13
|
+
case action.to_sym
|
14
|
+
when :read
|
15
|
+
read_cache(key)
|
16
|
+
when :write
|
17
|
+
write_cache(key, value, options)
|
18
|
+
when :delete
|
19
|
+
delete_cache(key)
|
20
|
+
when :exist?
|
21
|
+
cache_exists?(key)
|
22
|
+
when :fetch
|
23
|
+
fetch_cache(key, options) { value }
|
24
|
+
when :clear
|
25
|
+
clear_cache
|
26
|
+
else
|
27
|
+
{ error: "Unknown action: #{action}" }
|
28
|
+
end
|
29
|
+
rescue => e
|
30
|
+
{ error: "Cache operation failed", message: e.message }
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def read_cache(key)
|
36
|
+
value = ::Rails.cache.read(key)
|
37
|
+
{ key: key, value: value, exists: !value.nil? }
|
38
|
+
end
|
39
|
+
|
40
|
+
def write_cache(key, value, options = {})
|
41
|
+
success = ::Rails.cache.write(key, value, options)
|
42
|
+
{ key: key, written: success }
|
43
|
+
end
|
44
|
+
|
45
|
+
def delete_cache(key)
|
46
|
+
deleted = ::Rails.cache.delete(key)
|
47
|
+
{ key: key, deleted: deleted }
|
48
|
+
end
|
49
|
+
|
50
|
+
def cache_exists?(key)
|
51
|
+
exists = ::Rails.cache.exist?(key)
|
52
|
+
{ key: key, exists: exists }
|
53
|
+
end
|
54
|
+
|
55
|
+
def fetch_cache(key, options = {})
|
56
|
+
value = ::Rails.cache.fetch(key, options) do
|
57
|
+
yield if block_given?
|
58
|
+
end
|
59
|
+
{ key: key, value: value }
|
60
|
+
end
|
61
|
+
|
62
|
+
def clear_cache
|
63
|
+
::Rails.cache.clear
|
64
|
+
{ cleared: true }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module RcrewAI
|
2
|
+
module Rails
|
3
|
+
module Tools
|
4
|
+
class RailsLoggerTool < RCrewAI::Tools::Base
|
5
|
+
def initialize(tag: "RcrewAI")
|
6
|
+
@tag = tag
|
7
|
+
super(
|
8
|
+
name: "Rails Logger Tool",
|
9
|
+
description: "Log messages to Rails logger"
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute(level, message, metadata = {})
|
14
|
+
logger = ::Rails.logger
|
15
|
+
|
16
|
+
# Format the message with metadata
|
17
|
+
formatted_message = format_message(message, metadata)
|
18
|
+
|
19
|
+
case level.to_sym
|
20
|
+
when :debug
|
21
|
+
logger.debug(formatted_message)
|
22
|
+
when :info
|
23
|
+
logger.info(formatted_message)
|
24
|
+
when :warn
|
25
|
+
logger.warn(formatted_message)
|
26
|
+
when :error
|
27
|
+
logger.error(formatted_message)
|
28
|
+
when :fatal
|
29
|
+
logger.fatal(formatted_message)
|
30
|
+
else
|
31
|
+
return { error: "Unknown log level: #{level}" }
|
32
|
+
end
|
33
|
+
|
34
|
+
# Also instrument for monitoring
|
35
|
+
ActiveSupport::Notifications.instrument("log.rcrewai", {
|
36
|
+
level: level,
|
37
|
+
message: message,
|
38
|
+
metadata: metadata,
|
39
|
+
tag: @tag
|
40
|
+
})
|
41
|
+
|
42
|
+
{ logged: true, level: level, message: message }
|
43
|
+
rescue => e
|
44
|
+
{ error: "Logging failed", message: e.message }
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def format_message(message, metadata)
|
50
|
+
return "[#{@tag}] #{message}" if metadata.empty?
|
51
|
+
|
52
|
+
"[#{@tag}] #{message} | #{metadata.to_json}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|