roast-ai 0.4.5 → 0.4.6
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 +8 -0
- data/Gemfile.lock +1 -1
- data/examples/retry/workflow.yml +23 -0
- data/lib/roast/version.rb +1 -1
- data/lib/roast/workflow/error_handler.rb +32 -2
- data/lib/roast/workflow/step_executor_coordinator.rb +10 -3
- data/lib/roast/workflow/workflow_initializer.rb +15 -1
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1b30690bd6345f217ff752e69865abf269792917e6463bbb70745a812ec15a4b
|
4
|
+
data.tar.gz: de3732ccd3322fb8ff782d10e52168e0e4292fdb21596ff0eff7201532498c8d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ba81d35b6484ffbdb3bb5aaed1baacbbc684033e62df23131c6d0bcb7833bc0902600f84a462fb451213d56c43862e49e8db5bfd150fb4a7520b66cd1e078d85
|
7
|
+
data.tar.gz: 01b3d585d72528c5d8382a13edd379f6af249d75059564adb8eb33671b2dfb252749a09f8e2d8a551b09fad048b735960fcf8f2cf37d1c0f1d06aa758feee50d
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
|
+
## [0.4.6]
|
9
|
+
|
10
|
+
### Added
|
11
|
+
- Step retries
|
12
|
+
|
13
|
+
### Fixed
|
14
|
+
- Tokens containing whitespace
|
15
|
+
|
8
16
|
## [0.4.5]
|
9
17
|
|
10
18
|
### Added
|
data/Gemfile.lock
CHANGED
@@ -0,0 +1,23 @@
|
|
1
|
+
description: Demonstrate retry functionality for steps
|
2
|
+
|
3
|
+
# This example shows how to configure steps to automatically retry on failure
|
4
|
+
# The 'retries' parameter specifies how many times to retry a failing step
|
5
|
+
|
6
|
+
steps:
|
7
|
+
- check_network: "$(curl -f -s -o /dev/null -w '%{http_code}' https://httpstat.us/500)"
|
8
|
+
- process_data: "Process important data that might fail transiently"
|
9
|
+
- flaky_command: "$(./flaky_script.sh)"
|
10
|
+
|
11
|
+
# Configuration for each step
|
12
|
+
check_network:
|
13
|
+
retries: 3 # Will retry up to 3 times if it fails
|
14
|
+
exit_on_error: true # Will exit workflow if all retries fail
|
15
|
+
|
16
|
+
process_data:
|
17
|
+
model: gpt-4o-mini
|
18
|
+
retries: 2 # Will retry up to 2 times
|
19
|
+
|
20
|
+
# Command with retries but won't exit on error after all retries fail
|
21
|
+
flaky_command:
|
22
|
+
retries: 5
|
23
|
+
exit_on_error: false # Continue workflow even after all retries fail
|
data/lib/roast/version.rb
CHANGED
@@ -10,8 +10,9 @@ module Roast
|
|
10
10
|
# Use the Roast logger singleton
|
11
11
|
end
|
12
12
|
|
13
|
-
def with_error_handling(step_name, resource_type: nil)
|
13
|
+
def with_error_handling(step_name, resource_type: nil, retries: 0, &block)
|
14
14
|
start_time = Time.now
|
15
|
+
maximum_attempts = retries + 1
|
15
16
|
|
16
17
|
ActiveSupport::Notifications.instrument("roast.step.start", {
|
17
18
|
step_name: step_name,
|
@@ -19,7 +20,19 @@ module Roast
|
|
19
20
|
workflow_name: @workflow&.name,
|
20
21
|
})
|
21
22
|
|
22
|
-
result =
|
23
|
+
result = nil #: untyped?
|
24
|
+
|
25
|
+
maximum_attempts.times do |current_attempt|
|
26
|
+
# .times starts at 0 index, the math is easier to reason about if current_attempt matches normal description
|
27
|
+
current_attempt += 1
|
28
|
+
result = block.call
|
29
|
+
break
|
30
|
+
rescue StandardError => e
|
31
|
+
remaining_attempts = maximum_attempts - current_attempt
|
32
|
+
raise e if remaining_attempts == 0
|
33
|
+
|
34
|
+
handle_retry(e, step_name, resource_type, start_time, remaining_attempts)
|
35
|
+
end
|
23
36
|
|
24
37
|
execution_time = Time.now - start_time
|
25
38
|
|
@@ -62,6 +75,23 @@ module Roast
|
|
62
75
|
|
63
76
|
private
|
64
77
|
|
78
|
+
def handle_retry(error, step_name, resource_type, start_time, remaining_attempts)
|
79
|
+
execution_time = Time.now - start_time
|
80
|
+
|
81
|
+
ActiveSupport::Notifications.instrument("roast.step.error.retry", {
|
82
|
+
step_name: step_name,
|
83
|
+
resource_type: resource_type,
|
84
|
+
workflow_name: @workflow&.name,
|
85
|
+
error: error.class.name,
|
86
|
+
message: error.message,
|
87
|
+
execution_time: execution_time,
|
88
|
+
remaining_attempts: remaining_attempts,
|
89
|
+
})
|
90
|
+
|
91
|
+
log_warning("[#{step_name}] failed: #{error.message}")
|
92
|
+
log_warning("Retrying, #{remaining_attempts} attempts left")
|
93
|
+
end
|
94
|
+
|
65
95
|
def handle_workflow_error(error, step_name, resource_type, start_time)
|
66
96
|
execution_time = Time.now - start_time
|
67
97
|
|
@@ -60,6 +60,10 @@ module Roast
|
|
60
60
|
Thread.current[:current_step_name] = step_name if step_name
|
61
61
|
Thread.current[:workflow_metadata] = @context.workflow.metadata
|
62
62
|
|
63
|
+
unless options[:retries]
|
64
|
+
options[:retries] = @context.config_hash[step_name]&.fetch("retries", 0) || 0
|
65
|
+
end
|
66
|
+
|
63
67
|
case step_type
|
64
68
|
when StepTypeResolver::COMMAND_STEP
|
65
69
|
# Command steps should also go through interpolation
|
@@ -148,8 +152,9 @@ module Roast
|
|
148
152
|
resource_type = @context.resource_type
|
149
153
|
step_key = options[:step_key]
|
150
154
|
display_name = step_key || step
|
155
|
+
retries = options.fetch(:retries, 0)
|
151
156
|
|
152
|
-
error_handler.with_error_handling(display_name, resource_type: resource_type) do
|
157
|
+
error_handler.with_error_handling(display_name, resource_type: resource_type, retries:) do
|
153
158
|
$stderr.puts "Executing: #{display_name} (Resource type: #{resource_type || "unknown"})"
|
154
159
|
|
155
160
|
begin
|
@@ -263,7 +268,8 @@ module Roast
|
|
263
268
|
# Command step - execute directly, preserving any passed options including step_key
|
264
269
|
exit_on_error = options.fetch(:exit_on_error, true)
|
265
270
|
step_key = options[:step_key]
|
266
|
-
|
271
|
+
retries = options[:retries] || 0
|
272
|
+
execute_command_step(interpolated_step, { exit_on_error:, step_key:, retries: })
|
267
273
|
else
|
268
274
|
exit_on_error = options.fetch(:exit_on_error, context.exit_on_error?(step))
|
269
275
|
execute_standard_step(interpolated_step, options.merge(exit_on_error:))
|
@@ -286,8 +292,9 @@ module Roast
|
|
286
292
|
|
287
293
|
def execute_custom_step(name, step_key: nil, **options)
|
288
294
|
resource_type = @context.workflow.respond_to?(:resource) ? @context.workflow.resource&.type : nil
|
295
|
+
retries = options[:retries] || 0
|
289
296
|
|
290
|
-
error_handler.with_error_handling(name, resource_type: resource_type) do
|
297
|
+
error_handler.with_error_handling(name, resource_type: resource_type, retries:) do
|
291
298
|
$stderr.puts "Executing: #{name} (Resource type: #{resource_type || "unknown"})"
|
292
299
|
|
293
300
|
# Use step_key for loading if provided, otherwise use name
|
@@ -29,6 +29,9 @@ module Roast
|
|
29
29
|
# Only check if the workflow has steps that would need API access
|
30
30
|
return if @configuration.steps.empty?
|
31
31
|
|
32
|
+
# Strip whitespace from existing Raix clients
|
33
|
+
strip_tokens_from_existing_clients
|
34
|
+
|
32
35
|
# Check if Raix has been configured with the appropriate client
|
33
36
|
case @configuration.api_provider
|
34
37
|
when :openai
|
@@ -215,7 +218,7 @@ module Roast
|
|
215
218
|
|
216
219
|
def client_options
|
217
220
|
{
|
218
|
-
access_token: @configuration.api_token,
|
221
|
+
access_token: @configuration.api_token&.strip,
|
219
222
|
uri_base: @configuration.uri_base&.to_s,
|
220
223
|
}.compact
|
221
224
|
end
|
@@ -265,6 +268,17 @@ module Roast
|
|
265
268
|
end
|
266
269
|
interpolated
|
267
270
|
end
|
271
|
+
|
272
|
+
def strip_tokens_from_existing_clients
|
273
|
+
strip_token_in_client(Raix.configuration.openai_client)
|
274
|
+
strip_token_in_client(Raix.configuration.openrouter_client)
|
275
|
+
end
|
276
|
+
|
277
|
+
def strip_token_in_client(client)
|
278
|
+
return unless client.respond_to?(:access_token)
|
279
|
+
|
280
|
+
client.instance_variable_set(:@access_token, client.access_token&.strip)
|
281
|
+
end
|
268
282
|
end
|
269
283
|
end
|
270
284
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: roast-ai
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
@@ -397,6 +397,7 @@ files:
|
|
397
397
|
- examples/pre_post_processing/validate_changes/prompt.md
|
398
398
|
- examples/pre_post_processing/workflow.png
|
399
399
|
- examples/pre_post_processing/workflow.yml
|
400
|
+
- examples/retry/workflow.yml
|
400
401
|
- examples/rspec_to_minitest/README.md
|
401
402
|
- examples/rspec_to_minitest/analyze_spec/prompt.md
|
402
403
|
- examples/rspec_to_minitest/create_minitest/prompt.md
|