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.
- checksums.yaml +4 -4
- data/.rubocop.yml +21 -0
- data/.rubocop_todo.yml +99 -0
- data/CHANGELOG.md +64 -1
- data/README.md +170 -2
- data/ROADMAP.md +84 -0
- data/Rakefile +53 -53
- data/bin/rcrewai +3 -3
- data/docs/mcp.md +109 -0
- data/docs/superpowers/plans/2026-05-11-llm-modernization.md +2753 -0
- data/docs/superpowers/specs/2026-05-11-llm-modernization-design.md +479 -0
- data/docs/upgrading-to-0.3.md +163 -0
- data/examples/async_execution_example.rb +82 -81
- data/examples/hierarchical_crew_example.rb +68 -72
- data/examples/human_in_the_loop_example.rb +73 -74
- data/examples/mcp_example.rb +48 -0
- data/examples/native_tools_example.rb +64 -0
- data/examples/streaming_example.rb +56 -0
- data/lib/rcrewai/agent.rb +181 -286
- data/lib/rcrewai/async_executor.rb +43 -43
- data/lib/rcrewai/cli.rb +11 -11
- data/lib/rcrewai/configuration.rb +34 -9
- data/lib/rcrewai/crew.rb +134 -39
- data/lib/rcrewai/events.rb +30 -0
- data/lib/rcrewai/flow/state.rb +47 -0
- data/lib/rcrewai/flow/state_store.rb +50 -0
- data/lib/rcrewai/flow.rb +243 -0
- data/lib/rcrewai/human_input.rb +104 -114
- data/lib/rcrewai/knowledge/base.rb +52 -0
- data/lib/rcrewai/knowledge/chunker.rb +31 -0
- data/lib/rcrewai/knowledge/embedder.rb +48 -0
- data/lib/rcrewai/knowledge/sources.rb +83 -0
- data/lib/rcrewai/knowledge/store.rb +58 -0
- data/lib/rcrewai/knowledge.rb +13 -0
- data/lib/rcrewai/legacy_react_runner.rb +172 -0
- data/lib/rcrewai/llm_client.rb +24 -1
- data/lib/rcrewai/llm_clients/anthropic.rb +174 -54
- data/lib/rcrewai/llm_clients/azure.rb +23 -128
- data/lib/rcrewai/llm_clients/base.rb +11 -7
- data/lib/rcrewai/llm_clients/google.rb +159 -95
- data/lib/rcrewai/llm_clients/ollama.rb +150 -106
- data/lib/rcrewai/llm_clients/openai.rb +140 -63
- data/lib/rcrewai/mcp/client.rb +101 -0
- data/lib/rcrewai/mcp/tool_adapter.rb +59 -0
- data/lib/rcrewai/mcp/transport/http.rb +53 -0
- data/lib/rcrewai/mcp/transport/stdio.rb +55 -0
- data/lib/rcrewai/mcp.rb +8 -0
- data/lib/rcrewai/memory.rb +45 -37
- data/lib/rcrewai/output_schema.rb +79 -0
- data/lib/rcrewai/planning.rb +65 -0
- data/lib/rcrewai/pricing.rb +34 -0
- data/lib/rcrewai/process.rb +86 -95
- data/lib/rcrewai/provider_schema.rb +38 -0
- data/lib/rcrewai/sse_parser.rb +55 -0
- data/lib/rcrewai/task.rb +145 -66
- data/lib/rcrewai/tool_runner.rb +132 -0
- data/lib/rcrewai/tool_schema.rb +97 -0
- data/lib/rcrewai/tools/base.rb +98 -37
- data/lib/rcrewai/tools/code_executor.rb +71 -74
- data/lib/rcrewai/tools/email_sender.rb +70 -78
- data/lib/rcrewai/tools/file_reader.rb +38 -30
- data/lib/rcrewai/tools/file_writer.rb +40 -38
- data/lib/rcrewai/tools/pdf_processor.rb +115 -130
- data/lib/rcrewai/tools/sql_database.rb +58 -55
- data/lib/rcrewai/tools/web_search.rb +26 -25
- data/lib/rcrewai/version.rb +2 -2
- data/lib/rcrewai.rb +20 -10
- data/rcrewai.gemspec +39 -39
- metadata +77 -47
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f742fcf06518c80cbf29191d0eff275830ee618c9b896c3d443258b06aa120ff
|
|
4
|
+
data.tar.gz: 5cc9c03182d0bd80d73d99edb08f2267252684f64a23da1626d2d61e0abe1683
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
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
|
|
4
|
-
require
|
|
5
|
-
require
|
|
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: [
|
|
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 =
|
|
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 = [
|
|
17
|
+
task.options = ['--display-cop-names', '--display-style-guide']
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
# Test with coverage
|
|
21
|
-
desc
|
|
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
|
|
28
|
+
desc 'Clean coverage files'
|
|
29
29
|
task :clean_coverage do
|
|
30
|
-
FileUtils.rm_rf(
|
|
30
|
+
FileUtils.rm_rf('coverage/')
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
# Build and install gem locally
|
|
34
|
-
desc
|
|
34
|
+
desc 'Build and install gem locally'
|
|
35
35
|
task :install_local do
|
|
36
|
-
sh
|
|
37
|
-
sh
|
|
38
|
-
FileUtils.rm(Dir.glob(
|
|
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
|
|
42
|
+
desc 'Run all tests and code quality checks'
|
|
43
43
|
task :ci do
|
|
44
|
-
puts
|
|
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
|
|
54
|
+
desc 'Start interactive console with gem loaded'
|
|
55
55
|
task :console do
|
|
56
|
-
require
|
|
57
|
-
require_relative
|
|
56
|
+
require 'irb'
|
|
57
|
+
require_relative 'lib/rcrewai'
|
|
58
58
|
IRB.start
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
# Generate documentation
|
|
62
|
-
desc
|
|
62
|
+
desc 'Generate documentation'
|
|
63
63
|
task :docs do
|
|
64
|
-
sh
|
|
65
|
-
puts
|
|
64
|
+
sh 'yard doc lib/**/*.rb'
|
|
65
|
+
puts 'Documentation generated in doc/ directory'
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
# Benchmark task
|
|
69
|
-
desc
|
|
69
|
+
desc 'Run performance benchmarks'
|
|
70
70
|
task :benchmark do
|
|
71
|
-
require_relative
|
|
72
|
-
|
|
71
|
+
require_relative 'lib/rcrewai'
|
|
72
|
+
|
|
73
73
|
# Simple benchmark example
|
|
74
|
-
require
|
|
75
|
-
|
|
74
|
+
require 'benchmark'
|
|
75
|
+
|
|
76
76
|
RCrewAI.configure do |config|
|
|
77
77
|
config.llm_provider = :openai
|
|
78
|
-
config.api_key =
|
|
78
|
+
config.api_key = 'test-key'
|
|
79
79
|
end
|
|
80
|
-
|
|
80
|
+
|
|
81
81
|
agent = RCrewAI::Agent.new(
|
|
82
|
-
name:
|
|
83
|
-
role:
|
|
84
|
-
goal:
|
|
82
|
+
name: 'benchmark_agent',
|
|
83
|
+
role: 'Test Agent',
|
|
84
|
+
goal: 'Run benchmarks'
|
|
85
85
|
)
|
|
86
|
-
|
|
86
|
+
|
|
87
87
|
task = RCrewAI::Task.new(
|
|
88
|
-
name:
|
|
89
|
-
description:
|
|
88
|
+
name: 'benchmark_task',
|
|
89
|
+
description: 'Test task',
|
|
90
90
|
agent: agent
|
|
91
91
|
)
|
|
92
|
-
|
|
93
|
-
crew = RCrewAI::Crew.new(
|
|
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:
|
|
103
|
-
role:
|
|
104
|
-
goal:
|
|
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
|
|
114
|
+
desc 'Set up development environment'
|
|
115
115
|
task :setup do
|
|
116
|
-
puts
|
|
117
|
-
|
|
116
|
+
puts 'Setting up development environment...'
|
|
117
|
+
|
|
118
118
|
# Install dependencies
|
|
119
|
-
sh
|
|
120
|
-
|
|
119
|
+
sh 'bundle install'
|
|
120
|
+
|
|
121
121
|
# Create necessary directories
|
|
122
|
-
FileUtils.mkdir_p(
|
|
123
|
-
FileUtils.mkdir_p(
|
|
124
|
-
FileUtils.mkdir_p(
|
|
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
|