Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion lib/roast.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,11 @@ def execute(*paths)

raise Thor::Error, "Expected a Roast workflow configuration file, got directory: #{expanded_workflow_path}" if File.directory?(expanded_workflow_path)

Roast::Workflow::ConfigurationParser.new(expanded_workflow_path, files, options.transform_keys(&:to_sym)).begin!
begin
Roast::Workflow::ConfigurationParser.new(expanded_workflow_path, files, options.transform_keys(&:to_sym)).begin!
rescue Roast::Errors::AuthenticationError => e
raise Thor::Error, "API authentication failed: #{e.message}"
end
end

desc "version", "Display the current version of Roast"
Expand Down
25 changes: 23 additions & 2 deletions lib/roast/workflow/api_configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,23 @@ def effective_token
@api_token || environment_token
end

# A reason for why the API authentication failed, if it failed.
# Includes the API provider and the environment variable,
# and whether it was present and invalid or missing
def authentication_failure_reason(e)
return unless e

reason = "#{@api_provider.inspect} provider requires"
reason += " the #{environment_key} environment variable" if environment_key
reason += if environment_token.present?
" to be valid, but failed to authenticate"
else
" to be present, but not found"
end

reason
end

private

def process_api_configuration
Expand All @@ -54,10 +71,14 @@ def extract_uri_base
end

def environment_token
ENV[environment_key] if environment_key
end

def environment_key
if openai?
ENV["OPENAI_API_KEY"]
"OPENAI_API_KEY"
elsif openrouter?
ENV["OPENROUTER_API_KEY"]
"OPENROUTER_API_KEY"
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/roast/workflow/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class Configuration
attr_reader :config_hash, :workflow_path, :name, :steps, :pre_processing, :post_processing, :tools, :tool_configs, :mcp_tools, :function_configs, :model, :resource
attr_accessor :target

delegate :api_provider, :openrouter?, :openai?, :uri_base, to: :api_configuration
delegate :api_provider, :openrouter?, :openai?, :uri_base, :authentication_failure_reason, to: :api_configuration

# Delegate api_token to effective_token for backward compatibility
def api_token
Expand Down
2 changes: 1 addition & 1 deletion lib/roast/workflow/workflow_initializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def configure_api_client
# Validate the client configuration by making a test API call
validate_api_client(client) if client
rescue OpenRouter::ConfigurationError, Faraday::UnauthorizedError => e
error = Roast::Errors::AuthenticationError.new("API authentication failed: No API token provided or token is invalid")
error = Roast::Errors::AuthenticationError.new(@configuration.authentication_failure_reason(e))
error.set_backtrace(e.backtrace)

ActiveSupport::Notifications.instrument("roast.workflow.start.error", {
Expand Down
12 changes: 8 additions & 4 deletions test/roast/workflow/workflow_initializer_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ def test_skips_configuration_for_unsupported_api_provider_when_no_token
def test_raises_authentication_error_when_api_token_invalid
@configuration.stubs(:api_token).returns("invalid-token")
@configuration.stubs(:api_provider).returns(:openai)
@configuration.stubs(:openai?).returns(true)
@configuration.stubs(:openrouter?).returns(false)

# Stub Raix configuration to indicate no client is configured yet
Raix.configuration.stubs(:openai_client).returns(nil)
Expand All @@ -232,20 +234,22 @@ def test_raises_authentication_error_when_api_token_invalid
"roast.workflow.start.error",
has_entries(
error: "Roast::Errors::AuthenticationError",
message: "API authentication failed: No API token provided or token is invalid",
message: ":openai provider requires the OPENAI_API_KEY environment variable to be present, but not found",
),
).once

error = assert_raises(Roast::Errors::AuthenticationError) do
@initializer.setup
end

assert_equal("API authentication failed: No API token provided or token is invalid", error.message)
assert_equal(":openai provider requires the OPENAI_API_KEY environment variable to be present, but not found", error.message)
end

def test_handles_openrouter_configuration_error
@configuration.stubs(:api_token).returns("invalid-format-token")
@configuration.stubs(:api_provider).returns(:openrouter)
@configuration.stubs(:openrouter?).returns(true)
@configuration.stubs(:openai?).returns(false)

# Stub Raix configuration to indicate no client is configured yet
Raix.configuration.stubs(:openrouter_client).returns(nil)
Expand All @@ -257,14 +261,14 @@ def test_handles_openrouter_configuration_error
"roast.workflow.start.error",
has_entries(
error: "Roast::Errors::AuthenticationError",
message: "API authentication failed: No API token provided or token is invalid",
message: ":openrouter provider requires the OPENROUTER_API_KEY environment variable to be present, but not found",
),
).once

error = assert_raises(Roast::Errors::AuthenticationError) do
@initializer.setup
end

assert_equal("API authentication failed: No API token provided or token is invalid", error.message)
assert_equal(":openrouter provider requires the OPENROUTER_API_KEY environment variable to be present, but not found", error.message)
end
end
Loading