rcrewai 0.2.1 → 0.4.0

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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +21 -0
  3. data/.rubocop_todo.yml +99 -0
  4. data/CHANGELOG.md +64 -1
  5. data/README.md +170 -2
  6. data/ROADMAP.md +84 -0
  7. data/Rakefile +53 -53
  8. data/bin/rcrewai +3 -3
  9. data/docs/mcp.md +109 -0
  10. data/docs/superpowers/plans/2026-05-11-llm-modernization.md +2753 -0
  11. data/docs/superpowers/specs/2026-05-11-llm-modernization-design.md +479 -0
  12. data/docs/upgrading-to-0.3.md +163 -0
  13. data/examples/async_execution_example.rb +82 -81
  14. data/examples/hierarchical_crew_example.rb +68 -72
  15. data/examples/human_in_the_loop_example.rb +73 -74
  16. data/examples/mcp_example.rb +48 -0
  17. data/examples/native_tools_example.rb +64 -0
  18. data/examples/streaming_example.rb +56 -0
  19. data/lib/rcrewai/agent.rb +181 -286
  20. data/lib/rcrewai/async_executor.rb +43 -43
  21. data/lib/rcrewai/cli.rb +11 -11
  22. data/lib/rcrewai/configuration.rb +34 -9
  23. data/lib/rcrewai/crew.rb +134 -39
  24. data/lib/rcrewai/events.rb +30 -0
  25. data/lib/rcrewai/flow/state.rb +47 -0
  26. data/lib/rcrewai/flow/state_store.rb +50 -0
  27. data/lib/rcrewai/flow.rb +243 -0
  28. data/lib/rcrewai/human_input.rb +104 -114
  29. data/lib/rcrewai/knowledge/base.rb +52 -0
  30. data/lib/rcrewai/knowledge/chunker.rb +31 -0
  31. data/lib/rcrewai/knowledge/embedder.rb +48 -0
  32. data/lib/rcrewai/knowledge/sources.rb +83 -0
  33. data/lib/rcrewai/knowledge/store.rb +58 -0
  34. data/lib/rcrewai/knowledge.rb +13 -0
  35. data/lib/rcrewai/legacy_react_runner.rb +172 -0
  36. data/lib/rcrewai/llm_client.rb +24 -1
  37. data/lib/rcrewai/llm_clients/anthropic.rb +174 -54
  38. data/lib/rcrewai/llm_clients/azure.rb +23 -128
  39. data/lib/rcrewai/llm_clients/base.rb +11 -7
  40. data/lib/rcrewai/llm_clients/google.rb +159 -95
  41. data/lib/rcrewai/llm_clients/ollama.rb +150 -106
  42. data/lib/rcrewai/llm_clients/openai.rb +140 -63
  43. data/lib/rcrewai/mcp/client.rb +101 -0
  44. data/lib/rcrewai/mcp/tool_adapter.rb +59 -0
  45. data/lib/rcrewai/mcp/transport/http.rb +53 -0
  46. data/lib/rcrewai/mcp/transport/stdio.rb +55 -0
  47. data/lib/rcrewai/mcp.rb +8 -0
  48. data/lib/rcrewai/memory.rb +45 -37
  49. data/lib/rcrewai/output_schema.rb +79 -0
  50. data/lib/rcrewai/planning.rb +65 -0
  51. data/lib/rcrewai/pricing.rb +34 -0
  52. data/lib/rcrewai/process.rb +86 -95
  53. data/lib/rcrewai/provider_schema.rb +38 -0
  54. data/lib/rcrewai/sse_parser.rb +55 -0
  55. data/lib/rcrewai/task.rb +145 -66
  56. data/lib/rcrewai/tool_runner.rb +132 -0
  57. data/lib/rcrewai/tool_schema.rb +97 -0
  58. data/lib/rcrewai/tools/base.rb +98 -37
  59. data/lib/rcrewai/tools/code_executor.rb +71 -74
  60. data/lib/rcrewai/tools/email_sender.rb +70 -78
  61. data/lib/rcrewai/tools/file_reader.rb +38 -30
  62. data/lib/rcrewai/tools/file_writer.rb +40 -38
  63. data/lib/rcrewai/tools/pdf_processor.rb +115 -130
  64. data/lib/rcrewai/tools/sql_database.rb +58 -55
  65. data/lib/rcrewai/tools/web_search.rb +26 -25
  66. data/lib/rcrewai/version.rb +2 -2
  67. data/lib/rcrewai.rb +20 -10
  68. data/rcrewai.gemspec +39 -39
  69. metadata +77 -47
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '05099e1309676b2a0d1028fc658aead2c212aa39903a0e75f13a944063897806'
4
- data.tar.gz: 39a53213a82a4e6d5f44d71eca94ac2b960e657c07fc42af4f2290e162188ab7
3
+ metadata.gz: f742fcf06518c80cbf29191d0eff275830ee618c9b896c3d443258b06aa120ff
4
+ data.tar.gz: 5cc9c03182d0bd80d73d99edb08f2267252684f64a23da1626d2d61e0abe1683
5
5
  SHA512:
