rcrewai 0.2.1 → 0.3.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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -0
  3. data/.rubocop_todo.yml +99 -0
  4. data/CHANGELOG.md +24 -0
  5. data/README.md +2 -2
  6. data/Rakefile +53 -53
  7. data/bin/rcrewai +3 -3
  8. data/docs/mcp.md +109 -0
  9. data/docs/superpowers/plans/2026-05-11-llm-modernization.md +2753 -0
  10. data/docs/superpowers/specs/2026-05-11-llm-modernization-design.md +479 -0
  11. data/docs/upgrading-to-0.3.md +163 -0
  12. data/examples/async_execution_example.rb +82 -81
  13. data/examples/hierarchical_crew_example.rb +68 -72
  14. data/examples/human_in_the_loop_example.rb +73 -74
  15. data/examples/mcp_example.rb +48 -0
  16. data/examples/native_tools_example.rb +64 -0
  17. data/examples/streaming_example.rb +56 -0
  18. data/lib/rcrewai/agent.rb +148 -287
  19. data/lib/rcrewai/async_executor.rb +43 -43
  20. data/lib/rcrewai/cli.rb +11 -11
  21. data/lib/rcrewai/configuration.rb +14 -9
  22. data/lib/rcrewai/crew.rb +56 -39
  23. data/lib/rcrewai/events.rb +30 -0
  24. data/lib/rcrewai/human_input.rb +104 -114
  25. data/lib/rcrewai/legacy_react_runner.rb +172 -0
  26. data/lib/rcrewai/llm_client.rb +1 -1
  27. data/lib/rcrewai/llm_clients/anthropic.rb +174 -54
  28. data/lib/rcrewai/llm_clients/azure.rb +23 -128
  29. data/lib/rcrewai/llm_clients/base.rb +11 -7
  30. data/lib/rcrewai/llm_clients/google.rb +159 -95
  31. data/lib/rcrewai/llm_clients/ollama.rb +150 -106
  32. data/lib/rcrewai/llm_clients/openai.rb +140 -63
  33. data/lib/rcrewai/mcp/client.rb +101 -0
  34. data/lib/rcrewai/mcp/tool_adapter.rb +59 -0
  35. data/lib/rcrewai/mcp/transport/http.rb +53 -0
  36. data/lib/rcrewai/mcp/transport/stdio.rb +55 -0
  37. data/lib/rcrewai/mcp.rb +8 -0
  38. data/lib/rcrewai/memory.rb +45 -37
  39. data/lib/rcrewai/pricing.rb +34 -0
  40. data/lib/rcrewai/process.rb +86 -95
  41. data/lib/rcrewai/provider_schema.rb +38 -0
  42. data/lib/rcrewai/sse_parser.rb +55 -0
  43. data/lib/rcrewai/task.rb +56 -64
  44. data/lib/rcrewai/tool_runner.rb +132 -0
  45. data/lib/rcrewai/tool_schema.rb +97 -0
  46. data/lib/rcrewai/tools/base.rb +98 -37
  47. data/lib/rcrewai/tools/code_executor.rb +71 -74
  48. data/lib/rcrewai/tools/email_sender.rb +70 -78
  49. data/lib/rcrewai/tools/file_reader.rb +38 -30
  50. data/lib/rcrewai/tools/file_writer.rb +40 -38
  51. data/lib/rcrewai/tools/pdf_processor.rb +115 -130
  52. data/lib/rcrewai/tools/sql_database.rb +58 -55
  53. data/lib/rcrewai/tools/web_search.rb +26 -25
  54. data/lib/rcrewai/version.rb +2 -2
  55. data/lib/rcrewai.rb +18 -10
  56. data/rcrewai.gemspec +39 -39
  57. metadata +65 -47
@@ -1,39 +1,40 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative '../lib/rcrewai'
4
5
 
5
- puts "🤝 Human-in-the-Loop Example"
6
- puts "=" * 50
6
+ puts '🤝 Human-in-the-Loop Example'
7
+ puts '=' * 50
7
8
 
