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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 70a8b9b5eb5427205d65f9e2d3eb989c53a0cd449aa83d2e8df712f3b06ae203
4
- data.tar.gz: 29d8236ed59630399b69ddc7286d9f80428620158117a68bc14048c8ac0a1e78
3
+ metadata.gz: 1b30690bd6345f217ff752e69865abf269792917e6463bbb70745a812ec15a4b
4
+ data.tar.gz: de3732ccd3322fb8ff782d10e52168e0e4292fdb21596ff0eff7201532498c8d
5
5
  SHA512:
6
- metadata.gz: 48fcaf3988124ce0d627ff22f11fc563a990b96eb92afa27b75294f4c21ed2341c240f5ecf1fcf2b618f87078b19876adaa5cc493419a6be3b05c6b2137abd58
7
- data.tar.gz: 4df2dfb16ff49b3bfe53102d878de8c356330621e4267a07fbfd35642251b512eab974cbe4c3377fbdaf29933b4a36c91010abc741ccb6b11321fcc4decf191a
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- roast-ai (0.4.5)
4
+ roast-ai (0.4.6)
5
5
  activesupport (>= 7.0)
6
6
  cli-kit (~> 5.0)
7
7
  cli-ui (= 2.3.0)
@@ -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
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Roast
5
- VERSION = "0.4.5"
5
+ VERSION = "0.4.6"
6
6
  end
@@ -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 = yield
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
- execute_command_step(interpolated_step, { exit_on_error:, step_key: })
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.5
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