rcrewai-rails 0.2.8 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a983bc15d53694f94c8730ff76d92b4d2b51c5526d4ce72b3e8df78251328f16
4
- data.tar.gz: 30cf57ab32d034b63fd3ff3f94a26afe3db2d6b2db02261378a59a9dd0bb6ce5
3
+ metadata.gz: de3fd7383b01dfbcf053637b30537fb9e78d3541b0fdf09d0d7c92f0f132525e
4
+ data.tar.gz: 0f3e47c876aefb405a63abdd5b22ea965afdf438bdc2af46d3154e619d89f42d
5
5
  SHA512:
6
- metadata.gz: e30e0d23b8dac34e84eb157cb5ac9478f3025ffb48805001e16548b0d238f93f2b631ed585484dd6aca17f126fce967ee1754edc07ab3a3c5c2655149d6ddc12
7
- data.tar.gz: dcda024dbcfec7f39e84c6e40aab2a3f8e9c1b53e9aa0a8d80f0220bd2a84be0b2a118a40296c5a96afff12b0dc9b0b829961e9643c7cd383f01603935643765
6
+ metadata.gz: 8c4bdd914f99b4e2f5bb324e2d5658e088698be20834b0f4186a3e6ef7d574330c0de9cdc112970f937858858738800cf80f97cbc88699c1b7397674ba979a2c
7
+ data.tar.gz: c93205e5e31f6036f796c616ad623711136a6f2ab0f902a149c02737d3ecea902bafc4d564d3f2c36bcf1cfddd754c6bf8a35aefbcc74f3ecabc4165cf767f87
@@ -13,52 +13,63 @@ module RcrewAI
13
13
 
14
14
  begin
15
15
  execution.start!
16
- execution.log("info", "Starting crew execution", { crew_id: crew.id })
16
+ execution.log("info", "Starting crew execution", { crew_id: crew.id, inputs: inputs })
17
17
 
18
- # Convert Rails models to RcrewAI objects
19
18
  rcrew = crew.to_rcrew
20
-
21
- # Execute the crew with logging
22
- result = execute_with_logging(rcrew, inputs, execution)
23
-
19
+
20
+ result = rcrew.execute(stream: stream_sink_for(execution))
21
+
24
22
  execution.complete!(result)
25
- execution.log("info", "Crew execution completed successfully", { result: result })
26
-
27
- # Trigger callbacks if configured
23
+ execution.log("info", "Crew execution completed", { result: result })
24
+
28
25
  notify_completion(crew, execution, result)
29
-
26
+
30
27
  result
31
28
  rescue => e
32
29
  execution.fail!(e)
33
- execution.log("error", "Crew execution failed", {
30
+ execution.log("error", "Crew execution failed", {
34
31
  error: e.message,
35
- backtrace: e.backtrace.first(5)
32
+ backtrace: e.backtrace&.first(5)
36
33
  })
37
-
38
- # Re-raise for ActiveJob retry mechanism
34
+
39
35
  raise
40
36
  end
41
37
  end
42
38
 
43
39
  private
44
40
 
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 })
41
+ # Build a stream sink that translates rcrewai events into ExecutionLog rows.
42
+ # Note: the gem currently builds the sink but does not yet thread it down
43
+ # to per-agent execution, so this is wired up for forward-compatibility.
44
+ def stream_sink_for(execution)
45
+ lambda do |event|
46
+ case event
47
+ when RCrewAI::Events::IterationStart
48
+ execution.log("debug", "Iteration #{event.iteration_index} start", { agent: event.agent })
49
+ when RCrewAI::Events::IterationEnd
50
+ execution.log("debug", "Iteration end", { agent: event.agent, finish_reason: event.finish_reason })
51
+ when RCrewAI::Events::ToolCallStart
52
+ execution.log("info", "Tool call: #{event.tool}", { args: event.args, agent: event.agent })
53
+ when RCrewAI::Events::ToolCallResult
54
+ execution.log("info", "Tool result: #{event.tool}", { duration_ms: event.duration_ms, agent: event.agent })
55
+ when RCrewAI::Events::ToolCallError
56
+ execution.log("error", "Tool error: #{event.tool}", { error: event.error, agent: event.agent })
57
+ when RCrewAI::Events::Usage
58
+ execution.log("debug", "Usage", {
59
+ prompt_tokens: event.prompt_tokens,
60
+ completion_tokens: event.completion_tokens,
61
+ total_tokens: event.total_tokens,
62
+ cost_usd: event.cost_usd,
63
+ agent: event.agent
64
+ })
65
+ when RCrewAI::Events::Error
66
+ execution.log("error", "Crew error", { error: event.error, agent: event.agent })
67
+ end
53
68
  end
