From a99508de322a16204e35389694128804f5ea202c Mon Sep 17 00:00:00 2001 From: Jesse Spevack Date: Mon, 1 Dec 2025 09:44:21 -0700 Subject: [PATCH 1/3] Fix VertexAI scope argument for GCE credentials Google::Auth.get_application_default expects scope as a positional argument, not a keyword argument. Passing `scope: [...]` causes Ruby to interpret it as a Hash, which triggers a TypeError in the signet gem when running on GCE: TypeError: Expected Array or String, got Hash This fix changes from keyword to positional argument syntax. --- lib/ruby_llm/providers/vertexai.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ruby_llm/providers/vertexai.rb b/lib/ruby_llm/providers/vertexai.rb index 268774eec..8cf7bc057 100644 --- a/lib/ruby_llm/providers/vertexai.rb +++ b/lib/ruby_llm/providers/vertexai.rb @@ -41,7 +41,7 @@ def configuration_requirements def initialize_authorizer require 'googleauth' @authorizer = ::Google::Auth.get_application_default( - scope: [ + [ 'https://www.googleapis.com/auth/cloud-platform', 'https://www.googleapis.com/auth/generative-language.retriever' ] From a7b276e874d4c913040b63120514697c97ab8054 Mon Sep 17 00:00:00 2001 From: Jesse Spevack Date: Mon, 1 Dec 2025 09:47:28 -0700 Subject: [PATCH 2/3] Add test for VertexAI scope argument fix --- spec/ruby_llm/providers/vertexai/auth_spec.rb | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 spec/ruby_llm/providers/vertexai/auth_spec.rb diff --git a/spec/ruby_llm/providers/vertexai/auth_spec.rb b/spec/ruby_llm/providers/vertexai/auth_spec.rb new file mode 100644 index 000000000..8d0f84716 --- /dev/null +++ b/spec/ruby_llm/providers/vertexai/auth_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe RubyLLM::Providers::VertexAI do + include_context 'with configured RubyLLM' + + describe '#initialize_authorizer' do + let(:provider) do + described_class.new(RubyLLM.config) + end + + let(:mock_credentials) do + instance_double(Google::Auth::GCECredentials, apply: { 'Authorization' => 'Bearer test-token' }) + end + + before do + # Reset the authorizer to force re-initialization + provider.instance_variable_set(:@authorizer, nil) + end + + it 'passes scope as a positional argument to get_application_default' do + # This test verifies the fix for the TypeError that occurs when running on GCE. + # Google::Auth.get_application_default expects scope as a positional argument, + # not a keyword argument. Passing `scope: [...]` causes Ruby to interpret it + # as a Hash, which triggers: TypeError: Expected Array or String, got Hash + expected_scopes = [ + 'https://www.googleapis.com/auth/cloud-platform', + 'https://www.googleapis.com/auth/generative-language.retriever' + ] + + expect(Google::Auth).to receive(:get_application_default) + .with(expected_scopes) + .and_return(mock_credentials) + + # Trigger the authorizer initialization by calling headers + provider.headers + end + + it 'does not pass scope as a keyword argument' do + # Ensure we're not passing a Hash like {scope: [...]} which would cause + # TypeError on GCE: "Expected Array or String, got Hash" + expect(Google::Auth).to receive(:get_application_default) do |arg| + expect(arg).to be_an(Array) + expect(arg).not_to be_a(Hash) + mock_credentials + end + + provider.headers + end + end +end From 51f9ca1fd3fdc11fd100040c7c0c5e0b18e35a96 Mon Sep 17 00:00:00 2001 From: Jesse Spevack Date: Mon, 1 Dec 2025 09:59:21 -0700 Subject: [PATCH 3/3] Consolidate redundant tests into single focused test --- spec/ruby_llm/providers/vertexai/auth_spec.rb | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/spec/ruby_llm/providers/vertexai/auth_spec.rb b/spec/ruby_llm/providers/vertexai/auth_spec.rb index 8d0f84716..bcb5325f0 100644 --- a/spec/ruby_llm/providers/vertexai/auth_spec.rb +++ b/spec/ruby_llm/providers/vertexai/auth_spec.rb @@ -15,11 +15,10 @@ end before do - # Reset the authorizer to force re-initialization provider.instance_variable_set(:@authorizer, nil) end - it 'passes scope as a positional argument to get_application_default' do + it 'passes scope as a positional Array argument to get_application_default' do # This test verifies the fix for the TypeError that occurs when running on GCE. # Google::Auth.get_application_default expects scope as a positional argument, # not a keyword argument. Passing `scope: [...]` causes Ruby to interpret it @@ -33,19 +32,6 @@ .with(expected_scopes) .and_return(mock_credentials) - # Trigger the authorizer initialization by calling headers - provider.headers - end - - it 'does not pass scope as a keyword argument' do - # Ensure we're not passing a Hash like {scope: [...]} which would cause - # TypeError on GCE: "Expected Array or String, got Hash" - expect(Google::Auth).to receive(:get_application_default) do |arg| - expect(arg).to be_an(Array) - expect(arg).not_to be_a(Hash) - mock_credentials - end - provider.headers end end