8
9
  # Configure RCrewAI
9
10
  RCrewAI.configure do |config|
10
- config.llm_provider = :openai # Change as needed
11
+ config.llm_provider = :openai # Change as needed
11
12
  config.temperature = 0.1
12
13
  end
13
14
 
14
- puts "🔧 Setting up crew with human interaction capabilities..."
15
+ puts '🔧 Setting up crew with human interaction capabilities...'
15
16
 
16
17
  # Create crew
17
- crew = RCrewAI::Crew.new("human_assisted_crew")
18
+ crew = RCrewAI::Crew.new('human_assisted_crew')
18
19
 
19
20
  # Create agents with human input capabilities
20
21
  research_agent = RCrewAI::Agent.new(
21
- name: "research_assistant",
22
- role: "Research Specialist",
23
- goal: "Conduct thorough research with human oversight",
24
- backstory: "You work closely with humans to ensure research quality and accuracy.",
22
+ name: 'research_assistant',
23
+ role: 'Research Specialist',
24
+ goal: 'Conduct thorough research with human oversight',
25
+ backstory: 'You work closely with humans to ensure research quality and accuracy.',
25
26
  tools: [RCrewAI::Tools::WebSearch.new, RCrewAI::Tools::FileWriter.new],
26
27
  verbose: true,
27
- human_input: true, # Enable human input
28
- require_approval_for_tools: true, # Require approval for tool usage
29
- require_approval_for_final_answer: true # Require approval for final answers
28
+ human_input: true, # Enable human input
29
+ require_approval_for_tools: true, # Require approval for tool usage
30
+ require_approval_for_final_answer: true # Require approval for final answers
30
31
  )
31
32
 
