rcrewai 0.7.1 → 0.8.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/CHANGELOG.md +41 -1
- data/ROADMAP.md +144 -75
- data/lib/rcrewai/checkpoint/cli.rb +98 -0
- data/lib/rcrewai/checkpoint.rb +145 -0
- data/lib/rcrewai/cli.rb +12 -1
- data/lib/rcrewai/configuration.rb +7 -0
- data/lib/rcrewai/crew.rb +82 -2
- data/lib/rcrewai/events.rb +71 -5
- data/lib/rcrewai/legacy_react_runner.rb +14 -9
- data/lib/rcrewai/llm_client.rb +20 -15
- data/lib/rcrewai/llm_clients/anthropic.rb +13 -4
- data/lib/rcrewai/llm_clients/azure.rb +2 -2
- data/lib/rcrewai/llm_clients/base.rb +55 -1
- data/lib/rcrewai/llm_clients/bedrock.rb +134 -0
- data/lib/rcrewai/llm_clients/google.rb +13 -4
- data/lib/rcrewai/llm_clients/ollama.rb +18 -4
- data/lib/rcrewai/llm_clients/openai.rb +19 -15
- data/lib/rcrewai/llm_clients/openai_compatible.rb +34 -0
- data/lib/rcrewai/llm_clients/openai_responses.rb +127 -0
- data/lib/rcrewai/llm_clients/snowflake_cortex.rb +42 -0
- data/lib/rcrewai/process.rb +39 -0
- data/lib/rcrewai/tool_runner.rb +16 -10
- data/lib/rcrewai/version.rb +1 -1
- data/lib/rcrewai.rb +3 -0
- metadata +7 -1
|
@@ -21,8 +21,8 @@ module RCrewAI
|
|
|
21
21
|
firefunction-v2
|
|
22
22
|
].freeze
|
|
23
23
|
|
|
24
|
-
def initialize(config = RCrewAI.configuration)
|
|
25
|
-
super
|
|
24
|
+
def initialize(config = RCrewAI.configuration, **hooks)
|
|
25
|
+
super(config, **hooks)
|
|
26
26
|
@base_url = config.base_url || ollama_url || DEFAULT_URL
|
|
27
27
|
end
|
|
28
28
|
|
|
@@ -78,17 +78,25 @@ module RCrewAI
|
|
|
78
78
|
handle_response(response)
|
|
79
79
|
end
|
|
80
80
|
|
|
81
|
+
def provider_name
|
|
82
|
+
:ollama
|
|
83
|
+
end
|
|
84
|
+
|
|
81
85
|
private
|
|
82
86
|
|
|
83
87
|
def plain_chat(url, payload)
|
|
88
|
+
payload = apply_before_request(payload)
|
|
89
|
+
started_at = Time.now
|
|
84
90
|
log_request(:post, url, payload)
|
|
85
91
|
response = http_client.post(url, payload, build_headers)
|
|
86
92
|
log_response(response)
|
|
87
93
|
body = handle_response(response)
|
|
88
|
-
normalize_non_streaming(body)
|
|
94
|
+
apply_after_response(normalize_non_streaming(body), started_at)
|
|
89
95
|
end
|
|
90
96
|
|
|
91
97
|
def stream_chat(url, payload, sink)
|
|
98
|
+
payload = apply_before_request(payload)
|
|
99
|
+
started_at = Time.now
|
|
92
100
|
log_request(:post, url, payload)
|
|
93
101
|
|
|
94
102
|
assembled_text = +''
|
|
@@ -145,8 +153,14 @@ module RCrewAI
|
|
|
145
153
|
))
|
|
146
154
|
end
|
|
147
155
|
|
|
156
|
+
result = stream_result(assembled_text, tool_calls, finish_reason,
|
|
157
|
+
prompt_tokens, completion_tokens)
|
|
158
|
+
apply_after_response(result, started_at)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def stream_result(text, tool_calls, finish_reason, prompt_tokens, completion_tokens)
|
|
148
162
|
{
|
|
149
|
-
content:
|
|
163
|
+
content: text.empty? ? nil : text,
|
|
150
164
|
tool_calls: tool_calls,
|
|
151
165
|
usage: {
|
|
152
166
|
prompt_tokens: prompt_tokens,
|
|
@@ -13,8 +13,8 @@ module RCrewAI
|
|
|
13
13
|
class OpenAI < Base
|
|
14
14
|
BASE_URL = 'https://api.openai.com/v1'
|
|
15
15
|
|
|
16
|
-
def initialize(config = RCrewAI.configuration)
|
|
17
|
-
super
|
|
16
|
+
def initialize(config = RCrewAI.configuration, **hooks)
|
|
17
|
+
super(config, **hooks)
|
|
18
18
|
@base_url = BASE_URL
|
|
19
19
|
end
|
|
20
20
|
|
|
@@ -49,6 +49,10 @@ module RCrewAI
|
|
|
49
49
|
true
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
+
def provider_name
|
|
53
|
+
:openai
|
|
54
|
+
end
|
|
55
|
+
|
|
52
56
|
def models
|
|
53
57
|
url = "#{@base_url}/models"
|
|
54
58
|
response = http_client.get(url, {}, build_headers.merge(auth_header))
|
|
@@ -64,15 +68,19 @@ module RCrewAI
|
|
|
64
68
|
|
|
65
69
|
def plain_chat(payload)
|
|
66
70
|
url = chat_url
|
|
71
|
+
payload = apply_before_request(payload)
|
|
72
|
+
started_at = Time.now
|
|
67
73
|
log_request(:post, url, payload)
|
|
68
74
|
response = http_client.post(url, payload, build_headers.merge(auth_header))
|
|
69
75
|
log_response(response)
|
|
70
76
|
body = handle_response(response)
|
|
71
|
-
normalize_non_streaming(body)
|
|
77
|
+
apply_after_response(normalize_non_streaming(body), started_at)
|
|
72
78
|
end
|
|
73
79
|
|
|
74
80
|
def stream_chat(payload, sink) # rubocop:disable Metrics/AbcSize
|
|
75
81
|
url = chat_url
|
|
82
|
+
payload = apply_before_request(payload)
|
|
83
|
+
started_at = Time.now
|
|
76
84
|
log_request(:post, url, payload)
|
|
77
85
|
|
|
78
86
|
assembled_text = +''
|
|
@@ -137,18 +145,14 @@ module RCrewAI
|
|
|
137
145
|
))
|
|
138
146
|
end
|
|
139
147
|
|
|
140
|
-
{
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
def provider_name
|
|
151
|
-
:openai
|
|
148
|
+
apply_after_response({
|
|
149
|
+
content: assembled_text.empty? ? nil : assembled_text,
|
|
150
|
+
tool_calls: tool_calls,
|
|
151
|
+
usage: final_usage || {},
|
|
152
|
+
finish_reason: finish_reason || :stop,
|
|
153
|
+
model: config.model,
|
|
154
|
+
provider: provider_name
|
|
155
|
+
}, started_at)
|
|
152
156
|
end
|
|
153
157
|
|
|
154
158
|
def streaming_post(url, payload, &on_chunk)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'openai'
|
|
4
|
+
|
|
5
|
+
module RCrewAI
|
|
6
|
+
module LLMClients
|
|
7
|
+
# Any endpoint speaking the OpenAI Chat Completions wire format: Together,
|
|
8
|
+
# Groq, Fireworks, vLLM, LiteLLM, OpenRouter, a self-hosted gateway. Only
|
|
9
|
+
# the base URL differs, so this is OpenAI with the endpoint made explicit
|
|
10
|
+
# and required.
|
|
11
|
+
class OpenAICompatible < OpenAI
|
|
12
|
+
def initialize(config = RCrewAI.configuration, **hooks)
|
|
13
|
+
super
|
|
14
|
+
@base_url = config.base_url.chomp('/')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def provider_name
|
|
18
|
+
:openai_compatible
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def validate_config!
|
|
24
|
+
raise ConfigurationError, 'API key is required' unless config.api_key
|
|
25
|
+
raise ConfigurationError, 'A base url is required for an OpenAI-compatible provider' unless config.base_url
|
|
26
|
+
raise ConfigurationError, 'Model is required' unless config.model
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def auth_header
|
|
30
|
+
{ 'Authorization' => "Bearer #{config.api_key}" }
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'openai'
|
|
4
|
+
|
|
5
|
+
module RCrewAI
|
|
6
|
+
module LLMClients
|
|
7
|
+
# OpenAI's Responses API. Same host and auth as Chat Completions, but a
|
|
8
|
+
# different request and response shape:
|
|
9
|
+
#
|
|
10
|
+
# - messages go under :input, and the system prompt under :instructions
|
|
11
|
+
# - :max_tokens becomes :max_output_tokens
|
|
12
|
+
# - the reply is an :output array of typed items (message, function_call,
|
|
13
|
+
# reasoning, ...) rather than a single choice
|
|
14
|
+
# - usage is input_tokens / output_tokens
|
|
15
|
+
#
|
|
16
|
+
# Only the non-streaming path is implemented; Responses streams a distinct
|
|
17
|
+
# set of semantic events that the SSE assembly here does not model.
|
|
18
|
+
class OpenAIResponses < OpenAI
|
|
19
|
+
def provider_name
|
|
20
|
+
:openai_responses
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def chat(messages:, tools: nil, tool_choice: :auto, stream: nil, **options) # rubocop:disable Lint/UnusedMethodArgument
|
|
24
|
+
payload = {
|
|
25
|
+
model: config.model,
|
|
26
|
+
input: format_input(messages),
|
|
27
|
+
temperature: options[:temperature] || config.temperature,
|
|
28
|
+
max_output_tokens: options[:max_tokens] || config.max_tokens
|
|
29
|
+
}.compact
|
|
30
|
+
|
|
31
|
+
instructions = extract_instructions(messages)
|
|
32
|
+
payload[:instructions] = instructions if instructions
|
|
33
|
+
|
|
34
|
+
if tools && !tools.empty?
|
|
35
|
+
payload[:tools] = format_tools(tools)
|
|
36
|
+
payload[:tool_choice] = tool_choice if tool_choice != :auto
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
plain_chat(payload)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def chat_url
|
|
45
|
+
"#{@base_url}/responses"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Responses carries the system prompt out-of-band as :instructions.
|
|
49
|
+
def extract_instructions(messages)
|
|
50
|
+
systems = messages.select { |m| m.is_a?(Hash) && m[:role].to_s == 'system' }
|
|
51
|
+
return nil if systems.empty?
|
|
52
|
+
|
|
53
|
+
systems.map { |m| m[:content] }.join("\n\n")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def format_input(messages)
|
|
57
|
+
messages.reject { |m| m.is_a?(Hash) && m[:role].to_s == 'system' }
|
|
58
|
+
.map { |m| { role: m[:role].to_s, content: m[:content] } }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Responses flattens the function definition instead of nesting it under
|
|
62
|
+
# a :function key the way Chat Completions does.
|
|
63
|
+
def format_tools(tools)
|
|
64
|
+
tools.map do |t|
|
|
65
|
+
{ type: 'function', name: t[:name], description: t[:description],
|
|
66
|
+
parameters: t[:parameters] }
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def normalize_non_streaming(body)
|
|
71
|
+
output = Array(body['output'])
|
|
72
|
+
text = extract_text(output)
|
|
73
|
+
tool_calls = extract_tool_calls(output)
|
|
74
|
+
|
|
75
|
+
{
|
|
76
|
+
content: text.empty? ? nil : text,
|
|
77
|
+
tool_calls: tool_calls,
|
|
78
|
+
usage: {
|
|
79
|
+
prompt_tokens: body.dig('usage', 'input_tokens'),
|
|
80
|
+
completion_tokens: body.dig('usage', 'output_tokens'),
|
|
81
|
+
total_tokens: body.dig('usage', 'total_tokens')
|
|
82
|
+
},
|
|
83
|
+
finish_reason: finish_reason_for(body, tool_calls),
|
|
84
|
+
model: body['model'] || config.model,
|
|
85
|
+
provider: provider_name
|
|
86
|
+
}
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def extract_text(output)
|
|
90
|
+
output.select { |item| item['type'] == 'message' }
|
|
91
|
+
.flat_map { |item| Array(item['content']) }
|
|
92
|
+
.select { |part| part['type'] == 'output_text' }
|
|
93
|
+
.map { |part| part['text'] }
|
|
94
|
+
.join
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def extract_tool_calls(output)
|
|
98
|
+
output.select { |item| item['type'] == 'function_call' }.map do |item|
|
|
99
|
+
{
|
|
100
|
+
id: item['call_id'] || item['id'],
|
|
101
|
+
name: item['name'],
|
|
102
|
+
arguments: parse_arguments(item['arguments'])
|
|
103
|
+
}
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def parse_arguments(raw)
|
|
108
|
+
return {} if raw.nil? || raw.empty?
|
|
109
|
+
|
|
110
|
+
JSON.parse(raw)
|
|
111
|
+
rescue JSON::ParserError
|
|
112
|
+
{}
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def finish_reason_for(body, tool_calls)
|
|
116
|
+
return :tool_calls if tool_calls.any?
|
|
117
|
+
|
|
118
|
+
if body['status'] == 'incomplete'
|
|
119
|
+
reason = body.dig('incomplete_details', 'reason')
|
|
120
|
+
return reason == 'max_output_tokens' ? :length : :incomplete
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
:stop
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'openai'
|
|
4
|
+
|
|
5
|
+
module RCrewAI
|
|
6
|
+
module LLMClients
|
|
7
|
+
# Snowflake Cortex inference. The request/response bodies follow the
|
|
8
|
+
# OpenAI Chat Completions shape, so only the endpoint and the auth headers
|
|
9
|
+
# differ -- Cortex wants the token type declared alongside the bearer
|
|
10
|
+
# token.
|
|
11
|
+
class SnowflakeCortex < OpenAI
|
|
12
|
+
def initialize(config = RCrewAI.configuration, **hooks)
|
|
13
|
+
super
|
|
14
|
+
@account = config.snowflake_account
|
|
15
|
+
@base_url = "https://#{@account}.snowflakecomputing.com"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def provider_name
|
|
19
|
+
:snowflake
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def chat_url
|
|
25
|
+
"#{@base_url}/api/v2/cortex/inference:complete"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def auth_header
|
|
29
|
+
{
|
|
30
|
+
'Authorization' => "Bearer #{config.api_key}",
|
|
31
|
+
'X-Snowflake-Authorization-Token-Type' => 'KEYPAIR_JWT'
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def validate_config!
|
|
36
|
+
raise ConfigurationError, 'Snowflake token is required' unless config.api_key
|
|
37
|
+
raise ConfigurationError, 'A Snowflake account identifier is required' unless config.snowflake_account
|
|
38
|
+
raise ConfigurationError, 'Model is required' unless config.model
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
data/lib/rcrewai/process.rb
CHANGED
|
@@ -17,6 +17,18 @@ module RCrewAI
|
|
|
17
17
|
|
|
18
18
|
protected
|
|
19
19
|
|
|
20
|
+
# True when this task's result was replayed from a checkpoint and so must
|
|
21
|
+
# not be executed again.
|
|
22
|
+
def restored?(task)
|
|
23
|
+
crew.respond_to?(:restored_task_names) &&
|
|
24
|
+
crew.restored_task_names.include?(task.name)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Records a settled task in the crew's checkpoint, if one is active.
|
|
28
|
+
def checkpoint(task, status)
|
|
29
|
+
crew.checkpoint_task(task, status) if crew.respond_to?(:checkpoint_task)
|
|
30
|
+
end
|
|
31
|
+
|
|
20
32
|
def log_execution_start
|
|
21
33
|
@logger.info "Starting #{self.class.name.split('::').last.downcase} process execution"
|
|
22
34
|
@logger.info "Crew: #{crew.name} with #{crew.agents.length} agents, #{crew.tasks.length} tasks"
|
|
@@ -34,13 +46,21 @@ module RCrewAI
|
|
|
34
46
|
results = []
|
|
35
47
|
|
|
36
48
|
crew.tasks.each do |task|
|
|
49
|
+
if restored?(task)
|
|
50
|
+
@logger.info "Skipping task (restored from checkpoint): #{task.name}"
|
|
51
|
+
results << { task: task, result: task.result, status: :completed }
|
|
52
|
+
next
|
|
53
|
+
end
|
|
54
|
+
|
|
37
55
|
@logger.info "Executing task: #{task.name}"
|
|
38
56
|
begin
|
|
39
57
|
result = task.execute
|
|
40
58
|
results << { task: task, result: result, status: :completed }
|
|
59
|
+
checkpoint(task, :completed)
|
|
41
60
|
rescue StandardError => e
|
|
42
61
|
@logger.error "Task #{task.name} failed: #{e.message}"
|
|
43
62
|
results << { task: task, result: e.message, status: :failed }
|
|
63
|
+
checkpoint(task, :failed)
|
|
44
64
|
end
|
|
45
65
|
end
|
|
46
66
|
|
|
@@ -241,6 +261,15 @@ module RCrewAI
|
|
|
241
261
|
tasks.each do |task|
|
|
242
262
|
assigned_agent = hierarchy[:task_assignments][task]
|
|
243
263
|
|
|
264
|
+
if restored?(task)
|
|
265
|
+
@logger.info "Skipping task (restored from checkpoint): #{task.name}"
|
|
266
|
+
phase_results << {
|
|
267
|
+
task: task, result: task.result, status: :completed,
|
|
268
|
+
assigned_agent: assigned_agent, phase: phase_number
|
|
269
|
+
}
|
|
270
|
+
next
|
|
271
|
+
end
|
|
272
|
+
|
|
244
273
|
@logger.info "Manager delegating '#{task.name}' to #{assigned_agent.name}"
|
|
245
274
|
|
|
246
275
|
begin
|
|
@@ -258,8 +287,10 @@ module RCrewAI
|
|
|
258
287
|
phase: phase_number
|
|
259
288
|
}
|
|
260
289
|
|
|
290
|
+
checkpoint(task, :completed)
|
|
261
291
|
@logger.info "Task '#{task.name}' completed successfully"
|
|
262
292
|
rescue StandardError => e
|
|
293
|
+
checkpoint(task, :failed)
|
|
263
294
|
@logger.error "Delegated task '#{task.name}' failed: #{e.message}"
|
|
264
295
|
|
|
265
296
|
phase_results << {
|
|
@@ -390,11 +421,17 @@ module RCrewAI
|
|
|
390
421
|
private
|
|
391
422
|
|
|
392
423
|
def execute_with_consensus(task)
|
|
424
|
+
if restored?(task)
|
|
425
|
+
@logger.info "Skipping task (restored from checkpoint): #{task.name}"
|
|
426
|
+
return { task: task, result: task.result, status: :completed }
|
|
427
|
+
end
|
|
428
|
+
|
|
393
429
|
@logger.info "Consensus for task: #{task.name}"
|
|
394
430
|
participants = select_participants(task)
|
|
395
431
|
candidates = gather_proposals(task, participants)
|
|
396
432
|
|
|
397
433
|
if candidates.empty?
|
|
434
|
+
checkpoint(task, :failed)
|
|
398
435
|
return { task: task, result: 'All agents failed to produce a proposal', status: :failed }
|
|
399
436
|
end
|
|
400
437
|
|
|
@@ -402,9 +439,11 @@ module RCrewAI
|
|
|
402
439
|
winner = pick_winner(task, scored)
|
|
403
440
|
task.result = winner[:content] if task.respond_to?(:result=)
|
|
404
441
|
|
|
442
|
+
checkpoint(task, :completed)
|
|
405
443
|
{ task: task, result: winner[:content], status: :completed }
|
|
406
444
|
rescue StandardError => e
|
|
407
445
|
@logger.error "Consensus failed for #{task.name}: #{e.message}"
|
|
446
|
+
checkpoint(task, :failed)
|
|
408
447
|
{ task: task, result: e.message, status: :failed }
|
|
409
448
|
end
|
|
410
449
|
|
data/lib/rcrewai/tool_runner.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'securerandom'
|
|
3
4
|
require_relative 'events'
|
|
4
5
|
require_relative 'provider_schema'
|
|
5
6
|
|
|
@@ -16,7 +17,13 @@ module RCrewAI
|
|
|
16
17
|
@sink = event_sink || ->(_) {}
|
|
17
18
|
end
|
|
18
19
|
|
|
19
|
-
def run(messages:)
|
|
20
|
+
def run(messages:)
|
|
21
|
+
Events.with_parent(@run_span_id ||= SecureRandom.uuid) { run_loop(messages: messages) }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def run_loop(messages:) # rubocop:disable Metrics/AbcSize
|
|
20
27
|
msgs = messages.dup
|
|
21
28
|
history = []
|
|
22
29
|
iter = 0
|
|
@@ -78,8 +85,6 @@ module RCrewAI
|
|
|
78
85
|
finish_reason: :max_iterations, usage: total_usage)
|
|
79
86
|
end
|
|
80
87
|
|
|
81
|
-
private
|
|
82
|
-
|
|
83
88
|
# Trims the message list to the model's context window when the agent
|
|
84
89
|
# supports it; a no-op otherwise.
|
|
85
90
|
def fit_context(messages)
|
|
@@ -94,13 +99,13 @@ module RCrewAI
|
|
|
94
99
|
type_sym = klass.name.split('::').last
|
|
95
100
|
.gsub(/([A-Z])/) { "_#{Regexp.last_match(1).downcase}" }
|
|
96
101
|
.sub(/^_/, '').to_sym
|
|
97
|
-
@sink
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
Events.emit(@sink, klass.new(
|
|
103
|
+
type: type_sym,
|
|
104
|
+
timestamp: Time.now,
|
|
105
|
+
agent: agent_name,
|
|
106
|
+
iteration: iteration,
|
|
107
|
+
**attrs
|
|
108
|
+
))
|
|
104
109
|
end
|
|
105
110
|
|
|
106
111
|
def agent_name
|
|
@@ -110,6 +115,7 @@ module RCrewAI
|
|
|
110
115
|
def retag(event, iter)
|
|
111
116
|
event.agent = agent_name if event.respond_to?(:agent=) && event.agent.nil?
|
|
112
117
|
event.iteration = iter if event.respond_to?(:iteration=) && event.iteration.nil?
|
|
118
|
+
event.parent_id ||= Events.current_parent
|
|
113
119
|
event
|
|
114
120
|
end
|
|
115
121
|
|
data/lib/rcrewai/version.rb
CHANGED
data/lib/rcrewai.rb
CHANGED
|
@@ -19,6 +19,7 @@ end
|
|
|
19
19
|
require_relative 'rcrewai/version'
|
|
20
20
|
require_relative 'rcrewai/configuration'
|
|
21
21
|
require_relative 'rcrewai/events'
|
|
22
|
+
require_relative 'rcrewai/checkpoint'
|
|
22
23
|
require_relative 'rcrewai/sse_parser'
|
|
23
24
|
require_relative 'rcrewai/pricing'
|
|
24
25
|
require_relative 'rcrewai/llm_client'
|
|
@@ -56,3 +57,5 @@ require_relative 'rcrewai/task'
|
|
|
56
57
|
require_relative 'rcrewai/crew'
|
|
57
58
|
require_relative 'rcrewai/flow'
|
|
58
59
|
require_relative 'rcrewai/mcp'
|
|
60
|
+
require_relative 'rcrewai/checkpoint/cli'
|
|
61
|
+
require_relative 'rcrewai/cli'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rcrewai
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- gkosmo
|
|
@@ -389,6 +389,8 @@ files:
|
|
|
389
389
|
- lib/rcrewai/agent.rb
|
|
390
390
|
- lib/rcrewai/agent_augmentations.rb
|
|
391
391
|
- lib/rcrewai/async_executor.rb
|
|
392
|
+
- lib/rcrewai/checkpoint.rb
|
|
393
|
+
- lib/rcrewai/checkpoint/cli.rb
|
|
392
394
|
- lib/rcrewai/cli.rb
|
|
393
395
|
- lib/rcrewai/configuration.rb
|
|
394
396
|
- lib/rcrewai/context_window.rb
|
|
@@ -409,9 +411,13 @@ files:
|
|
|
409
411
|
- lib/rcrewai/llm_clients/anthropic.rb
|
|
410
412
|
- lib/rcrewai/llm_clients/azure.rb
|
|
411
413
|
- lib/rcrewai/llm_clients/base.rb
|
|
414
|
+
- lib/rcrewai/llm_clients/bedrock.rb
|
|
412
415
|
- lib/rcrewai/llm_clients/google.rb
|
|
413
416
|
- lib/rcrewai/llm_clients/ollama.rb
|
|
414
417
|
- lib/rcrewai/llm_clients/openai.rb
|
|
418
|
+
- lib/rcrewai/llm_clients/openai_compatible.rb
|
|
419
|
+
- lib/rcrewai/llm_clients/openai_responses.rb
|
|
420
|
+
- lib/rcrewai/llm_clients/snowflake_cortex.rb
|
|
415
421
|
- lib/rcrewai/mcp.rb
|
|
416
422
|
- lib/rcrewai/mcp/client.rb
|
|
417
423
|
- lib/rcrewai/mcp/tool_adapter.rb
|