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,98 @@
|
|
1
|
+
module RcrewAI
|
2
|
+
module Rails
|
3
|
+
class Execution < ApplicationRecord
|
4
|
+
self.table_name = "rcrewai_executions"
|
5
|
+
|
6
|
+
belongs_to :crew
|
7
|
+
has_many :execution_logs, dependent: :destroy
|
8
|
+
|
9
|
+
validates :status, inclusion: { in: %w[pending running completed failed cancelled] }
|
10
|
+
|
11
|
+
serialize :inputs, coder: JSON
|
12
|
+
serialize :output, coder: JSON
|
13
|
+
serialize :error_details, coder: JSON
|
14
|
+
|
15
|
+
scope :successful, -> { where(status: "completed") }
|
16
|
+
scope :failed, -> { where(status: "failed") }
|
17
|
+
scope :pending, -> { where(status: "pending") }
|
18
|
+
scope :running, -> { where(status: "running") }
|
19
|
+
scope :recent, -> { order(created_at: :desc) }
|
20
|
+
|
21
|
+
before_create :set_initial_status
|
22
|
+
|
23
|
+
def start!
|
24
|
+
update!(
|
25
|
+
status: "running",
|
26
|
+
started_at: Time.current
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
def complete!(result)
|
31
|
+
update!(
|
32
|
+
status: "completed",
|
33
|
+
output: result,
|
34
|
+
completed_at: Time.current,
|
35
|
+
duration_seconds: calculate_duration
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
def fail!(error)
|
40
|
+
update!(
|
41
|
+
status: "failed",
|
42
|
+
error_message: error.message,
|
43
|
+
error_details: {
|
44
|
+
class: error.class.name,
|
45
|
+
message: error.message,
|
46
|
+
backtrace: error.backtrace&.first(10)
|
47
|
+
},
|
48
|
+
completed_at: Time.current,
|
49
|
+
duration_seconds: calculate_duration
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
def cancel!
|
54
|
+
update!(
|
55
|
+
status: "cancelled",
|
56
|
+
completed_at: Time.current,
|
57
|
+
duration_seconds: calculate_duration
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
def running?
|
62
|
+
status == "running"
|
63
|
+
end
|
64
|
+
|
65
|
+
def completed?
|
66
|
+
status == "completed"
|
67
|
+
end
|
68
|
+
|
69
|
+
def failed?
|
70
|
+
status == "failed"
|
71
|
+
end
|
72
|
+
|
73
|
+
def finished?
|
74
|
+
%w[completed failed cancelled].include?(status)
|
75
|
+
end
|
76
|
+
|
77
|
+
def log(level, message, details = {})
|
78
|
+
execution_logs.create!(
|
79
|
+
level: level,
|
80
|
+
message: message,
|
81
|
+
details: details,
|
82
|
+
timestamp: Time.current
|
83
|
+
)
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def set_initial_status
|
89
|
+
self.status ||= "pending"
|
90
|
+
end
|
91
|
+
|
92
|
+
def calculate_duration
|
93
|
+
return nil unless started_at.present?
|
94
|
+
(Time.current - started_at).to_i
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module RcrewAI
|
2
|
+
module Rails
|
3
|
+
class ExecutionLog < ApplicationRecord
|
4
|
+
self.table_name = "rcrewai_execution_logs"
|
5
|
+
|
6
|
+
belongs_to :execution
|
7
|
+
|
8
|
+
validates :level, inclusion: { in: %w[debug info warn error] }
|
9
|
+
validates :message, presence: true
|
10
|
+
|
11
|
+
serialize :details, coder: JSON
|
12
|
+
|
13
|
+
scope :errors, -> { where(level: "error") }
|
14
|
+
scope :warnings, -> { where(level: "warn") }
|
15
|
+
scope :recent, -> { order(timestamp: :desc) }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module RcrewAI
|
2
|
+
module Rails
|
3
|
+
class Task < ApplicationRecord
|
4
|
+
self.table_name = "rcrewai_tasks"
|
5
|
+
|
6
|
+
belongs_to :crew
|
7
|
+
has_many :task_assignments, dependent: :destroy
|
8
|
+
has_many :agents, through: :task_assignments
|
9
|
+
has_many :task_dependencies, foreign_key: :task_id, dependent: :destroy
|
10
|
+
has_many :dependencies, through: :task_dependencies, source: :dependency
|
11
|
+
|
12
|
+
validates :description, presence: true
|
13
|
+
validates :expected_output, presence: true
|
14
|
+
|
15
|
+
serialize :context, coder: JSON, type: Array
|
16
|
+
serialize :output_json, coder: JSON
|
17
|
+
serialize :output_pydantic, coder: JSON
|
18
|
+
serialize :tools, coder: JSON, type: Array
|
19
|
+
|
20
|
+
scope :ordered, -> { order(position: :asc) }
|
21
|
+
|
22
|
+
def to_rcrew_task
|
23
|
+
RCrewAI::Task.new(
|
24
|
+
description: description,
|
25
|
+
expected_output: expected_output,
|
26
|
+
agent: agent&.to_rcrew_agent,
|
27
|
+
context: context,
|
28
|
+
async_execution: async_execution,
|
29
|
+
output_json: output_json,
|
30
|
+
output_pydantic: output_pydantic,
|
31
|
+
output_file: output_file,
|
32
|
+
tools: instantiated_tools,
|
33
|
+
callback: callback_method
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def agent
|
38
|
+
agents.first
|
39
|
+
end
|
40
|
+
|
41
|
+
def instantiated_tools
|
42
|
+
return [] if tools.blank?
|
43
|
+
|
44
|
+
tools.map do |tool_config|
|
45
|
+
tool_class = tool_config["class"].constantize
|
46
|
+
tool_params = tool_config["params"] || {}
|
47
|
+
tool_class.new(**tool_params.symbolize_keys)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def add_dependency(other_task)
|
52
|
+
task_dependencies.create!(dependency: other_task)
|
53
|
+
end
|
54
|
+
|
55
|
+
def remove_dependency(other_task)
|
56
|
+
task_dependencies.where(dependency: other_task).destroy_all
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def callback_method
|
62
|
+
return nil unless callback_class.present? && callback_method_name.present?
|
63
|
+
|
64
|
+
klass = callback_class.constantize
|
65
|
+
->(output) { klass.new.send(callback_method_name, output) }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module RcrewAI
|
2
|
+
module Rails
|
3
|
+
class TaskDependency < ApplicationRecord
|
4
|
+
self.table_name = "rcrewai_task_dependencies"
|
5
|
+
|
6
|
+
belongs_to :task
|
7
|
+
belongs_to :dependency, class_name: "Task"
|
8
|
+
|
9
|
+
validates :task_id, uniqueness: { scope: :dependency_id }
|
10
|
+
validate :no_circular_dependencies
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def no_circular_dependencies
|
15
|
+
return unless dependency
|
16
|
+
|
17
|
+
if creates_circular_dependency?
|
18
|
+
errors.add(:dependency, "would create a circular dependency")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def creates_circular_dependency?
|
23
|
+
visited = Set.new
|
24
|
+
queue = [dependency_id]
|
25
|
+
|
26
|
+
while queue.any?
|
27
|
+
current_id = queue.shift
|
28
|
+
return true if current_id == task_id
|
29
|
+
|
30
|
+
next if visited.include?(current_id)
|
31
|
+
visited.add(current_id)
|
32
|
+
|
33
|
+
TaskDependency.where(task_id: current_id).pluck(:dependency_id).each do |dep_id|
|
34
|
+
queue << dep_id
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>RcrewAI Rails</title>
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
6
|
+
<%= csrf_meta_tags %>
|
7
|
+
<%= csp_meta_tag %>
|
8
|
+
|
9
|
+
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
|
10
|
+
<%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>
|
11
|
+
</head>
|
12
|
+
|
13
|
+
<body>
|
14
|
+
<nav class="navbar">
|
15
|
+
<div class="container">
|
16
|
+
<%= link_to "RcrewAI", root_path, class: "navbar-brand" %>
|
17
|
+
<div class="navbar-menu">
|
18
|
+
<%= link_to "Crews", crews_path, class: "navbar-item" %>
|
19
|
+
<%= link_to "Executions", executions_path, class: "navbar-item" %>
|
20
|
+
<%= link_to "Agents", agents_path, class: "navbar-item" %>
|
21
|
+
<%= link_to "Tasks", tasks_path, class: "navbar-item" %>
|
22
|
+
</div>
|
23
|
+
</div>
|
24
|
+
</nav>
|
25
|
+
|
26
|
+
<main class="container">
|
27
|
+
<% if notice.present? %>
|
28
|
+
<div class="notice"><%= notice %></div>
|
29
|
+
<% end %>
|
30
|
+
<% if alert.present? %>
|
31
|
+
<div class="alert"><%= alert %></div>
|
32
|
+
<% end %>
|
33
|
+
|
34
|
+
<%= yield %>
|
35
|
+
</main>
|
36
|
+
</body>
|
37
|
+
</html>
|
@@ -0,0 +1,42 @@
|
|
1
|
+
<div class="page-header">
|
2
|
+
<h1>Crews</h1>
|
3
|
+
<%= link_to "New Crew", new_crew_path, class: "btn btn-primary" %>
|
4
|
+
</div>
|
5
|
+
|
6
|
+
<div class="crews-grid">
|
7
|
+
<% @crews.each do |crew| %>
|
8
|
+
<div class="crew-card">
|
9
|
+
<h3><%= link_to crew.name, crew %></h3>
|
10
|
+
<p class="crew-description"><%= crew.description %></p>
|
11
|
+
|
12
|
+
<div class="crew-meta">
|
13
|
+
<span class="badge"><%= crew.process_type %></span>
|
14
|
+
<span class="badge"><%= crew.agents.count %> agents</span>
|
15
|
+
<span class="badge"><%= crew.tasks.count %> tasks</span>
|
16
|
+
</div>
|
17
|
+
|
18
|
+
<div class="crew-stats">
|
19
|
+
<% stats = crew.execution_stats %>
|
20
|
+
<div class="stat">
|
21
|
+
<strong>Total Runs:</strong> <%= stats[:total] %>
|
22
|
+
</div>
|
23
|
+
<div class="stat">
|
24
|
+
<strong>Success Rate:</strong>
|
25
|
+
<%= stats[:total] > 0 ? "#{(stats[:successful].to_f / stats[:total] * 100).round}%" : "N/A" %>
|
26
|
+
</div>
|
27
|
+
<div class="stat">
|
28
|
+
<strong>Avg Duration:</strong>
|
29
|
+
<%= stats[:average_duration] ? "#{stats[:average_duration].round}s" : "N/A" %>
|
30
|
+
</div>
|
31
|
+
</div>
|
32
|
+
|
33
|
+
<div class="crew-actions">
|
34
|
+
<%= link_to "View", crew, class: "btn btn-sm" %>
|
35
|
+
<%= link_to "Edit", edit_crew_path(crew), class: "btn btn-sm" %>
|
36
|
+
<%= button_to "Execute", execute_crew_path(crew), method: :post, class: "btn btn-sm btn-success" %>
|
37
|
+
</div>
|
38
|
+
</div>
|
39
|
+
<% end %>
|
40
|
+
</div>
|
41
|
+
|
42
|
+
<%= paginate @crews %>
|
@@ -0,0 +1,95 @@
|
|
1
|
+
<div class="page-header">
|
2
|
+
<h1><%= @crew.name %></h1>
|
3
|
+
<div class="actions">
|
4
|
+
<%= link_to "Edit", edit_crew_path(@crew), class: "btn" %>
|
5
|
+
<%= button_to "Execute", execute_crew_path(@crew), method: :post, class: "btn btn-success" %>
|
6
|
+
<%= button_to "Delete", @crew, method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-danger" %>
|
7
|
+
</div>
|
8
|
+
</div>
|
9
|
+
|
10
|
+
<div class="crew-details">
|
11
|
+
<p><%= @crew.description %></p>
|
12
|
+
|
13
|
+
<div class="metadata">
|
14
|
+
<div class="meta-item">
|
15
|
+
<strong>Process Type:</strong> <%= @crew.process_type %>
|
16
|
+
</div>
|
17
|
+
<div class="meta-item">
|
18
|
+
<strong>Memory:</strong> <%= @crew.memory_enabled ? "Enabled" : "Disabled" %>
|
19
|
+
</div>
|
20
|
+
<div class="meta-item">
|
21
|
+
<strong>Cache:</strong> <%= @crew.cache_enabled ? "Enabled" : "Disabled" %>
|
22
|
+
</div>
|
23
|
+
<div class="meta-item">
|
24
|
+
<strong>Status:</strong> <%= @crew.active ? "Active" : "Inactive" %>
|
25
|
+
</div>
|
26
|
+
</div>
|
27
|
+
</div>
|
28
|
+
|
29
|
+
<div class="section">
|
30
|
+
<h2>Agents (<%= @agents.count %>)</h2>
|
31
|
+
<div class="agents-list">
|
32
|
+
<% @agents.each do |agent| %>
|
33
|
+
<div class="agent-item">
|
34
|
+
<h4><%= agent.name %></h4>
|
35
|
+
<p><strong>Role:</strong> <%= agent.role %></p>
|
36
|
+
<p><strong>Goal:</strong> <%= agent.goal %></p>
|
37
|
+
<% if agent.tools.present? %>
|
38
|
+
<p><strong>Tools:</strong> <%= agent.tools.count %> configured</p>
|
39
|
+
<% end %>
|
40
|
+
</div>
|
41
|
+
<% end %>
|
42
|
+
</div>
|
43
|
+
</div>
|
44
|
+
|
45
|
+
<div class="section">
|
46
|
+
<h2>Tasks (<%= @tasks.count %>)</h2>
|
47
|
+
<div class="tasks-list">
|
48
|
+
<% @tasks.each do |task| %>
|
49
|
+
<div class="task-item">
|
50
|
+
<h4>Task #<%= task.position || task.id %></h4>
|
51
|
+
<p><%= task.description %></p>
|
52
|
+
<p><strong>Expected Output:</strong> <%= task.expected_output %></p>
|
53
|
+
<% if task.agents.any? %>
|
54
|
+
<p><strong>Assigned to:</strong> <%= task.agents.map(&:name).join(", ") %></p>
|
55
|
+
<% end %>
|
56
|
+
</div>
|
57
|
+
<% end %>
|
58
|
+
</div>
|
59
|
+
</div>
|
60
|
+
|
61
|
+
<div class="section">
|
62
|
+
<h2>Recent Executions</h2>
|
63
|
+
<% if @recent_executions.any? %>
|
64
|
+
<table class="table">
|
65
|
+
<thead>
|
66
|
+
<tr>
|
67
|
+
<th>ID</th>
|
68
|
+
<th>Status</th>
|
69
|
+
<th>Started</th>
|
70
|
+
<th>Duration</th>
|
71
|
+
<th>Actions</th>
|
72
|
+
</tr>
|
73
|
+
</thead>
|
74
|
+
<tbody>
|
75
|
+
<% @recent_executions.each do |execution| %>
|
76
|
+
<tr>
|
77
|
+
<td><%= link_to "##{execution.id}", execution %></td>
|
78
|
+
<td>
|
79
|
+
<span class="badge badge-<%= execution.status %>">
|
80
|
+
<%= execution.status %>
|
81
|
+
</span>
|
82
|
+
</td>
|
83
|
+
<td><%= execution.started_at&.strftime("%Y-%m-%d %H:%M") %></td>
|
84
|
+
<td><%= execution.duration_seconds ? "#{execution.duration_seconds}s" : "-" %></td>
|
85
|
+
<td>
|
86
|
+
<%= link_to "View", execution, class: "btn btn-sm" %>
|
87
|
+
</td>
|
88
|
+
</tr>
|
89
|
+
<% end %>
|
90
|
+
</tbody>
|
91
|
+
</table>
|
92
|
+
<% else %>
|
93
|
+
<p>No executions yet.</p>
|
94
|
+
<% end %>
|
95
|
+
</div>
|
@@ -0,0 +1,92 @@
|
|
1
|
+
<div class="page-header">
|
2
|
+
<h1>Execution #<%= @execution.id %></h1>
|
3
|
+
<div class="actions">
|
4
|
+
<% if @execution.running? %>
|
5
|
+
<%= button_to "Cancel", cancel_execution_path(@execution), method: :post, class: "btn btn-danger" %>
|
6
|
+
<% end %>
|
7
|
+
<%= link_to "Back to Crew", @execution.crew, class: "btn" %>
|
8
|
+
</div>
|
9
|
+
</div>
|
10
|
+
|
11
|
+
<div class="execution-details">
|
12
|
+
<div class="status-banner status-<%= @execution.status %>">
|
13
|
+
<strong>Status:</strong> <%= @execution.status.humanize %>
|
14
|
+
</div>
|
15
|
+
|
16
|
+
<div class="metadata-grid">
|
17
|
+
<div class="meta-item">
|
18
|
+
<strong>Crew:</strong> <%= link_to @execution.crew.name, @execution.crew %>
|
19
|
+
</div>
|
20
|
+
<div class="meta-item">
|
21
|
+
<strong>Started:</strong> <%= @execution.started_at&.strftime("%Y-%m-%d %H:%M:%S") || "Not started" %>
|
22
|
+
</div>
|
23
|
+
<div class="meta-item">
|
24
|
+
<strong>Completed:</strong> <%= @execution.completed_at&.strftime("%Y-%m-%d %H:%M:%S") || "In progress" %>
|
25
|
+
</div>
|
26
|
+
<div class="meta-item">
|
27
|
+
<strong>Duration:</strong> <%= @execution.duration_seconds ? "#{@execution.duration_seconds} seconds" : "N/A" %>
|
28
|
+
</div>
|
29
|
+
</div>
|
30
|
+
|
31
|
+
<% if @execution.inputs.present? %>
|
32
|
+
<div class="section">
|
33
|
+
<h3>Inputs</h3>
|
34
|
+
<pre class="code-block"><%= JSON.pretty_generate(@execution.inputs) %></pre>
|
35
|
+
</div>
|
36
|
+
<% end %>
|
37
|
+
|
38
|
+
<% if @execution.output.present? %>
|
39
|
+
<div class="section">
|
40
|
+
<h3>Output</h3>
|
41
|
+
<pre class="code-block"><%= JSON.pretty_generate(@execution.output) %></pre>
|
42
|
+
</div>
|
43
|
+
<% end %>
|
44
|
+
|
45
|
+
<% if @execution.error_message.present? %>
|
46
|
+
<div class="section error-section">
|
47
|
+
<h3>Error</h3>
|
48
|
+
<p class="error-message"><%= @execution.error_message %></p>
|
49
|
+
<% if @execution.error_details.present? %>
|
50
|
+
<details>
|
51
|
+
<summary>Error Details</summary>
|
52
|
+
<pre class="code-block"><%= JSON.pretty_generate(@execution.error_details) %></pre>
|
53
|
+
</details>
|
54
|
+
<% end %>
|
55
|
+
</div>
|
56
|
+
<% end %>
|
57
|
+
</div>
|
58
|
+
|
59
|
+
<div class="section">
|
60
|
+
<h2>Execution Logs</h2>
|
61
|
+
|
62
|
+
<div class="log-filters">
|
63
|
+
<%= link_to "All", execution_logs_path(@execution), class: "filter-btn", data: { turbo_frame: "logs" } %>
|
64
|
+
<%= link_to "Errors", execution_logs_path(@execution, level: "error"), class: "filter-btn", data: { turbo_frame: "logs" } %>
|
65
|
+
<%= link_to "Warnings", execution_logs_path(@execution, level: "warn"), class: "filter-btn", data: { turbo_frame: "logs" } %>
|
66
|
+
<%= link_to "Info", execution_logs_path(@execution, level: "info"), class: "filter-btn", data: { turbo_frame: "logs" } %>
|
67
|
+
</div>
|
68
|
+
|
69
|
+
<%= turbo_frame_tag "logs", data: { turbo_action: "advance" } do %>
|
70
|
+
<div class="logs-container">
|
71
|
+
<% @logs.each do |log| %>
|
72
|
+
<div class="log-entry log-<%= log.level %>">
|
73
|
+
<span class="log-timestamp"><%= log.timestamp.strftime("%H:%M:%S.%L") %></span>
|
74
|
+
<span class="log-level"><%= log.level.upcase %></span>
|
75
|
+
<span class="log-message"><%= log.message %></span>
|
76
|
+
<% if log.details.present? %>
|
77
|
+
<details class="log-details">
|
78
|
+
<summary>Details</summary>
|
79
|
+
<pre><%= JSON.pretty_generate(log.details) %></pre>
|
80
|
+
</details>
|
81
|
+
<% end %>
|
82
|
+
</div>
|
83
|
+
<% end %>
|
84
|
+
</div>
|
85
|
+
|
86
|
+
<%= paginate @logs %>
|
87
|
+
<% end %>
|
88
|
+
</div>
|
89
|
+
|
90
|
+
<% if @execution.running? %>
|
91
|
+
<%= turbo_stream_from @execution, :logs %>
|
92
|
+
<% end %>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
RcrewAI::Rails::Engine.routes.draw do
|
2
|
+
root to: "crews#index"
|
3
|
+
|
4
|
+
resources :crews do
|
5
|
+
member do
|
6
|
+
post :execute
|
7
|
+
end
|
8
|
+
resources :agents
|
9
|
+
resources :tasks
|
10
|
+
end
|
11
|
+
|
12
|
+
resources :executions, only: [:index, :show] do
|
13
|
+
member do
|
14
|
+
post :cancel
|
15
|
+
get :logs
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
resources :agents do
|
20
|
+
resources :tools
|
21
|
+
end
|
22
|
+
|
23
|
+
resources :tasks do
|
24
|
+
member do
|
25
|
+
post :add_dependency
|
26
|
+
delete :remove_dependency
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# API endpoints
|
31
|
+
namespace :api do
|
32
|
+
namespace :v1 do
|
33
|
+
resources :crews, only: [:index, :show, :create] do
|
34
|
+
member do
|
35
|
+
post :execute
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
resources :executions, only: [:index, :show] do
|
40
|
+
member do
|
41
|
+
get :status
|
42
|
+
get :logs
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Mount Turbo Streams for real-time updates
|
49
|
+
mount ActionCable.server => '/cable' if defined?(ActionCable)
|
50
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "rails/generators"
|
2
|
+
|
3
|
+
module RcrewAI
|
4
|
+
module Rails
|
5
|
+
module Generators
|
6
|
+
class CrewGenerator < ::Rails::Generators::NamedBase
|
7
|
+
source_root File.expand_path("templates", __dir__)
|
8
|
+
|
9
|
+
argument :process_type, type: :string, default: "sequential", banner: "sequential|hierarchical"
|
10
|
+
|
11
|
+
class_option :agents, type: :array, default: [], desc: "List of agents to create"
|
12
|
+
class_option :description, type: :string, desc: "Crew description"
|
13
|
+
|
14
|
+
def create_crew_file
|
15
|
+
template "crew.rb.erb", "app/crews/#{file_name}_crew.rb"
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_agent_files
|
19
|
+
options[:agents].each do |agent_name|
|
20
|
+
@agent_name = agent_name
|
21
|
+
template "agent.rb.erb", "app/crews/agents/#{agent_name.underscore}_agent.rb"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def display_next_steps
|
26
|
+
say "\n✅ Created #{class_name}Crew!", :green
|
27
|
+
say "\nNext steps:", :yellow
|
28
|
+
say " 1. Configure your crew in app/crews/#{file_name}_crew.rb"
|
29
|
+
say " 2. Define tasks for your crew"
|
30
|
+
say " 3. Run your crew with: #{class_name}Crew.new.execute"
|
31
|
+
say "\n"
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def crew_description
|
37
|
+
options[:description] || "#{class_name} crew for AI task orchestration"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class <%= @agent_name.camelize %>Agent
|
2
|
+
include RcrewAI::Rails::AgentBuilder
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@agent = RcrewAI::Agent.new(
|
6
|
+
role: role,
|
7
|
+
goal: goal,
|
8
|
+
backstory: backstory,
|
9
|
+
memory: true,
|
10
|
+
verbose: true,
|
11
|
+
allow_delegation: false,
|
12
|
+
tools: tools,
|
13
|
+
max_iter: 25,
|
14
|
+
max_rpm: 10
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
def role
|
19
|
+
"<%= @agent_name.humanize %> Specialist"
|
20
|
+
end
|
21
|
+
|
22
|
+
def goal
|
23
|
+
"Perform <%= @agent_name.humanize.downcase %> tasks efficiently and accurately"
|
24
|
+
end
|
25
|
+
|
26
|
+
def backstory
|
27
|
+
<<~BACKSTORY
|
28
|
+
You are a highly skilled <%= @agent_name.humanize.downcase %> specialist with extensive experience.
|
29
|
+
Your expertise allows you to handle complex <%= @agent_name.humanize.downcase %> challenges effectively.
|
30
|
+
BACKSTORY
|
31
|
+
end
|
32
|
+
|
33
|
+
def tools
|
34
|
+
[
|
35
|
+
# Add your tools here
|
36
|
+
# RcrewAI::Tools::WebSearch.new,
|
37
|
+
# RcrewAI::Tools::FileReader.new,
|
38
|
+
# CustomTool.new
|
39
|
+
]
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_agent
|
43
|
+
@agent
|
44
|
+
end
|
45
|
+
end
|