54
-
55
- # Execute the crew
56
- rcrew.kickoff(inputs)
57
69
  end
58
70
 
59
71
  def notify_completion(crew, execution, result)
60
- # Send notifications if configured
61
- if crew.notification_webhook_url.present?
72
+ if crew.respond_to?(:notification_webhook_url) && crew.notification_webhook_url.present?
62
73
  NotificationJob.perform_later(
63
74
  crew.notification_webhook_url,
64
75
  {
@@ -70,7 +81,6 @@ module RcrewAI
70
81
  )
71
82
  end
72
83
 
73
- # Trigger Rails events
74
84
  ActiveSupport::Notifications.instrument("crew_execution.completed", {
75
85
  crew: crew,
76
86
  execution: execution,
@@ -79,4 +89,4 @@ module RcrewAI
79
89
  end
80
90
  end
81
91
  end
82
- end
92
+ end
@@ -17,17 +17,25 @@ module RcrewAI
17
17
  rcrew_task = task.to_rcrew_task
18
18
  rcrew_agent = agent.to_rcrew_agent
19
19
 
20
- # Execute the task
21
- result = rcrew_agent.execute_task(rcrew_task, context: inputs)
20
+ # Execute the task. Agent#execute_task returns a hash:
21
+ # { content:, tool_calls_history:, usage:, iterations:, finish_reason: }
22
+ # `inputs` is recorded by the caller and made available as task context
23
+ # via the Task#context column; the gem does not accept it as a kwarg.
24
+ result = rcrew_agent.execute_task(rcrew_task)
25
+ content = result.is_a?(Hash) ? result[:content].to_s : result.to_s
22
26
 
23
27
  execution_log[:completed_at] = Time.current
24
28
  execution_log[:status] = "completed"
25
- execution_log[:result] = result
29
+ execution_log[:result] = content
30
+ if result.is_a?(Hash)
31
+ execution_log[:usage] = result[:usage]
32
+ execution_log[:tool_calls] = result[:tool_calls_history]
33
+ execution_log[:iterations] = result[:iterations]
34
+ execution_log[:finish_reason] = result[:finish_reason]
35
+ end
26
36
 
27
37
  # Save result if configured
28
- if task.output_file.present?
29
- save_output_to_file(task.output_file, result)
30
- end
38
+ save_output_to_file(task.output_file, content) if task.output_file.present?
31
39
 
32
40
  # Log success
33
41
  ::Rails.logger.info "Task #{task.id} completed successfully by agent #{agent.id}"
@@ -17,16 +17,14 @@ module RcrewAI
17
17
 
18
18
  def to_rcrew_agent
19
19
  RCrewAI::Agent.new(
20
+ name: name,
20
21
  role: role,
21
22
  goal: goal,
22
23
  backstory: backstory,
23
- memory: memory_enabled,
24
24
  verbose: verbose,
25
25
  allow_delegation: allow_delegation,
26
26
  tools: instantiated_tools,
27
- max_iter: max_iterations,
28
- max_rpm: max_rpm,
29
- llm: llm_config
27
+ max_iterations: max_iterations
30
28
  )
31
29
  end
32
30
 
@@ -18,14 +18,9 @@ module RcrewAI
18
18
 
19
19
  def to_rcrew
20
20
  crew = RCrewAI::Crew.new(
21
- name: name,
22
- description: description,
21
+ name,
23
22
  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
23
+ verbose: verbose
29
24
  )
30
25
 
31
26
  agents.each do |agent|
@@ -20,14 +20,12 @@ module RcrewAI
20
20
 
21
21
  def to_rcrew_task
22
22
  RCrewAI::Task.new(
23
+ name: rcrew_task_name,
23
24
  description: description,
24
25
  expected_output: expected_output,
25
26
  agent: agent&.to_rcrew_agent,
26
27
  context: context,
27
- async_execution: async_execution,
28
- output_json: output_json,
29
- output_pydantic: output_pydantic,
30
- output_file: output_file,
28
+ async: async_execution,
31
29
  tools: instantiated_tools,
32
30
  callback: callback_method
33
31
  )
@@ -54,6 +52,13 @@ module RcrewAI
54
52
 
55
53
  private
56
54
 
55
+ def rcrew_task_name
56
+ return "task_#{id}" if id
57
+ return description.to_s.parameterize.first(40).presence || "task" if description.present?
58
+
59
+ "task"
60
+ end
61
+
57
62
  def callback_method
58
63
  return nil unless callback_class.present? && callback_method_name.present?
59
64
 
data/docs/index.html ADDED
@@ -0,0 +1,410 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>RcrewAI Rails — AI agent crews for Rails</title>
7
+ <meta name="description" content="Rails engine for building AI agent crews with ActiveRecord persistence, background jobs, generators, and a web dashboard.">
8
+ <style>
9
+ :root {
10
+ --crimson: #c4221f;
11
+ --crimson-dark: #9c1714;
12
+ --ink: #1a1a1a;
13
+ --muted: #5c5c64;
14
+ --line: #e5e3df;
15
+ --bg: #fbfaf8;
16
+ --card: #ffffff;
17
+ --code-bg: #1f1d24;
18
+ --code-fg: #e8e6f0;
19
+ --code-line: #2e2b36;
20
+ --radius: 10px;
21
+ --maxw: 960px;
22
+ }
23
+ * { box-sizing: border-box; }
24
+ html { scroll-behavior: smooth; scroll-padding-top: 76px; }
25
+ body {
26
+ margin: 0;
27
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
28
+ color: var(--ink);
29
+ background: var(--bg);
30
+ line-height: 1.6;
31
+ -webkit-font-smoothing: antialiased;
32
+ }
33
+ a { color: var(--crimson); text-decoration: none; }
34
+ a:hover { text-decoration: underline; }
35
+ .wrap { max-width: var(--maxw); margin: 0 auto; padding: 0 24px; }
36
+
37
+ /* Header */
38
+ header {
39
+ position: sticky; top: 0; z-index: 50;
40
+ background: rgba(251,250,248,0.92);
41
+ backdrop-filter: saturate(180%) blur(8px);
42
+ border-bottom: 1px solid var(--line);
43
+ }
44
+ .nav {
45
+ display: flex; align-items: center; gap: 20px;
46
+ height: 60px;
47
+ }
48
+ .brand { font-weight: 700; font-size: 18px; color: var(--ink); display: flex; align-items: center; gap: 8px; }
49
+ .brand .dot { width: 12px; height: 12px; border-radius: 3px; background: var(--crimson); display: inline-block; }
50
+ .nav-links { display: flex; gap: 20px; margin-left: auto; align-items: center; }
51
+ .nav-links a { color: var(--muted); font-size: 14px; font-weight: 500; }
52
+ .nav-links a:hover { color: var(--ink); text-decoration: none; }
53
+ .nav-links .gh { color: var(--ink); font-weight: 600; }
54
+ @media (max-width: 720px) { .nav-links a:not(.gh) { display: none; } }
55
+
56
+ /* Hero */
57
+ .hero { padding: 72px 0 56px; text-align: center; border-bottom: 1px solid var(--line); }
58
+ .hero h1 { font-size: 44px; line-height: 1.1; margin: 0 0 16px; letter-spacing: -0.02em; }
59
+ .hero h1 .accent { color: var(--crimson); }
60
+ .hero p.lead { font-size: 19px; color: var(--muted); max-width: 640px; margin: 0 auto 28px; }
61
+ .install-line {
62
+ display: inline-block; font-family: "SF Mono", Menlo, Consolas, monospace;
63
+ background: var(--code-bg); color: var(--code-fg);
64
+ padding: 12px 18px; border-radius: var(--radius); font-size: 15px;
65
+ margin-bottom: 28px;
66
+ }
67
+ .install-line .prompt { color: #8a86a0; user-select: none; }
68
+ .cta { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }
69
+ .btn {
70
+ display: inline-block; padding: 11px 22px; border-radius: var(--radius);
71
+ font-weight: 600; font-size: 15px; border: 1px solid transparent;
72
+ }
73
+ .btn-primary { background: var(--crimson); color: #fff; }
74
+ .btn-primary:hover { background: var(--crimson-dark); text-decoration: none; }
75
+ .btn-secondary { background: var(--card); color: var(--ink); border-color: var(--line); }
76
+ .btn-secondary:hover { border-color: var(--muted); text-decoration: none; }
77
+
78
+ /* Sections */
79
+ section { padding: 56px 0; border-bottom: 1px solid var(--line); }
80
+ section h2 { font-size: 28px; margin: 0 0 8px; letter-spacing: -0.01em; }
81
+ section .sub { color: var(--muted); margin: 0 0 28px; font-size: 16px; }
82
+ h3 { font-size: 18px; margin: 32px 0 10px; }
83
+ h3:first-of-type { margin-top: 0; }
84
+
85
+ /* Feature grid */
86
+ .grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }
87
+ @media (max-width: 760px) { .grid { grid-template-columns: 1fr; } }
88
+ .feature {
89
+ background: var(--card); border: 1px solid var(--line);
90
+ border-radius: var(--radius); padding: 22px;
91
+ }
92
+ .feature h3 { margin: 0 0 6px; font-size: 16px; }
93
+ .feature p { margin: 0; color: var(--muted); font-size: 14.5px; }
94
+ .feature .ic { font-size: 22px; display: block; margin-bottom: 10px; }
95
+
96
+ /* Code */
97
+ pre {
98
+ background: var(--code-bg); color: var(--code-fg);
99
+ border: 1px solid var(--code-line); border-radius: var(--radius);
100
+ padding: 18px 20px; overflow-x: auto; font-size: 14px; line-height: 1.55;
101
+ font-family: "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
102
+ margin: 0 0 16px;
103
+ }
104
+ code { font-family: "SF Mono", Menlo, Consolas, monospace; }
105
+ p code, li code, td code {
106
+ background: #efece6; color: var(--crimson-dark);
107
+ padding: 2px 6px; border-radius: 5px; font-size: 13.5px;
108
+ }
109
+ .caption { font-size: 13px; color: var(--muted); margin: -8px 0 22px; }
110
+
111
+ /* Lists */
112
+ ul.clean { padding-left: 20px; }
113
+ ul.clean li { margin-bottom: 6px; }
114
+
115
+ /* Table */
116
+ table { width: 100%; border-collapse: collapse; font-size: 14px; }
117
+ th, td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--line); }
118
+ th { color: var(--muted); font-weight: 600; font-size: 12.5px; text-transform: uppercase; letter-spacing: 0.04em; }
119
+ td.method { font-family: "SF Mono", Menlo, Consolas, monospace; }
120
+ .verb { display: inline-block; min-width: 46px; font-weight: 700; }
121
+ .verb.get { color: #1f7a3d; }
122
+ .verb.post { color: var(--crimson); }
123
+
124
+ /* Footer */
125
+ footer { padding: 40px 0; text-align: center; color: var(--muted); font-size: 14px; }
126
+ footer a { color: var(--muted); text-decoration: underline; }
127
+ .two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 28px; }
128
+ @media (max-width: 760px) { .two-col { grid-template-columns: 1fr; } }
129
+ </style>
130
+ </head>
131
+ <body>
132
+
133
+ <header>
134
+ <div class="wrap nav">
135
+ <span class="brand"><span class="dot"></span>RcrewAI&nbsp;Rails</span>
136
+ <nav class="nav-links">
137
+ <a href="#features">Features</a>
138
+ <a href="#install">Install</a>
139
+ <a href="#usage">Usage</a>
140
+ <a href="#tools">Tools</a>
141
+ <a href="#api">API</a>
142
+ <a class="gh" href="https://github.com/gkosmo/rcrewai-rails">GitHub →</a>
143
+ </nav>
144
+ </div>
145
+ </header>
146
+
147
+ <section class="hero" style="border-top:none;">
148
+ <div class="wrap">
149
+ <h1>Build AI agent crews,<br><span class="accent">the Rails way.</span></h1>
150
+ <p class="lead">
151
+ A Rails engine for orchestrating AI agents — with ActiveRecord persistence,
152
+ background-job execution, generators, and a built-in web dashboard.
153
+ </p>
154
+ <div class="install-line"><span class="prompt">gem </span>'rcrewai-rails'</div>
155
+ <div class="cta">
156
+ <a class="btn btn-primary" href="https://github.com/gkosmo/rcrewai-rails">View on GitHub</a>
157
+ <a class="btn btn-secondary" href="https://rubygems.org/gems/rcrewai-rails">RubyGems</a>
158
+ </div>
159
+ </div>
160
+ </section>
161
+
162
+ <section id="features">
163
+ <div class="wrap">
164
+ <h2>Features</h2>
165
+ <p class="sub">Everything you need to run collaborative AI crews inside a Rails app.</p>
166
+ <div class="grid">
167
+ <div class="feature">
168
+ <span class="ic">🗄️</span>
169
+ <h3>ActiveRecord Integration</h3>
170
+ <p>Persist crews, agents, tasks, and executions directly in your database.</p>
171
+ </div>
172
+ <div class="feature">
173
+ <span class="ic">⚙️</span>
174
+ <h3>Background Jobs</h3>
175
+ <p>Runs through ActiveJob — works with Sidekiq, Resque, Delayed Job, or any adapter.</p>
176
+ </div>
177
+ <div class="feature">
178
+ <span class="ic">🏗️</span>
179
+ <h3>Rails Generators</h3>
180
+ <p>Scaffold new crews and agents from the command line in seconds.</p>
181
+ </div>
182
+ <div class="feature">
183
+ <span class="ic">📊</span>
184
+ <h3>Web Dashboard</h3>
185
+ <p>Monitor executions, view logs, and manage crews through a built-in UI.</p>
186
+ </div>
187
+ <div class="feature">
188
+ <span class="ic">🤖</span>
189
+ <h3>Multi-LLM Support</h3>
190
+ <p>OpenAI GPT, Anthropic Claude, Google Gemini, Azure OpenAI, and Ollama.</p>
191
+ </div>
192
+ <div class="feature">
193
+ <span class="ic">🧰</span>
194
+ <h3>Rails-Specific Tools</h3>
195
+ <p>Pre-built tools for ActiveRecord, ActionMailer, the Rails cache, and more.</p>
196
+ </div>
197
+ </div>
198
+ </div>
199
+ </section>
200
+
201
+ <section id="install">
202
+ <div class="wrap">
203
+ <h2>Installation</h2>
204
+ <p class="sub">Add the gem, run the installer, and migrate.</p>
205
+
206
+ <h3>1. Add to your Gemfile</h3>
207
+ <pre>gem 'rcrewai-rails'</pre>
208
+ <pre>$ bundle install</pre>
209
+
210
+ <h3>2. Run the install generator</h3>
211
+ <pre>$ rails generate rcrew_a_i:rails:install
212
+ $ rails db:migrate</pre>
213
+ <p class="caption">This creates the database migrations, adds a configuration initializer, and mounts the engine routes.</p>
214
+
215
+ <h3>Manual route setup</h3>
216
+ <p>If you prefer to mount the engine yourself, add this to <code>config/routes.rb</code>:</p>
217
+ <pre>Rails.application.routes.draw do
218
+ mount RcrewAI::Rails::Engine => '/rcrewai'
219
+ # Your other routes...
220
+ end</pre>
221
+ <p class="caption">The web UI is then available at <code>/rcrewai</code> and the API at <code>/rcrewai/api/v1/</code>.</p>
222
+ </div>
223
+ </section>
224
+
225
+ <section id="config">
226
+ <div class="wrap">
227
+ <h2>Configuration</h2>
228
+ <p class="sub">Configure the engine and the underlying RcrewAI gem in an initializer.</p>
229
+ <pre>RcrewAI::Rails.configure do |config|
230
+ # ActiveJob queue for background processing
231
+ config.job_queue_name = "default"
232
+
233
+ # Enable/disable web UI
234
+ config.enable_web_ui = true
235
+
236
+ # Use async execution by default
237
+ config.async_execution = true
238
+
239
+ # Default LLM settings
240
+ config.default_llm_provider = "openai"
241
+ config.default_llm_model = "gpt-4"
242
+
243
+ # Logging
244
+ config.enable_logging = true
245
+ config.log_level = :info
246
+ end
247
+
248
+ # Configure the base RcrewAI gem
249
+ RcrewAI.configure do |config|
250
+ config.openai_api_key = ENV["OPENAI_API_KEY"]
251
+ # Add other LLM provider keys as needed
252
+ end</pre>
253
+ </div>
254
+ </section>
255
+
256
+ <section id="usage">
257
+ <div class="wrap">
258
+ <h2>Usage</h2>
259
+ <p class="sub">Generate a crew, or define one programmatically.</p>
260
+
261
+ <h3>Generate a crew</h3>
262
+ <pre>$ rails generate rcrewai:rails:crew research_team sequential \
263
+ --agents researcher analyst writer \
264
+ --description "Research team for market analysis"</pre>
265
+ <p class="caption">Creates a crew class in <code>app/crews/research_team_crew.rb</code>.</p>
266
+
267
+ <h3>Define a crew programmatically</h3>
268
+ <pre>class ResearchCrew
269
+ include RcrewAI::Rails::CrewBuilder
270
+
271
+ crew_name "research_team"
272
+ crew_description "AI-powered research team"
273
+ process_type :sequential
274
+ memory_enabled true
275
+
276
+ def setup_agents
277
+ @researcher = create_agent("researcher",
278
+ role: "Senior Research Analyst",
279
+ goal: "Uncover insights and trends",
280
+ backstory: "Expert researcher with years of experience"
281
+ )
282
+
283
+ @writer = create_agent("writer",
284
+ role: "Content Writer",
285
+ goal: "Create compelling reports",
286
+ backstory: "Skilled writer specializing in technical content"
287
+ )
288
+ end
289
+
290
+ def setup_tasks
291
+ @research_task = create_task("Research latest AI trends",
292
+ expected_output: "Comprehensive research report",
293
+ position: 1
294
+ )
295
+ assign_agent_to_task(@researcher, @research_task)
296
+
297
+ @writing_task = create_task("Write executive summary",
298
+ expected_output: "2-page executive summary",
299
+ position: 2
300
+ )
301
+ assign_agent_to_task(@writer, @writing_task)
302
+ add_task_dependency(@writing_task, @research_task)
303
+ end
304
+ end
305
+
306
+ # Execute the crew
307
+ crew = ResearchCrew.new
308
+ execution = crew.execute(topic: "AI in Healthcare")</pre>
309
+
310
+ <h3>Running executions</h3>
311
+ <p>Executions run through ActiveJob by default, using whatever adapter your app is configured with:</p>
312
+ <pre># Async execution (default)
313
+ crew.execute_async(inputs)
314
+
315
+ # Sync execution
316
+ crew.execute_sync(inputs)
317
+
318
+ # Custom job options
319
+ CrewExecutionJob.set(wait: 5.minutes).perform_later(crew, inputs)</pre>
320
+ </div>
321
+ </section>
322
+
323
+ <section id="tools">
324
+ <div class="wrap">
325
+ <h2>Rails Tools</h2>
326
+ <p class="sub">Give your agents safe, scoped access to your Rails app.</p>
327
+ <p>Bundled tools include <code>ActiveRecordTool</code>, <code>ActionMailerTool</code>,
328
+ <code>RailsCacheTool</code>, <code>ActiveStorageTool</code>, and <code>RailsLoggerTool</code> —
329
+ each with allow-lists so agents only touch what you permit.</p>
330
+ <pre>class DataAnalystAgent
331
+ include RcrewAI::Rails::AgentBuilder
332
+
333
+ agent_role "Data Analyst"
334
+ agent_goal "Analyze application data"
335
+
336
+ tools [
337
+ RcrewAI::Rails::Tools::ActiveRecordTool.new(
338
+ model_class: User,
339
+ allowed_methods: [:count, :where, :pluck]
340
+ ),
341
+ RcrewAI::Rails::Tools::RailsCacheTool.new,
342
+ RcrewAI::Rails::Tools::ActionMailerTool.new(
343
+ mailer_class: ReportMailer,
344
+ allowed_methods: [:send_report]
345
+ )
346
+ ]
347
+ end</pre>
348
+ </div>
349
+ </section>
350
+
351
+ <section id="dashboard">
352
+ <div class="wrap">
353
+ <div class="two-col">
354
+ <div>
355
+ <h2>Web Dashboard</h2>
356
+ <p class="sub">Mounted at <code>/rcrewai</code>.</p>
357
+ <ul class="clean">
358
+ <li>View all crews and their configurations</li>
359
+ <li>Monitor execution status and logs</li>
360
+ <li>Start new executions</li>
361
+ <li>Browse execution history and results</li>
362
+ </ul>
363
+ </div>
364
+ <div>
365
+ <h2>Models</h2>
366
+ <p class="sub">Persisted as ActiveRecord.</p>
367
+ <ul class="clean">
368
+ <li><code>RcrewAI::Rails::Crew</code></li>
369
+ <li><code>RcrewAI::Rails::Agent</code></li>
370
+ <li><code>RcrewAI::Rails::Task</code></li>
371
+ <li><code>RcrewAI::Rails::Execution</code></li>
372
+ <li><code>RcrewAI::Rails::ExecutionLog</code></li>
373
+ </ul>
374
+ </div>
375
+ </div>
376
+ </div>
377
+ </section>
378
+
379
+ <section id="api">
380
+ <div class="wrap">
381
+ <h2>API Endpoints</h2>
382
+ <p class="sub">JSON API served under <code>/rcrewai/api/v1/</code>.</p>
383
+ <table>
384
+ <thead><tr><th style="width:90px;">Method</th><th>Path</th></tr></thead>
385
+ <tbody>
386
+ <tr><td><span class="verb get">GET</span></td><td class="method">/crews</td></tr>
387
+ <tr><td><span class="verb get">GET</span></td><td class="method">/crews/:id</td></tr>
388
+ <tr><td><span class="verb post">POST</span></td><td class="method">/crews/:id/execute</td></tr>
389
+ <tr><td><span class="verb get">GET</span></td><td class="method">/executions</td></tr>
390
+ <tr><td><span class="verb get">GET</span></td><td class="method">/executions/:id</td></tr>
391
+ <tr><td><span class="verb get">GET</span></td><td class="method">/executions/:id/status</td></tr>
392
+ <tr><td><span class="verb get">GET</span></td><td class="method">/executions/:id/logs</td></tr>
393
+ </tbody>
394
+ </table>
395
+ </div>
396
+ </section>
397
+
398
+ <footer>
399
+ <div class="wrap">
400
+ <p>
401
+ <a href="https://github.com/gkosmo/rcrewai-rails">GitHub</a> ·
402
+ <a href="https://rubygems.org/gems/rcrewai-rails">RubyGems</a> ·
403
+ <a href="https://github.com/gkosmo/rcrewai-rails/issues">Issues</a>
404
+ </p>
405
+ <p>Released under the MIT License.</p>
406
+ </div>
407
+ </footer>
408
+
409
+ </body>
410
+ </html>