32
33
  content_creator = RCrewAI::Agent.new(
33
- name: "content_creator",
34
- role: "Content Writer",
35
- goal: "Create engaging content with human guidance",
36
- backstory: "You collaborate with humans to create compelling, accurate content.",
34
+ name: 'content_creator',
35
+ role: 'Content Writer',
36
+ goal: 'Create engaging content with human guidance',
37
+ backstory: 'You collaborate with humans to create compelling, accurate content.',
37
38
  tools: [RCrewAI::Tools::FileWriter.new, RCrewAI::Tools::FileReader.new],
38
39
  verbose: true,
39
40
  human_input: true,
@@ -41,10 +42,10 @@ content_creator = RCrewAI::Agent.new(
41
42
  )
42
43
 
43
44
  quality_checker = RCrewAI::Agent.new(
44
- name: "quality_checker",
45
- role: "Quality Assurance Specialist",
46
- goal: "Ensure content meets quality standards",
47
- backstory: "You work with humans to maintain high quality standards.",
45
+ name: 'quality_checker',
46
+ role: 'Quality Assurance Specialist',
47
+ goal: 'Ensure content meets quality standards',
48
+ backstory: 'You work with humans to maintain high quality standards.',
48
49
  tools: [RCrewAI::Tools::FileReader.new],
49
50
  verbose: true,
50
51
  human_input: true
@@ -61,22 +62,22 @@ puts "👥 Created crew with #{crew.agents.length} agents (all human-enabled)"
61
62
 
62
63
  # Task 1: Research with human confirmation and guidance
63
64
  research_task = RCrewAI::Task.new(
64
- name: "research_ai_trends",
65
- description: "Research the latest AI trends for 2024, focusing on practical business applications",
65
+ name: 'research_ai_trends',
66
+ description: 'Research the latest AI trends for 2024, focusing on practical business applications',
66
67
  agent: research_agent,
67
- expected_output: "Comprehensive research document saved as ai_trends_2024.md",
68
+ expected_output: 'Comprehensive research document saved as ai_trends_2024.md',
68
69
  human_input: true,
69
70
  require_confirmation: true, # Human must confirm before starting
70
71
  allow_guidance: true, # Allow human to provide guidance during execution
71
- human_review_points: [:completion] # Review when completed
72
+ human_review_points: [:completion] # Review when completed
72
73
  )
73
74
 
74
75
  # Task 2: Content creation with human oversight
75
76
  content_task = RCrewAI::Task.new(
76
- name: "create_ai_article",
77
- description: "Write an engaging article based on the research findings about AI trends",
77
+ name: 'create_ai_article',
78
+ description: 'Write an engaging article based on the research findings about AI trends',
78
79
  agent: content_creator,
79
- expected_output: "Well-structured article saved as ai_trends_article.md",
80
+ expected_output: 'Well-structured article saved as ai_trends_article.md',
80
81
  context: [research_task],
81
82
  human_input: true,
82
83
  allow_guidance: true,
@@ -85,10 +86,10 @@ content_task = RCrewAI::Task.new(
85
86
 
86
87
  # Task 3: Quality check with human review
87
88
  quality_task = RCrewAI::Task.new(
88
- name: "quality_review",
89
- description: "Review the article for accuracy, clarity, and engagement",
89
+ name: 'quality_review',
90
+ description: 'Review the article for accuracy, clarity, and engagement',
90
91
  agent: quality_checker,
91
- expected_output: "Quality assessment report with recommendations",
92
+ expected_output: 'Quality assessment report with recommendations',
92
93
  context: [content_task],
93
94
  human_input: true,
94
95
  human_review_points: [:completion]
@@ -102,18 +103,18 @@ puts "📋 Created #{crew.tasks.length} tasks with human interaction points"
102
103
 
103
104
  # Demonstrate different human interaction modes
104
105
  puts "\n🎯 HUMAN-IN-THE-LOOP EXECUTION MODES"
105
- puts "-" * 40
106
+ puts '-' * 40
106
107
 
107
108
  puts "\n1️⃣ BASIC HUMAN APPROVAL MODE"
108
- puts "Tasks will request human approval at key decision points..."
109
+ puts 'Tasks will request human approval at key decision points...'
109
110
 
110
111
  begin
111
112
  results = crew.execute
112
-
113
+
113
114
  puts "\n📊 Execution Results:"
114
115
  puts " Completed: #{results[:completed_tasks]}/#{results[:total_tasks]}"
115
116
  puts " Success Rate: #{results[:success_rate]}%"
116
-
117
+
117
118
  # Show any generated files
118
119
  output_files = ['ai_trends_2024.md', 'ai_trends_article.md']
119
120
  puts "\n📄 Generated Files:"
@@ -124,20 +125,19 @@ begin
124
125
  puts " ❌ #{file} (not created)"
125
126
  end
126
127
  end
127
-
128
- rescue => e
128
+ rescue StandardError => e
129
129
  puts "❌ Execution failed or was cancelled: #{e.message}"
130
130
  end
131
131
 
132
132
  puts "\n2️⃣ AGENT-LEVEL HUMAN INTERACTION DEMONSTRATION"
133
- puts "Creating a standalone task to show detailed human interactions..."
133
+ puts 'Creating a standalone task to show detailed human interactions...'
134
134
 
135
135
  # Create a simple demonstration task
136
136
  demo_agent = RCrewAI::Agent.new(
137
- name: "demo_agent",
138
- role: "Demonstration Agent",
139
- goal: "Show human interaction capabilities",
140
- backstory: "I demonstrate various human interaction patterns.",
137
+ name: 'demo_agent',
138
+ role: 'Demonstration Agent',
139
+ goal: 'Show human interaction capabilities',
140
+ backstory: 'I demonstrate various human interaction patterns.',
141
141
  tools: [RCrewAI::Tools::FileWriter.new],
142
142
  verbose: true
143
143
  )
@@ -149,10 +149,10 @@ demo_agent.enable_human_input(
149
149
  )
150
150
 
151
151
  demo_task = RCrewAI::Task.new(
152
- name: "human_interaction_demo",
153
- description: "Write a short summary of Ruby programming benefits and save it to ruby_benefits.txt",
152
+ name: 'human_interaction_demo',
153
+ description: 'Write a short summary of Ruby programming benefits and save it to ruby_benefits.txt',
154
154
  agent: demo_agent,
155
- expected_output: "A text file containing Ruby programming benefits",
155
+ expected_output: 'A text file containing Ruby programming benefits',
156
156
  human_input: true,
157
157
  require_confirmation: true,
158
158
  allow_guidance: true
@@ -163,27 +163,26 @@ puts "\n🚀 Starting human interaction demonstration..."
163
163
  begin
164
164
  demo_result = demo_task.execute
165
165
  puts "\nDemo result: #{demo_result}"
166
-
166
+
167
167
  if File.exist?('ruby_benefits.txt')
168
168
  puts "\n📄 Generated demo file:"
169
- puts File.read('ruby_benefits.txt')[0..200] + "..."
169
+ puts "#{File.read('ruby_benefits.txt')[0..200]}..."
170
170
  end
171
-
172
- rescue => e
171
+ rescue StandardError => e
173
172
  puts "Demo task result: #{e.message}"
174
173
  end
175
174
 
176
175
  puts "\n3️⃣ HUMAN INPUT UTILITY DEMONSTRATION"
177
- puts "Testing different types of human input requests..."
176
+ puts 'Testing different types of human input requests...'
178
177
 
179
178
  # Demonstrate the HumanInput utility directly
180
179
  human_input = RCrewAI::HumanInput.new(verbose: true)
181
180
 
182
181
  puts "\nTesting approval request..."
183
182
  approval_result = human_input.request_approval(
184
- "Test approval request - this is just a demonstration",
185
- context: "This is a test of the human input system",
186
- consequences: "Nothing will actually happen",
183
+ 'Test approval request - this is just a demonstration',
184
+ context: 'This is a test of the human input system',
185
+ consequences: 'Nothing will actually happen',
187
186
  timeout: 30
188
187
  )
189
188
  puts "Approval result: #{approval_result[:approved] ? 'APPROVED' : 'REJECTED'}"
@@ -191,15 +190,15 @@ puts "Approval result: #{approval_result[:approved] ? 'APPROVED' : 'REJECTED'}"
191
190
  puts "\nTesting choice request..."
192
191
  choice_result = human_input.request_choice(
193
192
  "What's your preferred programming language?",
194
- ["Ruby", "Python", "JavaScript", "Other"],
193
+ %w[Ruby Python JavaScript Other],
195
194
  timeout: 30
196
195
  )
197
196
  puts "Choice result: #{choice_result[:choice]}" if choice_result[:valid]
198
197
 
199
198
  puts "\nTesting input request..."
200
199
  input_result = human_input.request_input(
201
- "Enter a short message (or press Enter to skip):",
202
- help_text: "This is just for testing the input system",
200
+ 'Enter a short message (or press Enter to skip):',
201
+ help_text: 'This is just for testing the input system',
203
202
  timeout: 20
204
203
  )
205
204
  puts "Input result: '#{input_result[:input]}'" if input_result[:valid]
@@ -210,24 +209,24 @@ summary = human_input.session_summary
210
209
  summary.each { |key, value| puts " #{key}: #{value}" }
211
210
 
212
211
  puts "\n🎯 HUMAN-IN-THE-LOOP FEATURES DEMONSTRATED:"
213
- puts " • Task-level human confirmation before execution"
214
- puts " • Agent-level tool approval workflows"
215
- puts " • Final answer review and revision capabilities"
216
- puts " • Human guidance integration into agent reasoning"
217
- puts " • Task completion review and feedback handling"
218
- puts " • Error handling with human intervention options"
219
- puts " • Flexible human input types (approval, choice, input, review)"
220
- puts " • Session tracking and interaction history"
221
- puts " • Configurable timeouts and auto-approval modes"
222
- puts " • Integration with both sync and async execution"
212
+ puts ' • Task-level human confirmation before execution'
213
+ puts ' • Agent-level tool approval workflows'
214
+ puts ' • Final answer review and revision capabilities'
215
+ puts ' • Human guidance integration into agent reasoning'
216
+ puts ' • Task completion review and feedback handling'
217
+ puts ' • Error handling with human intervention options'
218
+ puts ' • Flexible human input types (approval, choice, input, review)'
219
+ puts ' • Session tracking and interaction history'
220
+ puts ' • Configurable timeouts and auto-approval modes'
221
+ puts ' • Integration with both sync and async execution'
223
222
 
224
223
  puts "\n💡 USAGE PATTERNS:"
225
- puts " 1. Development & Testing: Use human approval for tool usage"
226
- puts " 2. Content Creation: Human review of final outputs"
227
- puts " 3. Critical Tasks: Human confirmation before execution"
228
- puts " 4. Learning & Training: Human guidance during reasoning"
229
- puts " 5. Quality Assurance: Human review at completion points"
230
-
231
- puts "\n" + "=" * 50
232
- puts "🤝 Human-in-the-Loop Demo Complete!"
233
- puts "=" * 50
224
+ puts ' 1. Development & Testing: Use human approval for tool usage'
225
+ puts ' 2. Content Creation: Human review of final outputs'
226
+ puts ' 3. Critical Tasks: Human confirmation before execution'
227
+ puts ' 4. Learning & Training: Human guidance during reasoning'
228
+ puts ' 5. Quality Assurance: Human review at completion points'
229
+
230
+ puts "\n#{'=' * 50}"
231
+ puts '🤝 Human-in-the-Loop Demo Complete!'
232
+ puts '=' * 50
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Connect to an MCP server over stdio and expose its tools to an agent.
5
+ #
6
+ # Prerequisites:
7
+ # # The "filesystem" reference MCP server (Node):
8
+ # npm install -g @modelcontextprotocol/server-filesystem
9
+ # # or run it ad-hoc with npx (no global install):
10
+ # # command: "npx", args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
11
+ #
12
+ # Run:
13
+ # OPENAI_API_KEY=... ruby examples/mcp_example.rb
14
+
15
+ require_relative '../lib/rcrewai'
16
+
17
+ RCrewAI.configure do |c|
18
+ c.llm_provider = :openai
19
+ c.openai_model = 'gpt-4o-mini'
20
+ end
21
+
22
+ RCrewAI::MCP::Client.with_connection(
23
+ command: 'npx',
24
+ args: ['-y', '@modelcontextprotocol/server-filesystem', '/tmp']
25
+ ) do |client|
26
+ puts "Connected to MCP server: #{client.server_name}"
27
+ puts 'Available tools:'
28
+ client.tools.each { |t| puts " - #{t.name}: #{t.description}" }
29
+ puts
30
+
31
+ agent = RCrewAI::Agent.new(
32
+ name: 'fs_agent',
33
+ role: 'Filesystem operator',
34
+ goal: 'Read and summarize files using the filesystem tools',
35
+ tools: client.tools
36
+ )
37
+
38
+ task = RCrewAI::Task.new(
39
+ name: 'list_tmp',
40
+ description: 'List the files in /tmp and tell me how many there are.',
41
+ agent: agent,
42
+ expected_output: 'A short summary'
43
+ )
44
+
45
+ result = agent.execute_task(task)
46
+ puts "Answer: #{result[:content]}"
47
+ puts "Tool calls: #{result[:tool_calls_history].length}"
48
+ end
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Native function calling end-to-end.
5
+ #
6
+ # A tool declares a JSON schema via the DSL on Tools::Base. The OpenAI
7
+ # client sends that schema as `tools:` in the request payload, the model
8
+ # replies with a `tool_calls` block, and ToolRunner dispatches the call,
9
+ # threads the result back into the conversation, and continues until the
10
+ # model is done.
11
+ #
12
+ # Run:
13
+ # OPENAI_API_KEY=... ruby examples/native_tools_example.rb
14
+
15
+ require_relative '../lib/rcrewai'
16
+
17
+ # 1. Configure the LLM.
18
+ RCrewAI.configure do |c|
19
+ c.llm_provider = :openai
20
+ c.openai_model = 'gpt-4o-mini'
21
+ end
22
+
23
+ # 2. Declare a tool with the DSL.
24
+ class WeatherTool < RCrewAI::Tools::Base
25
+ tool_name 'get_weather'
26
+ description 'Get the current weather for a city'
27
+ param :city, type: :string, required: true, description: 'City name'
28
+ param :units, type: :enum, values: %w[metric imperial], default: 'metric'
29
+
30
+ def execute(city:, units: 'metric')
31
+ # In a real tool this would call a weather API. We fake it for demo.
32
+ {
33
+ city: city,
34
+ temperature: units == 'metric' ? 22 : 72,
35
+ conditions: 'sunny'
36
+ }
37
+ end
38
+ end
39
+
40
+ # 3. Build an agent that has the tool.
41
+ agent = RCrewAI::Agent.new(
42
+ name: 'meteorologist',
43
+ role: 'Helpful weather assistant',
44
+ goal: 'Answer weather questions accurately',
45
+ tools: [WeatherTool.new]
46
+ )
47
+
48
+ # 4. Define and execute a task.
49
+ task = RCrewAI::Task.new(
50
+ name: 'weather_check',
51
+ description: "What's the weather in Tokyo?",
52
+ agent: agent,
53
+ expected_output: 'A short sentence about the weather'
54
+ )
55
+
56
+ result = agent.execute_task(task)
57
+
58
+ puts "Answer: #{result[:content]}"
59
+ puts 'Tool calls:'
60
+ result[:tool_calls_history].each do |tc|
61
+ puts " - #{tc[:tool]}(#{tc[:args]}) -> #{tc[:result]}"
62
+ end
63
+ puts "Tokens: #{result.dig(:usage, :total_tokens)}"
64
+ puts "Finish: #{result[:finish_reason]}"
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Streaming events from agent.execute_task.
5
+ #
6
+ # Multiple sinks can subscribe to the same stream: one prints tokens as
7
+ # they arrive; another tallies cost from Events::Usage. The sinks are
8
+ # fanned out independently — exceptions in one don't kill the others.
9
+ #
10
+ # Run:
11
+ # OPENAI_API_KEY=... ruby examples/streaming_example.rb
12
+
13
+ require_relative '../lib/rcrewai'
14
+
15
+ RCrewAI.configure do |c|
16
+ c.llm_provider = :openai
17
+ c.openai_model = 'gpt-4o-mini'
18
+ end
19
+
20
+ agent = RCrewAI::Agent.new(
21
+ name: 'storyteller',
22
+ role: 'Short-story author',
23
+ goal: 'Tell concise, vivid stories'
24
+ )
25
+ task = RCrewAI::Task.new(
26
+ name: 'tell_story',
27
+ description: 'Tell me a 3-sentence story about a robot and a cat.',
28
+ agent: agent,
29
+ expected_output: 'A 3-sentence story'
30
+ )
31
+
32
+ # Sink 1: print tokens as they arrive.
33
+ printer = lambda do |event|
34
+ print event.text if event.is_a?(RCrewAI::Events::TextDelta)
35
+ $stdout.flush
36
+ end
37
+
38
+ # Sink 2: tally cost and tokens.
39
+ total_cost = 0.0
40
+ total_tokens = 0
41
+ cost_tracker = lambda do |event|
42
+ next unless event.is_a?(RCrewAI::Events::Usage)
43
+
44
+ total_cost += event.cost_usd || 0
45
+ total_tokens += event.total_tokens || 0
46
+ end
47
+
48
+ # Combine sinks (each event flows to both):
49
+ fan = RCrewAI::Events.fan_out([printer, cost_tracker])
50
+
51
+ agent.execute_task(task, stream: fan)
52
+
53
+ puts
54
+ puts '---'
55
+ puts "Total tokens: #{total_tokens}"
56
+ puts "Total cost: $#{format('%.4f', total_cost)}"