6
- metadata.gz: 500fcf358b1473741b0248f6ce12b721d6d8d2d5f55bf849d6bec9c74faa525e7e067794613de89983470c9d8b1c10d4375eef4867e7ffc4058c441fc0cd607a
7
- data.tar.gz: 428121cbe207d084507671874f786563357d48b18fe1517ab01a7c83460ddaf1604211f9d9d934d170ecdce82b0e69f5d7373bfdc63876b435bcb209f25d7d70
6
+ metadata.gz: 0c32e4110a5bbabac9b120262e2ebe0e86b0c58c3b4213a11214efcff647024d9714fcee12934f4b734c9f64184e1f802d82b97ec50e5dc7ceab424c4cec38f1
7
+ data.tar.gz: 8a678ae53008c7c6a73cf365b460b80989da05f22eb646cf3a9126028a0d6dd220e6af75ef4dee80c02d73e69b536fb3d2b7c23f827e929078b76ed450d8f1ce
data/.rubocop.yml ADDED
@@ -0,0 +1,21 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ Naming/MethodParameterName:
4
+ # `k` (top-k retrieval) and short math vars for vector similarity are
5
+ # conventional and clearer than forced longer names. The rest are RuboCop's
6
+ # defaults, restated because AllowedNames replaces rather than extends.
7
+ AllowedNames:
8
+ - k
9
+ - a
10
+ - b
11
+ - io
12
+ - id
13
+ - to
14
+ - by
15
+ - 'on'
16
+ - in
17
+ - at
18
+ - ip
19
+ - db
20
+ - os
21
+ - pp
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,99 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2026-05-12 09:06:14 UTC using RuboCop version 1.78.0.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 2
10
+ Lint/DuplicateMethods:
11
+ Exclude:
12
+ - 'lib/rcrewai/configuration.rb'
13
+
14
+ # Offense count: 55
15
+ # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
16
+ Metrics/AbcSize:
17
+ Max: 61
18
+
19
+ # Offense count: 30
20
+ # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
21
+ # AllowedMethods: refine
22
+ Metrics/BlockLength:
23
+ Max: 325
24
+
25
+ # Offense count: 1
26
+ # Configuration parameters: CountBlocks, CountModifierForms.
27
+ Metrics/BlockNesting:
28
+ Max: 4
29
+
30
+ # Offense count: 14
31
+ # Configuration parameters: CountComments, CountAsOne.
32
+ Metrics/ClassLength:
33
+ Max: 408
34
+
35
+ # Offense count: 33
36
+ # Configuration parameters: AllowedMethods, AllowedPatterns.
37
+ Metrics/CyclomaticComplexity:
38
+ Max: 21
39
+
40
+ # Offense count: 99
41
+ # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
42
+ Metrics/MethodLength:
43
+ Max: 67
44
+
45
+ # Offense count: 1
46
+ # Configuration parameters: CountKeywordArgs, MaxOptionalParameters.
47
+ Metrics/ParameterLists:
48
+ Max: 6
49
+
50
+ # Offense count: 18
51
+ # Configuration parameters: AllowedMethods, AllowedPatterns.
52
+ Metrics/PerceivedComplexity:
53
+ Max: 21
54
+
55
+ # Offense count: 1
56
+ # Configuration parameters: NamePrefix, ForbiddenPrefixes, AllowedMethods, MethodDefinitionMacros, UseSorbetSigs.
57
+ # NamePrefix: is_, has_, have_, does_
58
+ # ForbiddenPrefixes: is_, has_, have_, does_
59
+ # AllowedMethods: is_a?
60
+ # MethodDefinitionMacros: define_method, define_singleton_method
61
+ Naming/PredicatePrefix:
62
+ Exclude:
63
+ - 'spec/**/*'
64
+ - 'lib/rcrewai/agent.rb'
65
+
66
+ # Offense count: 32
67
+ # Configuration parameters: AllowedConstants.
68
+ Style/Documentation:
69
+ Enabled: false
70
+
71
+ # Offense count: 23
72
+ # This cop supports safe autocorrection (--autocorrect).
73
+ Style/IfUnlessModifier:
74
+ Exclude:
75
+ - 'lib/rcrewai/agent.rb'
76
+ - 'lib/rcrewai/human_input.rb'
77
+ - 'lib/rcrewai/llm_clients/ollama.rb'
78
+ - 'lib/rcrewai/memory.rb'
79
+ - 'lib/rcrewai/process.rb'
80
+ - 'lib/rcrewai/tools/code_executor.rb'
81
+ - 'lib/rcrewai/tools/email_sender.rb'
82
+ - 'lib/rcrewai/tools/file_writer.rb'
83
+ - 'lib/rcrewai/tools/pdf_processor.rb'
84
+ - 'lib/rcrewai/tools/sql_database.rb'
85
+
86
+ # Offense count: 1
87
+ # This cop supports safe autocorrection (--autocorrect).
88
+ # Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, AllowedMethods.
89
+ # AllowedMethods: to_ary, to_a, to_c, to_enum, to_h, to_hash, to_i, to_int, to_io, to_open, to_path, to_proc, to_r, to_regexp, to_str, to_s, to_sym
90
+ Style/TrivialAccessors:
91
+ Exclude:
92
+ - 'lib/rcrewai/process.rb'
93
+
94
+ # Offense count: 16
95
+ # This cop supports safe autocorrection (--autocorrect).
96
+ # Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings.
97
+ # URISchemes: http, https
98
+ Layout/LineLength:
99
+ Max: 201
data/CHANGELOG.md CHANGED
@@ -7,6 +7,67 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.4.0] - 2026-07-03
11
+
12
+ This release closes the feature-parity gap with the modern CrewAI framework,
13
+ adding its second pillar (**Flows**) alongside **Knowledge (RAG)**, structured
14
+ output, guardrails, planning, and training/testing. See `ROADMAP.md`.
15
+
16
+ ### Added
17
+
18
+ #### Flows (#11)
19
+ - `RCrewAI::Flow` — an event-driven workflow engine (CrewAI's second pillar). Subclass it and declare methods with a class-level DSL: `start`, `listen`, `router`, and the `and_` / `or_` trigger combinators. `kickoff` runs the graph to a fixed point; routers emit labels that listeners trigger on.
20
+ - Flow state (`Flow::State`) is a schemaless object with an automatic UUID, seedable via `kickoff(inputs:)`.
21
+ - Flow persistence: pluggable state stores (`Flow::MemoryStateStore`, `Flow::FileStateStore`, or any `#save`/`#load` object); `flow.restore(id)` resumes a persisted run.
22
+ - Flows can invoke a `Crew` as a step and pause for input via `#human_feedback`.
23
+
24
+ #### Knowledge / RAG (#9)
25
+ - `RCrewAI::Knowledge` module adds retrieval-augmented context. Sources (`StringSource`, `FileSource`, `PdfSource`, `CsvSource`, `UrlSource`) are chunked, embedded, and stored in an in-memory cosine-similarity vector store (no external DB required).
26
+ - Attach via `Agent.new(knowledge:)` / `knowledge_sources:` (role-specific) or `Crew.new(knowledge:)` / `knowledge_sources:` (shared with all agents); relevant chunks are injected into each task's prompt at execution.
27
+ - The embedder (`Knowledge::Embedder`, default OpenAI `text-embedding-3-small`) and vector store are pluggable.
28
+
29
+ #### Task output processing (#6, #7, #8)
30
+ - Structured output: `Task.new(output_schema:)` validates and coerces the agent's output against a JSON-schema subset, exposing the parsed object via `Task#structured_output` (and the raw string via `Task#raw_result`). JSON embedded in surrounding prose or a fenced code block is extracted automatically; output that doesn't conform re-runs the agent with the error fed back.
31
+ - Guardrails: `Task.new(guardrail:)` takes a callable returning `[ok, value_or_error]` to validate and transform output before it flows downstream, retrying up to `guardrail_max_retries` (default 3) with the rejection reason fed back to the agent.
32
+ - Output persistence & formatting: `Task.new(output_file:)` writes the result to disk (`create_directory:` controls parent-dir creation, default true), and `markdown: true` prepends a heading when the output isn't already a markdown document.
33
+ - `RCrewAI::OutputSchema` — a small JSON-schema-subset validator/coercer used by structured task output.
34
+
35
+ #### Per-agent LLM (#5)
36
+ - `Agent.new(llm:)` accepts a provider symbol (`:anthropic`), an options hash (`{ provider:, model:, api_key:, temperature: }`), or a pre-built client instance. Agents in the same crew can use different providers/models (e.g. a cheap worker model and a stronger manager model). Omitting `llm:` keeps the previous global-configuration behavior.
37
+ - `Configuration#with_overrides` returns a copy of the configuration with per-agent overrides applied, leaving global state untouched.
38
+
39
+ #### Planning (#10)
40
+ - `Crew.new(planning: true)` runs a single planner pass before execution that asks an LLM to draft a short plan for each task and folds it into the task's description. Optional `planning_llm:` selects the planner client (defaults to the global provider). Best-effort — a planner error or unparseable output leaves tasks unchanged and execution proceeds.
41
+ - `Task#enrich_description` appends supplementary guidance (used by the planner) without discarding the original instructions.
42
+
43
+ #### Training & testing (#12)
44
+ - `Crew#train(n_iterations:, filename:)` runs the crew repeatedly, collects feedback after each iteration (via a `feedback:` callable, defaulting to a human prompt), and persists it as JSON.
45
+ - `Crew#test(n_iterations:)` runs the crew repeatedly and reports per-run and average scores (via a `scorer:` callable, defaulting to the run's success rate).
46
+
47
+ ## [0.3.0] - 2026-05-12
48
+
49
+ ### Added
50
+ - Native function calling across all five providers (OpenAI, Anthropic, Google, Azure, Ollama). Tools declare a JSON schema via the new DSL (`tool_name`, `description`, `param`) on `Tools::Base`.
51
+ - Typed streaming event model (`RCrewAI::Events::*`) covering text deltas, tool-call lifecycle, usage, and errors. Pass `stream:` to `crew.execute` or `agent.execute_task`.
52
+ - MCP (Model Context Protocol) client. Connect to stdio or HTTP MCP servers and expose their tools as ordinary RCrewAI tools (`RCrewAI::MCP::Client.with_connection`).
53
+ - Per-model price table (`RCrewAI::Pricing`) and `cost_usd` on `Events::Usage` for cost tracking.
54
+ - `Tools::Base#execute_with_validation` coerces and validates args against the DSL schema.
55
+ - New `ToolRunner` (native function calling loop) and `LegacyReactRunner` (extracted `USE_TOOL[]` parsing loop).
56
+ - `RCrewAI::SSEParser` — reusable Server-Sent Events parser used by all providers and MCP HTTP transport.
57
+
58
+ ### Changed
59
+ - `Agent#execute_task` now delegates to `ToolRunner` (native function calling) or `LegacyReactRunner` (existing `USE_TOOL[]` parsing, used as fallback for legacy models or tools without a DSL declaration).
60
+ - `Agent#execute_task` return value is now a hash including `:content`, `:tool_calls_history`, `:usage`, `:iterations`, `:finish_reason`. `task.result` continues to hold the string content.
61
+ - `LLMClients::Base#chat` gains `tools:`, `tool_choice:`, and `stream:` keyword arguments.
62
+ - Provider clients return symbolic `finish_reason` (`:stop`, `:tool_calls`, `:length`) and symbol-keyed `usage`.
63
+
64
+ ### Breaking
65
+ - Subclasses of `LLMClients::Base` that override `chat` with an explicit kwarg list must add `tools: nil, stream: nil` to the signature (or accept `**options`).
66
+ - Tools without DSL declarations now receive a permissive fallback schema and emit a one-time deprecation warning to stderr.
67
+
68
+ ### Migration
69
+ - See `docs/upgrading-to-0.3.md` for step-by-step migration.
70
+
10
71
  ## [0.1.0] - 2025-01-12
11
72
 
12
73
  ### Added
@@ -104,5 +165,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
104
165
  - CLI usage documentation
105
166
  - Real-world use cases and examples
106
167
 
107
- [Unreleased]: https://github.com/gkosmo/rcrewAI/compare/v0.1.0...HEAD
168
+ [Unreleased]: https://github.com/gkosmo/rcrewAI/compare/v0.4.0...HEAD
169
+ [0.4.0]: https://github.com/gkosmo/rcrewAI/compare/v0.3.0...v0.4.0
170
+ [0.3.0]: https://github.com/gkosmo/rcrewAI/compare/v0.1.0...v0.3.0
108
171
  [0.1.0]: https://github.com/gkosmo/rcrewAI/releases/tag/v0.1.0
data/README.md CHANGED
@@ -19,6 +19,8 @@ RCrewAI is a Ruby implementation of the CrewAI framework, allowing you to create
19
19
  - **🏗️ Hierarchical Teams**: Manager agents that coordinate and delegate tasks to specialist agents
20
20
  - **🔒 Production Ready**: Security controls, error handling, logging, monitoring, and sandboxing
21
21
  - **🎯 Flexible Orchestration**: Sequential, hierarchical, and concurrent execution modes
22
+ - **🌊 Flows**: Event-driven workflows with `start`/`listen`/`router`, branching, and persistent state
23
+ - **📚 Knowledge (RAG)**: Ground agents in your own documents with built-in retrieval
22
24
  - **💎 Ruby-First Design**: Built specifically for Ruby developers with idiomatic patterns
23
25
 
24
26
  ## 📦 Installation
@@ -169,6 +171,172 @@ RCrewAI.configure do |config|
169
171
  end
170
172
  ```
171
173
 
174
+ ### Per-agent LLM
175
+
176
+ The `RCrewAI.configure` block sets the crew-wide default. Any agent can override
177
+ it with the `llm:` option, so a single crew can mix providers and models — for
178
+ example a cheap model for workers and a stronger one for the manager:
179
+
180
+ ```ruby
181
+ # Provider only (uses that provider's configured model + key)
182
+ researcher = RCrewAI::Agent.new(name: 'researcher', role: '...', goal: '...',
183
+ llm: :anthropic)
184
+
185
+ # Provider + model (and optionally api_key / temperature)
186
+ manager = RCrewAI::Agent.new(name: 'manager', role: '...', goal: '...',
187
+ llm: { provider: :anthropic, model: 'claude-3-opus-20240229' })
188
+
189
+ worker = RCrewAI::Agent.new(name: 'worker', role: '...', goal: '...',
190
+ llm: { provider: :openai, model: 'gpt-4o-mini' })
191
+
192
+ # Or pass a pre-built client instance
193
+ worker = RCrewAI::Agent.new(name: 'worker', role: '...', goal: '...',
194
+ llm: my_client)
195
+ ```
196
+
197
+ Omit `llm:` to use the global `RCrewAI.configure` settings. Overrides never
198
+ mutate the global configuration.
199
+
200
+ ## 📤 Structured Output, Guardrails & File Output
201
+
202
+ Tasks can validate, transform, and persist their output:
203
+
204
+ ```ruby
205
+ task = RCrewAI::Task.new(
206
+ name: 'extract',
207
+ description: 'Extract the article title and word count as JSON',
208
+ agent: analyst,
209
+
210
+ # Structured output — validated & coerced against a JSON schema.
211
+ # Non-conforming output re-runs the agent with the error fed back.
212
+ output_schema: {
213
+ type: 'object',
214
+ properties: { title: { type: 'string' }, words: { type: 'integer' } },
215
+ required: ['title']
216
+ },
217
+
218
+ # Guardrail — ->(output) { [ok, value_or_error] }. On rejection the agent
219
+ # re-runs (up to guardrail_max_retries) with the reason appended.
220
+ guardrail: ->(out) { [out.length < 5000, 'must be under 5000 chars'] },
221
+ guardrail_max_retries: 3,
222
+
223
+ # Persist the result. Parent dirs are created unless create_directory: false.
224
+ output_file: 'out/report.md',
225
+ markdown: true
226
+ )
227
+
228
+ task.execute
229
+ task.structured_output # => { "title" => "...", "words" => 1234 }
230
+ task.raw_result # => the unprocessed string the agent produced
231
+ ```
232
+
233
+ ## 🗺️ Planning
234
+
235
+ Enable `planning:` on a crew to run a planner pass before execution. The planner
236
+ drafts a short plan for each task and folds it into the task description, giving
237
+ the executing agent a head start:
238
+
239
+ ```ruby
240
+ crew = RCrewAI::Crew.new('research_crew', planning: true)
241
+ # Optionally use a dedicated (e.g. stronger) planner model:
242
+ crew = RCrewAI::Crew.new('research_crew', planning: true,
243
+ planning_llm: { provider: :anthropic, model: 'claude-3-opus-20240229' })
244
+ ```
245
+
246
+ Planning is best-effort: if the planner errors or returns unparseable output,
247
+ the crew runs with the original tasks unchanged.
248
+
249
+ ## 🏋️ Training & Testing
250
+
251
+ Iterate on a crew by training it with feedback or scoring repeated runs:
252
+
253
+ ```ruby
254
+ # Train: run N times, collect feedback after each run, persist to JSON.
255
+ crew.train(n_iterations: 3, filename: 'training.json')
256
+
257
+ # Provide feedback programmatically instead of prompting a human:
258
+ crew.train(n_iterations: 3, filename: 'training.json',
259
+ feedback: ->(iteration, result) { "run #{iteration}: #{result[:success_rate]}%" })
260
+
261
+ # Test: run N times and score each run (defaults to success_rate).
262
+ crew.test(n_iterations: 5)
263
+ # => { iterations: 5, scores: [...], average_score: 92.0 }
264
+ ```
265
+
266
+ ## 📚 Knowledge (RAG)
267
+
268
+ Ground agents in your own documents. Sources are chunked, embedded, and stored
269
+ in an in-memory vector store; the most relevant chunks are injected into each
270
+ task's prompt automatically.
271
+
272
+ ```ruby
273
+ kb = RCrewAI::Knowledge::Base.new(sources: [
274
+ RCrewAI::Knowledge::StringSource.new('Our refund window is 30 days.'),
275
+ RCrewAI::Knowledge::FileSource.new('docs/policy.txt'),
276
+ RCrewAI::Knowledge::PdfSource.new('handbook.pdf'),
277
+ RCrewAI::Knowledge::UrlSource.new('https://example.com/faq')
278
+ ])
279
+
280
+ # Agent-level (role-specific) knowledge:
281
+ support = RCrewAI::Agent.new(name: 'support', role: '...', goal: '...', knowledge: kb)
282
+
283
+ # Or pass raw sources and let the agent build the base:
284
+ support = RCrewAI::Agent.new(name: 'support', role: '...', goal: '...',
285
+ knowledge_sources: [RCrewAI::Knowledge::StringSource.new('...')])
286
+
287
+ # Crew-level knowledge is shared with every agent:
288
+ crew = RCrewAI::Crew.new('support_crew', knowledge: kb)
289
+ ```
290
+
291
+ Embeddings default to OpenAI's `text-embedding-3-small`; pass a custom
292
+ `embedder:` (anything responding to `embed(texts)`) or vector store to swap the
293
+ backend.
294
+
295
+ ## 🌊 Flows
296
+
297
+ Beyond crews, RCrewAI has **Flows** — an event-driven workflow engine for
298
+ orchestrating steps (and whole crews) with explicit branching and state:
299
+
300
+ ```ruby
301
+ class ArticleFlow < RCrewAI::Flow
302
+ start :outline
303
+ def outline
304
+ state.sections = %w[intro body conclusion]
305
+ state.sections.length
306
+ end
307
+
308
+ listen :outline
309
+ def draft(section_count)
310
+ state.words = section_count * 100
311
+ state.words
312
+ end
313
+
314
+ router :draft
315
+ def review(words)
316
+ words >= 250 ? :publish : :expand
317
+ end
318
+
319
+ listen :publish
320
+ def publish = state.status = 'published'
321
+
322
+ listen :expand
323
+ def expand = state.status = 'needs more work'
324
+ end
325
+
326
+ flow = ArticleFlow.new
327
+ flow.kickoff(inputs: { author: 'me' })
328
+ flow.state.status # => "published"
329
+ flow.state.id # => automatic UUID
330
+ ```
331
+
332
+ - `start` / `listen` / `router` wire methods into a graph; a listener receives
333
+ its trigger's return value.
334
+ - Combine triggers with `and_(:a, :b)` (all) and `or_(:a, :b)` (any).
335
+ - **State** is a schemaless object with a UUID, seedable via `kickoff(inputs:)`.
336
+ - **Persistence**: pass `state_store:` (`RCrewAI::Flow::FileStateStore.new(dir)`
337
+ or your own `#save`/`#load`) and call `flow.restore(id)` to resume.
338
+ - Invoke a `Crew` inside any step, or pause with `human_feedback('Approve?')`.
339
+
172
340
  ## 💡 Examples
173
341
 
174
342
  ### Hierarchical Team with Human Oversight
@@ -303,7 +471,7 @@ RCrewAI provides a flexible, production-ready architecture:
303
471
 
304
472
  ### rcrew RAILS
305
473
 
306
- For Rails applications, use the **rcrew RAILS** gem (`rcrewai-rails`) which provides:
474
+ For Rails applications, use the **rcrew RAILS** gem (`rcrewai-rails`) [(repo here)](https://github.com/gkosmo/rcrewai-rails) which provides:
307
475
 
308
476
  - **🏗️ Rails Engine**: Mountable engine with web UI for managing crews
309
477
  - **💾 ActiveRecord Integration**: Database persistence for agents, tasks, and executions
@@ -357,4 +525,4 @@ RCrewAI is released under the [MIT License](LICENSE).
357
525
 
358
526
  ---
359
527
 
360
- Made with ❤️ by the RCrewAI community
528
+ Made with ❤️ by the RCrewAI community
data/ROADMAP.md ADDED
@@ -0,0 +1,84 @@
1
+ # RCrewAI Roadmap
2
+
3
+ This roadmap tracks feature parity between **RCrewAI** (Ruby) and the upstream
4
+ [**crewai**](https://pypi.org/project/crewai/) Python framework.
5
+
6
+ ## Current status
7
+
8
+ - **RCrewAI:** `0.3.0` (2026-05-12)
9
+ - **Upstream crewai:** `1.15.x` (mid-2026)
10
+
11
+ RCrewAI is a faithful port of CrewAI's **"Crews"** mental model (Agents / Tasks /
12
+ Crew, sequential + hierarchical processes, tools, memory, human-in-the-loop). As
13
+ of `0.3.0` the LLM plumbing is modern: native function calling across all five
14
+ providers, a tool-schema DSL, typed streaming events, MCP client, and per-model
15
+ pricing.
16
+
17
+ Since CrewAI's `1.0`, the framework grew a second pillar (**Flows**) plus
18
+ **Knowledge (RAG)**, **Guardrails**, **structured output**, **Planning**, and
19
+ **Training/Testing**. As of the `[Unreleased]` changes, RCrewAI now implements
20
+ all of these — see the matrix below. Only backlog polish items remain.
21
+
22
+ **Status: all milestone issues (#5–#12) are complete.** The remaining backlog
23
+ covers smaller polish items (reasoning, rate-limiting, batch kickoff, kickoff
24
+ hooks, multimodal).
25
+
26
+ ## Parity matrix
27
+
28
+ | Concept | crewai | RCrewAI 0.3.0 | Target |
29
+ |---|---|---|---|
30
+ | Agents / Tasks / Crew | ✅ | ✅ | — |
31
+ | Sequential / hierarchical process | ✅ | ✅ | — |
32
+ | Native function calling + tool DSL | ✅ | ✅ (0.3.0) | — |
33
+ | Streaming events | ✅ | ✅ (0.3.0) | — |
34
+ | MCP client | ✅ | ✅ (0.3.0) | — |
35
+ | Per-model pricing / cost | ✅ | ✅ (0.3.0) | — |
36
+ | Per-agent LLM override | ✅ | ✅ (#5) | ✅ done |
37
+ | Structured output (schema) | ✅ | ✅ (#6) | ✅ done |
38
+ | Task guardrails | ✅ | ✅ (#7) | ✅ done |
39
+ | `output_file` / markdown | ✅ | ✅ (#8) | ✅ done |
40
+ | Knowledge / RAG | ✅ | ✅ (#9) | ✅ done |
41
+ | Planning | ✅ | ✅ (#10) | ✅ done |
42
+ | Flows (`start`/`listen`/`router`) | ✅ | ✅ (#11) | ✅ done |
43
+ | Flow state + persistence | ✅ | ✅ (#11) | ✅ done |
44
+ | Training / Testing | ✅ | ✅ (#12) | ✅ done |
45
+ | Reasoning, rate-limiting, batch kickoff | ✅ | ❌ | backlog |
46
+
47
+ ## Milestones (highest leverage first)
48
+
49
+ ### 0.3.1 — Per-agent LLM override
50
+ Let `Agent.new(llm:)` accept a provider/model, instead of only the global
51
+ `RCrewAI.configure`. Unblocks mixed-model crews (cheap model for workers, strong
52
+ model for the manager).
53
+
54
+ ### 0.4.0 — Structured output & guardrails
55
+ Builds directly on the 0.3.0 tool-schema/JSON-schema plumbing.
56
+ - `Task.new(output_schema:)` → validated, coerced structured result.
57
+ - `Task.new(guardrail:)` → proc/object that validates & transforms output, with
58
+ bounded retries (`guardrail_max_retries`).
59
+ - `output_file:` + `markdown:` output formatting.
60
+
61
+ ### 0.5.0 — Knowledge (RAG) & Planning
62
+ - Knowledge sources: string, `.txt`, PDF (have `pdf-reader`), CSV, JSON, URL
63
+ (have `nokogiri`). Embeddings client + a pluggable vector store (start with an
64
+ in-memory / SQLite cosine store; no hard Chroma dependency).
65
+ - Attach at agent **and** crew level.
66
+ - `Crew.new(planning: true)` → a planner pass that drafts a step plan before
67
+ execution.
68
+
69
+ ### 0.6.0 — Flows
70
+ The flagship. A Ruby DSL mirroring CrewAI Flows:
71
+ - `start`, `listen`, `router` decorators/class-methods.
72
+ - `and_` / `or_` trigger combinators.
73
+ - Structured flow **state** (a plain struct/`Data` or dry-struct) with a UUID.
74
+ - `@persist`-equivalent state persistence across restarts.
75
+ - `human_feedback` pause/resume point.
76
+
77
+ ### 0.7.0 — Training & Testing
78
+ - `crew.train(n_iterations:, filename:)` capturing human feedback.
79
+ - `crew.test(n_iterations:, model:)` scoring runs.
80
+
81
+ ### Backlog
82
+ Per-agent reasoning (`reasoning:`, `max_reasoning_attempts:`), `max_rpm`
83
+ rate-limiting, `respect_context_window`, `kickoff_for_each` batch execution,
84
+ `before_kickoff` / `after_kickoff` hooks, multimodal agents.
data/Rakefile CHANGED
@@ -1,130 +1,130 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
5
- require "rubocop/rake_task"
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+ require 'rubocop/rake_task'
6
6
 
7
7
  # Default task
8
- task default: [:spec, :rubocop]
8
+ task default: %i[spec rubocop]
9
9
 
10
10
  # RSpec task
11
11
  RSpec::Core::RakeTask.new(:spec) do |task|
12
- task.rspec_opts = "--color --format documentation"
12
+ task.rspec_opts = '--color --format documentation'
13
13
  end
14
14
 
15
15
  # RuboCop task
16
16
  RuboCop::RakeTask.new do |task|
17
- task.options = ["--display-cop-names", "--display-style-guide"]
17
+ task.options = ['--display-cop-names', '--display-style-guide']
18
18
  end
19
19
 
20
20
  # Test with coverage
21
- desc "Run specs with coverage report"
21
+ desc 'Run specs with coverage report'
22
22
  task :spec_coverage do
23
23
  ENV['COVERAGE'] = 'true'
24
24
  Rake::Task[:spec].invoke
25
25
  end
26
26
 
27
27
  # Clean coverage files
28
- desc "Clean coverage files"
28
+ desc 'Clean coverage files'
29
29
  task :clean_coverage do
30
- FileUtils.rm_rf("coverage/")
30
+ FileUtils.rm_rf('coverage/')
31
31
  end
32
32
 
33
33
  # Build and install gem locally
34
- desc "Build and install gem locally"
34
+ desc 'Build and install gem locally'
35
35
  task :install_local do
36
- sh "gem build rcrewai.gemspec"
37
- sh "gem install rcrewai-*.gem"
38
- FileUtils.rm(Dir.glob("rcrewai-*.gem"))
36
+ sh 'gem build rcrewai.gemspec'
37
+ sh 'gem install rcrewai-*.gem'
38
+ FileUtils.rm(Dir.glob('rcrewai-*.gem'))
39
39
  end
40
40
 
41
41
  # Run all tests and checks
42
- desc "Run all tests and code quality checks"
42
+ desc 'Run all tests and code quality checks'
43
43
  task :ci do
44
- puts "Running RSpec tests..."
44
+ puts 'Running RSpec tests...'
45
45
  Rake::Task[:spec].invoke
46
-
46
+
47
47
  puts "\nRunning RuboCop..."
48
48
  Rake::Task[:rubocop].invoke
49
-
49
+
50
50
  puts "\nAll checks passed! ✅"
51
51
  end
52
52
 
53
53
  # Console task for development
54
- desc "Start interactive console with gem loaded"
54
+ desc 'Start interactive console with gem loaded'
55
55
  task :console do
56
- require "irb"
57
- require_relative "lib/rcrewai"
56
+ require 'irb'
57
+ require_relative 'lib/rcrewai'
58
58
  IRB.start
59
59
  end
60
60
 
61
61
  # Generate documentation
62
- desc "Generate documentation"
62
+ desc 'Generate documentation'
63
63
  task :docs do
64
- sh "yard doc lib/**/*.rb"
65
- puts "Documentation generated in doc/ directory"
64
+ sh 'yard doc lib/**/*.rb'
65
+ puts 'Documentation generated in doc/ directory'
66
66
  end
67
67
 
68
68
  # Benchmark task
69
- desc "Run performance benchmarks"
69
+ desc 'Run performance benchmarks'
70
70
  task :benchmark do
71
- require_relative "lib/rcrewai"
72
-
71
+ require_relative 'lib/rcrewai'
72
+
73
73
  # Simple benchmark example
74
- require "benchmark"
75
-
74
+ require 'benchmark'
75
+
76
76
  RCrewAI.configure do |config|
77
77
  config.llm_provider = :openai
78
- config.api_key = "test-key"
78
+ config.api_key = 'test-key'
79
79
  end
80
-
80
+
81
81
  agent = RCrewAI::Agent.new(
82
- name: "benchmark_agent",
83
- role: "Test Agent",
84
- goal: "Run benchmarks"
82
+ name: 'benchmark_agent',
83
+ role: 'Test Agent',
84
+ goal: 'Run benchmarks'
85
85
  )
86
-
86
+
87
87
  task = RCrewAI::Task.new(
88
- name: "benchmark_task",
89
- description: "Test task",
88
+ name: 'benchmark_task',
89
+ description: 'Test task',
90
90
  agent: agent
91
91
  )
92
-
93
- crew = RCrewAI::Crew.new("benchmark_crew")
92
+
93
+ crew = RCrewAI::Crew.new('benchmark_crew')
94
94
  crew.add_agent(agent)
95
95
  crew.add_task(task)
96
-
96
+
97
97
  puts "\nBenchmarking crew creation and setup..."
98
98
  result = Benchmark.measure do
99
99
  100.times do
100
100
  test_crew = RCrewAI::Crew.new("test_#{rand(1000)}")
101
101
  test_agent = RCrewAI::Agent.new(
102
- name: "test_agent",
103
- role: "Test Agent",
104
- goal: "Test performance"
102
+ name: 'test_agent',
103
+ role: 'Test Agent',
104
+ goal: 'Test performance'
105
105
  )
106
106
  test_crew.add_agent(test_agent)
107
107
  end
108
108
  end
109
-
109
+
110
110
  puts "Created 100 crews with agents in: #{result.real.round(4)}s"
111
111
  end
112
112
 
113
113
  # Development setup
114
- desc "Set up development environment"
114
+ desc 'Set up development environment'
115
115
  task :setup do
116
- puts "Setting up development environment..."
117
-
116
+ puts 'Setting up development environment...'
117
+
118
118
  # Install dependencies
119
- sh "bundle install"
120
-
119
+ sh 'bundle install'
120
+
121
121
  # Create necessary directories
122
- FileUtils.mkdir_p("spec/vcr_cassettes") unless Dir.exist?("spec/vcr_cassettes")
123
- FileUtils.mkdir_p("coverage") unless Dir.exist?("coverage")
124
- FileUtils.mkdir_p("doc") unless Dir.exist?("doc")
125
-
122
+ FileUtils.mkdir_p('spec/vcr_cassettes') unless Dir.exist?('spec/vcr_cassettes')
123
+ FileUtils.mkdir_p('coverage') unless Dir.exist?('coverage')
124
+ FileUtils.mkdir_p('doc') unless Dir.exist?('doc')
125
+
126
126
  puts "\n✅ Development environment set up successfully!"
127
127
  puts "\nRun 'rake spec' to run tests"
128
128
  puts "Run 'rake console' to start an interactive session"
129
129
  puts "Run 'rake ci' to run all checks"
130
- end
130
+ end