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.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +222 -0
  4. data/Rakefile +6 -0
  5. data/app/controllers/rcrewai/rails/application_controller.rb +17 -0
  6. data/app/controllers/rcrewai/rails/crews_controller.rb +67 -0
  7. data/app/controllers/rcrewai/rails/executions_controller.rb +65 -0
  8. data/app/jobs/rcrewai/rails/crew_execution_job.rb +82 -0
  9. data/app/jobs/rcrewai/rails/task_execution_job.rb +63 -0
  10. data/app/models/rcrewai/rails/agent.rb +58 -0
  11. data/app/models/rcrewai/rails/application_record.rb +8 -0
  12. data/app/models/rcrewai/rails/crew.rb +65 -0
  13. data/app/models/rcrewai/rails/execution.rb +98 -0
  14. data/app/models/rcrewai/rails/execution_log.rb +18 -0
  15. data/app/models/rcrewai/rails/task.rb +69 -0
  16. data/app/models/rcrewai/rails/task_assignment.rb +12 -0
  17. data/app/models/rcrewai/rails/task_dependency.rb +42 -0
  18. data/app/views/layouts/rcrewai/rails/application.html.erb +37 -0
  19. data/app/views/rcrewai/rails/crews/index.html.erb +42 -0
  20. data/app/views/rcrewai/rails/crews/show.html.erb +95 -0
  21. data/app/views/rcrewai/rails/executions/show.html.erb +92 -0
  22. data/config/routes.rb +50 -0
  23. data/lib/generators/rcrew_a_i/rails/crew/crew_generator.rb +42 -0
  24. data/lib/generators/rcrew_a_i/rails/crew/templates/agent.rb.erb +45 -0
  25. data/lib/generators/rcrew_a_i/rails/crew/templates/crew.rb.erb +72 -0
  26. data/lib/generators/rcrew_a_i/rails/install/install_generator.rb +40 -0
  27. data/lib/generators/rcrew_a_i/rails/install/templates/create_rcrewai_tables.rb +113 -0
  28. data/lib/generators/rcrew_a_i/rails/install/templates/rcrewai.rb +53 -0
  29. data/lib/generators/rcrewai/rails/crew/crew_generator.rb +42 -0
  30. data/lib/generators/rcrewai/rails/crew/templates/agent.rb.erb +45 -0
  31. data/lib/generators/rcrewai/rails/crew/templates/crew.rb.erb +72 -0
  32. data/lib/generators/rcrewai/rails/install/install_generator.rb +40 -0
  33. data/lib/generators/rcrewai/rails/install/templates/create_rcrewai_tables.rb +113 -0
  34. data/lib/generators/rcrewai/rails/install/templates/rcrewai.rb +53 -0
  35. data/lib/rcrewai/rails/agent_builder.rb +123 -0
  36. data/lib/rcrewai/rails/configuration.rb +22 -0
  37. data/lib/rcrewai/rails/crew_builder.rb +112 -0
  38. data/lib/rcrewai/rails/engine.rb +38 -0
  39. data/lib/rcrewai/rails/tools/action_mailer_tool.rb +60 -0
  40. data/lib/rcrewai/rails/tools/active_record_tool.rb +67 -0
  41. data/lib/rcrewai/rails/tools/active_storage_tool.rb +122 -0
  42. data/lib/rcrewai/rails/tools/rails_cache_tool.rb +69 -0
  43. data/lib/rcrewai/rails/tools/rails_logger_tool.rb +57 -0
  44. data/lib/rcrewai/rails/version.rb +5 -0
  45. data/lib/rcrewai/rails.rb +31 -0
  46. data/lib/rcrewai-rails.rb +1 -0
  47. data/rcrewai-rails.gemspec +48 -0
  48. 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
@@ -0,0 +1,5 @@
1
+ module RcrewAI
2
+ module Rails
3
+ VERSION = "0.1.1"
4
+ end
5
+ end