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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d07d3c8b88c4bf9326dc8fc588cc58d0b02112d8c92d4d6eccc25a3da98d58a2
|
4
|
+
data.tar.gz: 1ce7c0563541bc473d6d1d3a092063d0ec86d7617d314fe733e444d37f761bfc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 267b55d87d5ef72a12560a689459cc82cc93e99e55200b2c6128e492d4736f3911717c07b8616cb623baf8c595398084d9f57c0375e95deaf220b6f695c8c50a
|
7
|
+
data.tar.gz: 899ce8199ad7727e3bca13bf07362cf5184ba3ccc1431e35bd2564c57e91597c67db9744d9286c1caa115c9bd54cb2d7fb27fc29859d0b044d6bcd2fd6d6b034
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2024 RcrewAI Rails Contributors
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,222 @@
|
|
1
|
+
# RcrewAI Rails
|
2
|
+
|
3
|
+
Rails engine for integrating RcrewAI into your Rails applications. Provides ActiveRecord persistence, background job integration, generators, and a web UI for managing AI crews and agents.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- **ActiveRecord Integration**: Persist crews, agents, tasks, and executions in your database
|
8
|
+
- **Background Job Support**: Works with any ActiveJob adapter (Sidekiq, Resque, Delayed Job, etc.)
|
9
|
+
- **Rails Generators**: Quickly scaffold new crews and agents
|
10
|
+
- **Web UI**: Monitor and manage crews through a built-in interface
|
11
|
+
- **Rails-Specific Tools**: Pre-built tools for ActiveRecord, ActionMailer, Rails cache, and more
|
12
|
+
- **Configuration**: Flexible configuration through Rails initializers
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
Add this line to your application's Gemfile:
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
gem 'rcrewai-rails'
|
20
|
+
```
|
21
|
+
|
22
|
+
And then execute:
|
23
|
+
|
24
|
+
```bash
|
25
|
+
$ bundle install
|
26
|
+
```
|
27
|
+
|
28
|
+
Run the installation generator:
|
29
|
+
|
30
|
+
```bash
|
31
|
+
$ rails generate rcrewai:rails:install
|
32
|
+
$ rails db:migrate
|
33
|
+
```
|
34
|
+
|
35
|
+
This will:
|
36
|
+
- Create the necessary database migrations
|
37
|
+
- Add an initializer file for configuration
|
38
|
+
- Mount the engine routes
|
39
|
+
|
40
|
+
## Configuration
|
41
|
+
|
42
|
+
Configure RcrewAI Rails in `config/initializers/rcrewai.rb`:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
RcrewAI::Rails.configure do |config|
|
46
|
+
# ActiveJob queue for background processing
|
47
|
+
config.job_queue_name = "default"
|
48
|
+
|
49
|
+
# Enable/disable web UI
|
50
|
+
config.enable_web_ui = true
|
51
|
+
|
52
|
+
# Use async execution by default
|
53
|
+
config.async_execution = true
|
54
|
+
|
55
|
+
# Default LLM settings
|
56
|
+
config.default_llm_provider = "openai"
|
57
|
+
config.default_llm_model = "gpt-4"
|
58
|
+
|
59
|
+
# Logging
|
60
|
+
config.enable_logging = true
|
61
|
+
config.log_level = :info
|
62
|
+
end
|
63
|
+
|
64
|
+
# Configure the base RcrewAI gem
|
65
|
+
RcrewAI.configure do |config|
|
66
|
+
config.openai_api_key = ENV["OPENAI_API_KEY"]
|
67
|
+
# Add other LLM provider keys as needed
|
68
|
+
end
|
69
|
+
```
|
70
|
+
|
71
|
+
## Usage
|
72
|
+
|
73
|
+
### Creating a Crew with Generators
|
74
|
+
|
75
|
+
Generate a new crew with agents:
|
76
|
+
|
77
|
+
```bash
|
78
|
+
$ rails generate rcrewai:rails:crew research_team sequential \
|
79
|
+
--agents researcher analyst writer \
|
80
|
+
--description "Research team for market analysis"
|
81
|
+
```
|
82
|
+
|
83
|
+
This creates a crew class in `app/crews/research_team_crew.rb`.
|
84
|
+
|
85
|
+
### Creating a Crew Programmatically
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
class ResearchCrew
|
89
|
+
include RcrewAI::Rails::CrewBuilder
|
90
|
+
|
91
|
+
crew_name "research_team"
|
92
|
+
crew_description "AI-powered research team"
|
93
|
+
process_type :sequential
|
94
|
+
memory_enabled true
|
95
|
+
|
96
|
+
def setup_agents
|
97
|
+
@researcher = create_agent("researcher",
|
98
|
+
role: "Senior Research Analyst",
|
99
|
+
goal: "Uncover insights and trends",
|
100
|
+
backstory: "Expert researcher with years of experience"
|
101
|
+
)
|
102
|
+
|
103
|
+
@writer = create_agent("writer",
|
104
|
+
role: "Content Writer",
|
105
|
+
goal: "Create compelling reports",
|
106
|
+
backstory: "Skilled writer specializing in technical content"
|
107
|
+
)
|
108
|
+
end
|
109
|
+
|
110
|
+
def setup_tasks
|
111
|
+
@research_task = create_task("Research latest AI trends",
|
112
|
+
expected_output: "Comprehensive research report",
|
113
|
+
position: 1
|
114
|
+
)
|
115
|
+
assign_agent_to_task(@researcher, @research_task)
|
116
|
+
|
117
|
+
@writing_task = create_task("Write executive summary",
|
118
|
+
expected_output: "2-page executive summary",
|
119
|
+
position: 2
|
120
|
+
)
|
121
|
+
assign_agent_to_task(@writer, @writing_task)
|
122
|
+
add_task_dependency(@writing_task, @research_task)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Execute the crew
|
127
|
+
crew = ResearchCrew.new
|
128
|
+
execution = crew.execute(topic: "AI in Healthcare")
|
129
|
+
```
|
130
|
+
|
131
|
+
### Using Rails-Specific Tools
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
class DataAnalystAgent
|
135
|
+
include RcrewAI::Rails::AgentBuilder
|
136
|
+
|
137
|
+
agent_role "Data Analyst"
|
138
|
+
agent_goal "Analyze application data"
|
139
|
+
|
140
|
+
tools [
|
141
|
+
RcrewAI::Rails::Tools::ActiveRecordTool.new(
|
142
|
+
model_class: User,
|
143
|
+
allowed_methods: [:count, :where, :pluck]
|
144
|
+
),
|
145
|
+
RcrewAI::Rails::Tools::RailsCacheTool.new,
|
146
|
+
RcrewAI::Rails::Tools::ActionMailerTool.new(
|
147
|
+
mailer_class: ReportMailer,
|
148
|
+
allowed_methods: [:send_report]
|
149
|
+
)
|
150
|
+
]
|
151
|
+
end
|
152
|
+
```
|
153
|
+
|
154
|
+
### Monitoring Executions
|
155
|
+
|
156
|
+
Access the web UI at `/rcrewai` to:
|
157
|
+
- View all crews and their configurations
|
158
|
+
- Monitor execution status and logs
|
159
|
+
- Start new executions
|
160
|
+
- View execution history and results
|
161
|
+
|
162
|
+
### Using with ActiveJob
|
163
|
+
|
164
|
+
Executions run through ActiveJob by default, using whatever adapter your Rails app is configured with:
|
165
|
+
|
166
|
+
```ruby
|
167
|
+
# Async execution (default)
|
168
|
+
crew.execute_async(inputs)
|
169
|
+
|
170
|
+
# Sync execution
|
171
|
+
crew.execute_sync(inputs)
|
172
|
+
|
173
|
+
# Custom job options
|
174
|
+
CrewExecutionJob.set(wait: 5.minutes).perform_later(crew, inputs)
|
175
|
+
```
|
176
|
+
|
177
|
+
## Database Models
|
178
|
+
|
179
|
+
The gem provides these ActiveRecord models:
|
180
|
+
|
181
|
+
- `RcrewAI::Rails::Crew` - Crew configurations
|
182
|
+
- `RcrewAI::Rails::Agent` - Agent definitions
|
183
|
+
- `RcrewAI::Rails::Task` - Task definitions
|
184
|
+
- `RcrewAI::Rails::Execution` - Execution history
|
185
|
+
- `RcrewAI::Rails::ExecutionLog` - Detailed execution logs
|
186
|
+
|
187
|
+
## API Endpoints
|
188
|
+
|
189
|
+
The engine provides JSON API endpoints:
|
190
|
+
|
191
|
+
```
|
192
|
+
GET /rcrewai/api/v1/crews
|
193
|
+
GET /rcrewai/api/v1/crews/:id
|
194
|
+
POST /rcrewai/api/v1/crews/:id/execute
|
195
|
+
GET /rcrewai/api/v1/executions
|
196
|
+
GET /rcrewai/api/v1/executions/:id
|
197
|
+
GET /rcrewai/api/v1/executions/:id/status
|
198
|
+
GET /rcrewai/api/v1/executions/:id/logs
|
199
|
+
```
|
200
|
+
|
201
|
+
## Development
|
202
|
+
|
203
|
+
After checking out the repo, run:
|
204
|
+
|
205
|
+
```bash
|
206
|
+
$ bundle install
|
207
|
+
$ bundle exec rspec
|
208
|
+
```
|
209
|
+
|
210
|
+
To install this gem onto your local machine:
|
211
|
+
|
212
|
+
```bash
|
213
|
+
$ bundle exec rake install
|
214
|
+
```
|
215
|
+
|
216
|
+
## Contributing
|
217
|
+
|
218
|
+
Bug reports and pull requests are welcome on GitHub.
|
219
|
+
|
220
|
+
## License
|
221
|
+
|
222
|
+
The gem is available as open source under the terms of the MIT License.
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module RcrewAI
|
2
|
+
module Rails
|
3
|
+
class ApplicationController < ActionController::Base
|
4
|
+
protect_from_forgery with: :exception
|
5
|
+
|
6
|
+
before_action :check_web_ui_enabled
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def check_web_ui_enabled
|
11
|
+
unless RcrewAI::Rails.config.enable_web_ui
|
12
|
+
render plain: "RcrewAI Web UI is disabled", status: :forbidden
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module RcrewAI
|
2
|
+
module Rails
|
3
|
+
class CrewsController < ApplicationController
|
4
|
+
before_action :set_crew, only: [:show, :edit, :update, :destroy, :execute]
|
5
|
+
|
6
|
+
def index
|
7
|
+
@crews = Crew.includes(:agents, :tasks).page(params[:page])
|
8
|
+
end
|
9
|
+
|
10
|
+
def show
|
11
|
+
@recent_executions = @crew.executions.recent.limit(10)
|
12
|
+
@agents = @crew.agents
|
13
|
+
@tasks = @crew.tasks.ordered
|
14
|
+
end
|
15
|
+
|
16
|
+
def new
|
17
|
+
@crew = Crew.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def create
|
21
|
+
@crew = Crew.new(crew_params)
|
22
|
+
|
23
|
+
if @crew.save
|
24
|
+
redirect_to @crew, notice: 'Crew was successfully created.'
|
25
|
+
else
|
26
|
+
render :new
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def edit
|
31
|
+
end
|
32
|
+
|
33
|
+
def update
|
34
|
+
if @crew.update(crew_params)
|
35
|
+
redirect_to @crew, notice: 'Crew was successfully updated.'
|
36
|
+
else
|
37
|
+
render :edit
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def destroy
|
42
|
+
@crew.destroy
|
43
|
+
redirect_to crews_url, notice: 'Crew was successfully destroyed.'
|
44
|
+
end
|
45
|
+
|
46
|
+
def execute
|
47
|
+
inputs = params[:inputs] || {}
|
48
|
+
execution = @crew.execute_async(inputs)
|
49
|
+
|
50
|
+
redirect_to execution_path(execution), notice: 'Crew execution started.'
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def set_crew
|
56
|
+
@crew = Crew.find(params[:id])
|
57
|
+
end
|
58
|
+
|
59
|
+
def crew_params
|
60
|
+
params.require(:crew).permit(
|
61
|
+
:name, :description, :process_type, :verbose,
|
62
|
+
:memory_enabled, :cache_enabled, :max_rpm, :manager_llm, :active
|
63
|
+
)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module RcrewAI
|
2
|
+
module Rails
|
3
|
+
class ExecutionsController < ApplicationController
|
4
|
+
before_action :set_execution, only: [:show, :cancel, :logs]
|
5
|
+
|
6
|
+
def index
|
7
|
+
@executions = Execution.includes(:crew)
|
8
|
+
@executions = @executions.where(crew_id: params[:crew_id]) if params[:crew_id]
|
9
|
+
@executions = @executions.where(status: params[:status]) if params[:status]
|
10
|
+
@executions = @executions.recent.page(params[:page])
|
11
|
+
end
|
12
|
+
|
13
|
+
def show
|
14
|
+
@logs = @execution.execution_logs.recent.page(params[:page])
|
15
|
+
|
16
|
+
respond_to do |format|
|
17
|
+
format.html
|
18
|
+
format.json { render json: execution_json }
|
19
|
+
format.turbo_stream if request.headers["Turbo-Frame"]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def cancel
|
24
|
+
if @execution.running?
|
25
|
+
@execution.cancel!
|
26
|
+
redirect_to @execution, notice: 'Execution was cancelled.'
|
27
|
+
else
|
28
|
+
redirect_to @execution, alert: 'Execution cannot be cancelled.'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def logs
|
33
|
+
@logs = @execution.execution_logs
|
34
|
+
@logs = @logs.where(level: params[:level]) if params[:level]
|
35
|
+
@logs = @logs.recent.limit(params[:limit] || 100)
|
36
|
+
|
37
|
+
respond_to do |format|
|
38
|
+
format.json { render json: @logs }
|
39
|
+
format.turbo_stream
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def set_execution
|
46
|
+
@execution = Execution.find(params[:id])
|
47
|
+
end
|
48
|
+
|
49
|
+
def execution_json
|
50
|
+
{
|
51
|
+
id: @execution.id,
|
52
|
+
crew_name: @execution.crew.name,
|
53
|
+
status: @execution.status,
|
54
|
+
started_at: @execution.started_at,
|
55
|
+
completed_at: @execution.completed_at,
|
56
|
+
duration_seconds: @execution.duration_seconds,
|
57
|
+
inputs: @execution.inputs,
|
58
|
+
output: @execution.output,
|
59
|
+
error_message: @execution.error_message,
|
60
|
+
logs_count: @execution.execution_logs.count
|
61
|
+
}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module RcrewAI
|
2
|
+
module Rails
|
3
|
+
class CrewExecutionJob < ActiveJob::Base
|
4
|
+
queue_as { RcrewAI::Rails.config.job_queue_name }
|
5
|
+
|
6
|
+
retry_on StandardError, wait: :exponentially_longer, attempts: 3
|
7
|
+
|
8
|
+
def perform(crew, inputs = {})
|
9
|
+
execution = crew.executions.create!(
|
10
|
+
status: "pending",
|
11
|
+
inputs: inputs
|
12
|
+
)
|
13
|
+
|
14
|
+
begin
|
15
|
+
execution.start!
|
16
|
+
execution.log("info", "Starting crew execution", { crew_id: crew.id })
|
17
|
+
|
18
|
+
# Convert Rails models to RcrewAI objects
|
19
|
+
rcrew = crew.to_rcrew
|
20
|
+
|
21
|
+
# Execute the crew with logging
|
22
|
+
result = execute_with_logging(rcrew, inputs, execution)
|
23
|
+
|
24
|
+
execution.complete!(result)
|
25
|
+
execution.log("info", "Crew execution completed successfully", { result: result })
|
26
|
+
|
27
|
+
# Trigger callbacks if configured
|
28
|
+
notify_completion(crew, execution, result)
|
29
|
+
|
30
|
+
result
|
31
|
+
rescue => e
|
32
|
+
execution.fail!(e)
|
33
|
+
execution.log("error", "Crew execution failed", {
|
34
|
+
error: e.message,
|
35
|
+
backtrace: e.backtrace.first(5)
|
36
|
+
})
|
37
|
+
|
38
|
+
# Re-raise for ActiveJob retry mechanism
|
39
|
+
raise
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def execute_with_logging(rcrew, inputs, execution)
|
46
|
+
# Set up logging callbacks
|
47
|
+
rcrew.before_task do |task|
|
48
|
+
execution.log("info", "Starting task: #{task.description}")
|
49
|
+
end
|
50
|
+
|
51
|
+
rcrew.after_task do |task, output|
|
52
|
+
execution.log("info", "Completed task: #{task.description}", { output: output })
|
53
|
+
end
|
54
|
+
|
55
|
+
# Execute the crew
|
56
|
+
rcrew.kickoff(inputs)
|
57
|
+
end
|
58
|
+
|
59
|
+
def notify_completion(crew, execution, result)
|
60
|
+
# Send notifications if configured
|
61
|
+
if crew.notification_webhook_url.present?
|
62
|
+
NotificationJob.perform_later(
|
63
|
+
crew.notification_webhook_url,
|
64
|
+
{
|
65
|
+
crew_id: crew.id,
|
66
|
+
execution_id: execution.id,
|
67
|
+
status: "completed",
|
68
|
+
result: result
|
69
|
+
}
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Trigger Rails events
|
74
|
+
ActiveSupport::Notifications.instrument("crew_execution.completed", {
|
75
|
+
crew: crew,
|
76
|
+
execution: execution,
|
77
|
+
result: result
|
78
|
+
})
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module RcrewAI
|
2
|
+
module Rails
|
3
|
+
class TaskExecutionJob < ActiveJob::Base
|
4
|
+
queue_as { RcrewAI::Rails.config.job_queue_name }
|
5
|
+
|
6
|
+
retry_on StandardError, wait: 5.seconds, attempts: 3
|
7
|
+
|
8
|
+
def perform(task, agent, inputs = {})
|
9
|
+
execution_log = {
|
10
|
+
task_id: task.id,
|
11
|
+
agent_id: agent.id,
|
12
|
+
started_at: Time.current
|
13
|
+
}
|
14
|
+
|
15
|
+
begin
|
16
|
+
# Convert to RcrewAI objects
|
17
|
+
rcrew_task = task.to_rcrew_task
|
18
|
+
rcrew_agent = agent.to_rcrew_agent
|
19
|
+
|
20
|
+
# Execute the task
|
21
|
+
result = rcrew_agent.execute_task(rcrew_task, context: inputs)
|
22
|
+
|
23
|
+
execution_log[:completed_at] = Time.current
|
24
|
+
execution_log[:status] = "completed"
|
25
|
+
execution_log[:result] = result
|
26
|
+
|
27
|
+
# Save result if configured
|
28
|
+
if task.output_file.present?
|
29
|
+
save_output_to_file(task.output_file, result)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Log success
|
33
|
+
::Rails.logger.info "Task #{task.id} completed successfully by agent #{agent.id}"
|
34
|
+
|
35
|
+
result
|
36
|
+
rescue => e
|
37
|
+
execution_log[:completed_at] = Time.current
|
38
|
+
execution_log[:status] = "failed"
|
39
|
+
execution_log[:error] = e.message
|
40
|
+
|
41
|
+
::Rails.logger.error "Task #{task.id} failed: #{e.message}"
|
42
|
+
|
43
|
+
raise
|
44
|
+
ensure
|
45
|
+
# Could save execution log to database if needed
|
46
|
+
log_task_execution(execution_log)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def save_output_to_file(filename, content)
|
53
|
+
output_path = ::Rails.root.join("tmp", "rcrewai_outputs", filename)
|
54
|
+
FileUtils.mkdir_p(File.dirname(output_path))
|
55
|
+
File.write(output_path, content)
|
56
|
+
end
|
57
|
+
|
58
|
+
def log_task_execution(log_data)
|
59
|
+
ActiveSupport::Notifications.instrument("task_execution.rcrewai", log_data)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module RcrewAI
|
2
|
+
module Rails
|
3
|
+
class Agent < ApplicationRecord
|
4
|
+
self.table_name = "rcrewai_agents"
|
5
|
+
|
6
|
+
belongs_to :crew
|
7
|
+
has_many :task_assignments, dependent: :destroy
|
8
|
+
has_many :tasks, through: :task_assignments
|
9
|
+
|
10
|
+
validates :name, presence: true
|
11
|
+
validates :role, presence: true
|
12
|
+
|
13
|
+
serialize :tools, coder: JSON, type: Array
|
14
|
+
serialize :llm_config, coder: JSON
|
15
|
+
|
16
|
+
scope :active, -> { where(active: true) }
|
17
|
+
|
18
|
+
def to_rcrew_agent
|
19
|
+
RCrewAI::Agent.new(
|
20
|
+
role: role,
|
21
|
+
goal: goal,
|
22
|
+
backstory: backstory,
|
23
|
+
memory: memory_enabled,
|
24
|
+
verbose: verbose,
|
25
|
+
allow_delegation: allow_delegation,
|
26
|
+
tools: instantiated_tools,
|
27
|
+
max_iter: max_iterations,
|
28
|
+
max_rpm: max_rpm,
|
29
|
+
llm: llm_config
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
def instantiated_tools
|
34
|
+
return [] if tools.blank?
|
35
|
+
|
36
|
+
tools.map do |tool_config|
|
37
|
+
tool_class = tool_config["class"].constantize
|
38
|
+
tool_params = tool_config["params"] || {}
|
39
|
+
tool_class.new(**tool_params.symbolize_keys)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_tool(tool_class, params = {})
|
44
|
+
self.tools ||= []
|
45
|
+
self.tools << {
|
46
|
+
"class" => tool_class.to_s,
|
47
|
+
"params" => params
|
48
|
+
}
|
49
|
+
save
|
50
|
+
end
|
51
|
+
|
52
|
+
def remove_tool(tool_class)
|
53
|
+
self.tools = tools.reject { |t| t["class"] == tool_class.to_s }
|
54
|
+
save
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module RcrewAI
|
2
|
+
module Rails
|
3
|
+
class Crew < ApplicationRecord
|
4
|
+
self.table_name = "rcrewai_crews"
|
5
|
+
|
6
|
+
has_many :agents, dependent: :destroy
|
7
|
+
has_many :tasks, dependent: :destroy
|
8
|
+
has_many :executions, dependent: :destroy
|
9
|
+
|
10
|
+
validates :name, presence: true
|
11
|
+
validates :process_type, inclusion: { in: %w[sequential hierarchical] }
|
12
|
+
|
13
|
+
serialize :config, coder: JSON
|
14
|
+
serialize :memory, coder: JSON
|
15
|
+
|
16
|
+
scope :active, -> { where(active: true) }
|
17
|
+
scope :with_agents, -> { includes(:agents) }
|
18
|
+
|
19
|
+
def to_rcrew
|
20
|
+
crew = RCrewAI::Crew.new(
|
21
|
+
name: name,
|
22
|
+
description: description,
|
23
|
+
process: process_type.to_sym,
|
24
|
+
verbose: verbose,
|
25
|
+
memory: memory_enabled,
|
26
|
+
cache: cache_enabled,
|
27
|
+
max_rpm: max_rpm,
|
28
|
+
manager_llm: manager_llm
|
29
|
+
)
|
30
|
+
|
31
|
+
agents.each do |agent|
|
32
|
+
crew.add_agent(agent.to_rcrew_agent)
|
33
|
+
end
|
34
|
+
|
35
|
+
tasks.each do |task|
|
36
|
+
crew.add_task(task.to_rcrew_task)
|
37
|
+
end
|
38
|
+
|
39
|
+
crew
|
40
|
+
end
|
41
|
+
|
42
|
+
def execute_async(inputs = {})
|
43
|
+
CrewExecutionJob.perform_later(self, inputs)
|
44
|
+
end
|
45
|
+
|
46
|
+
def execute_sync(inputs = {})
|
47
|
+
CrewExecutionJob.perform_now(self, inputs)
|
48
|
+
end
|
49
|
+
|
50
|
+
def last_execution
|
51
|
+
executions.order(created_at: :desc).first
|
52
|
+
end
|
53
|
+
|
54
|
+
def execution_stats
|
55
|
+
{
|
56
|
+
total: executions.count,
|
57
|
+
successful: executions.successful.count,
|
58
|
+
failed: executions.failed.count,
|
59
|
+
pending: executions.pending.count,
|
60
|
+
average_duration: executions.successful.average(:duration_seconds)
|
61
|
+
}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|