Skip to content

Commit 791f28b

Browse files
ESegundoRolonEnriquecrmne
authored
Inference regions (#338)
## What this does Support region-aware inference profiles for AWS Bedrock ### Problem The current implementation hardcodes us. prefix for all Bedrock inference profile models, causing failures when using cross-region inference in non-US regions like EU, AP, or CA. - Error encountered: The provided model identifier is invalid. (RubyLLM::BadRequestError) This occurs because: 1. EU regions expect for example `eu.anthropic.claude-sonnet-4-20250514-v1:0` 2. But the library sends `us.anthropic.claude-sonnet-4-20250514-v1:0` 3. AWS Bedrock rejects the incorrect region prefix ### Solution Updated model_id_with_region method to dynamically extract the region prefix from the configured bedrock_region: - eu-west-3 → eu. prefix - us-east-1 → us. prefix - ap-south-1 → ap. prefix - ca-central-1 → ca. prefix ### Changes Made Core Fix File: lib/ruby_llm/providers/bedrock/models.rb - Modified model_id_with_region to use dynamic region prefix - Added inference_profile_region_prefix helper method - Uses first two characters of bedrock_region as prefix - Maintains backwards compatibility with us default Tests Added File: spec/ruby_llm/providers/bedrock/models_spec.rb - Comprehensive tests for different AWS regions (US, EU, AP, CA) - Tests for edge cases (empty/nil regions) - Tests for both inference profile and on-demand models - Ensures backwards compatibility Testing - ✅ US regions continue to work (us. prefix) - ✅ EU regions now work (eu. prefix) - ✅ AP/CA regions supported (ap./ca. prefixes) - ✅ ON_DEMAND models unaffected - ✅ Backwards compatible with existing configurations ### Breaking Changes None. This change is fully backwards compatible. ## Type of change - [ ] Bug fix - [X] New feature - [ ] Breaking change - [ ] Documentation - [ ] Performance improvement ## Scope check - [X] I read the [Contributing Guide](https://github.com/crmne/ruby_llm/blob/main/CONTRIBUTING.md) - [X] This aligns with RubyLLM's focus on **LLM communication** - [X] This isn't application-specific logic that belongs in user code - [X] This benefits most users, not just my specific use case ## Quality check - [X] I ran `overcommit --install` and all hooks pass - [X] I tested my changes thoroughly - [X] I updated documentation if needed - [X] I didn't modify auto-generated files manually (`models.json`, `aliases.json`) ## API changes - [ ] Breaking change - [ ] New public methods/classes - [ ] Changed method signatures - [ ] No API changes --------- Co-authored-by: Enrique <[email protected]> Co-authored-by: Carmine Paolino <[email protected]>
1 parent 32611af commit 791f28b

File tree

2 files changed

+147
-5
lines changed

2 files changed

+147
-5
lines changed

lib/ruby_llm/providers/bedrock/models.rb

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,25 @@ def model_id_with_region(model_id, model_data)
7272
return model_id unless model_data['inferenceTypesSupported']&.include?('INFERENCE_PROFILE')
7373
return model_id if model_data['inferenceTypesSupported']&.include?('ON_DEMAND')
7474

75-
"us.#{model_id}"
75+
desired_region_prefix = inference_profile_region_prefix
76+
77+
# Return unchanged if model already has the correct region prefix
78+
return model_id if model_id.start_with?("#{desired_region_prefix}.")
79+
80+
# Remove any existing region prefix (e.g., "us.", "eu.", "ap.")
81+
clean_model_id = model_id.sub(/^[a-z]{2}\./, '')
82+
83+
# Apply the desired region prefix
84+
"#{desired_region_prefix}.#{clean_model_id}"
85+
end
86+
87+
def inference_profile_region_prefix
88+
# Extract region prefix from bedrock_region (e.g., "eu-west-3" -> "eu")
89+
region = @config.bedrock_region.to_s
90+
return 'us' if region.empty? # Default fallback
91+
92+
# Take first two characters as the region prefix
93+
region[0, 2]
7694
end
7795
end
7896
end

spec/ruby_llm/providers/bedrock/models_spec.rb

Lines changed: 128 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@
3737
end
3838

3939
it 'adds us. prefix to model ID' do
40-
model_info = described_class.create_model_info(model_data, slug, capabilities)
40+
# Mock a provider instance to test the region functionality
41+
allow(RubyLLM.config).to receive(:bedrock_region).and_return('us-east-1')
42+
provider = RubyLLM::Providers::Bedrock.new(RubyLLM.config)
43+
provider.extend(described_class)
44+
45+
model_info = provider.send(:create_model_info, model_data, slug, capabilities)
4146
expect(model_info.id).to eq('us.anthropic.claude-3-7-sonnet-20250219-v1:0')
4247
end
4348
end
@@ -57,7 +62,12 @@
5762
end
5863

5964
it 'does not add us. prefix to model ID' do
60-
model_info = described_class.create_model_info(model_data, slug, capabilities)
65+
# Mock a provider instance to test the region functionality
66+
allow(RubyLLM.config).to receive(:bedrock_region).and_return('us-east-1')
67+
provider = RubyLLM::Providers::Bedrock.new(RubyLLM.config)
68+
provider.extend(described_class)
69+
70+
model_info = provider.send(:create_model_info, model_data, slug, capabilities)
6171
expect(model_info.id).to eq('anthropic.claude-3-5-sonnet-20240620-v1:0')
6272
end
6373
end
@@ -77,7 +87,12 @@
7787
end
7888

7989
it 'does not add us. prefix to model ID' do
80-
model_info = described_class.create_model_info(model_data, slug, capabilities)
90+
# Mock a provider instance to test the region functionality
91+
allow(RubyLLM.config).to receive(:bedrock_region).and_return('us-east-1')
92+
provider = RubyLLM::Providers::Bedrock.new(RubyLLM.config)
93+
provider.extend(described_class)
94+
95+
model_info = provider.send(:create_model_info, model_data, slug, capabilities)
8196
expect(model_info.id).to eq('anthropic.claude-3-5-sonnet-20240620-v1:0')
8297
end
8398
end
@@ -96,9 +111,118 @@
96111
end
97112

98113
it 'does not add us. prefix to model ID' do
99-
model_info = described_class.create_model_info(model_data, slug, capabilities)
114+
# Mock a provider instance to test the region functionality
115+
allow(RubyLLM.config).to receive(:bedrock_region).and_return('us-east-1')
116+
provider = RubyLLM::Providers::Bedrock.new(RubyLLM.config)
117+
provider.extend(described_class)
118+
119+
model_info = provider.send(:create_model_info, model_data, slug, capabilities)
100120
expect(model_info.id).to eq('anthropic.claude-3-5-sonnet-20240620-v1:0')
101121
end
102122
end
103123
end
124+
125+
# New specs for region-aware inference profile handling
126+
describe '#model_id_with_region with region awareness' do
127+
let(:provider_instance) do
128+
allow(RubyLLM.config).to receive(:bedrock_region).and_return('eu-west-3')
129+
provider = RubyLLM::Providers::Bedrock.new(RubyLLM.config)
130+
provider.extend(described_class)
131+
provider
132+
end
133+
134+
context 'with EU region configured' do
135+
let(:inference_profile_model) do
136+
{
137+
'modelId' => 'anthropic.claude-3-7-sonnet-20250219-v1:0',
138+
'inferenceTypesSupported' => ['INFERENCE_PROFILE']
139+
}
140+
end
141+
142+
let(:us_prefixed_model) do
143+
{
144+
'modelId' => 'us.anthropic.claude-opus-4-1-20250805-v1:0',
145+
'inferenceTypesSupported' => ['INFERENCE_PROFILE']
146+
}
147+
end
148+
149+
it 'adds eu. prefix for inference profile models' do
150+
result = provider_instance.send(:model_id_with_region,
151+
inference_profile_model['modelId'],
152+
inference_profile_model)
153+
expect(result).to eq('eu.anthropic.claude-3-7-sonnet-20250219-v1:0')
154+
end
155+
156+
it 'adds eu. prefix to us. prefixed model' do
157+
result = provider_instance.send(:model_id_with_region,
158+
us_prefixed_model['modelId'],
159+
us_prefixed_model)
160+
expect(result).to eq('eu.anthropic.claude-opus-4-1-20250805-v1:0')
161+
end
162+
end
163+
164+
context 'with AP region configured' do
165+
let(:provider_instance) do
166+
allow(RubyLLM.config).to receive(:bedrock_region).and_return('ap-south-1')
167+
provider = RubyLLM::Providers::Bedrock.new(RubyLLM.config)
168+
provider.extend(described_class)
169+
provider
170+
end
171+
172+
it 'adds ap. prefix to existing us. prefixed model' do
173+
model_data = {
174+
'modelId' => 'us.anthropic.claude-opus-4-1-20250805-v1:0',
175+
'inferenceTypesSupported' => ['INFERENCE_PROFILE']
176+
}
177+
178+
result = provider_instance.send(:model_id_with_region,
179+
model_data['modelId'],
180+
model_data)
181+
expect(result).to eq('ap.anthropic.claude-opus-4-1-20250805-v1:0')
182+
end
183+
end
184+
185+
context 'with region prefix edge cases' do
186+
it 'handles empty region gracefully' do
187+
allow(RubyLLM.config).to receive(:bedrock_region).and_return('')
188+
provider = RubyLLM::Providers::Bedrock.new(RubyLLM.config)
189+
provider.extend(described_class)
190+
191+
model_data = {
192+
'modelId' => 'anthropic.claude-opus-4-1-20250805-v1:0',
193+
'inferenceTypesSupported' => ['INFERENCE_PROFILE']
194+
}
195+
196+
result = provider.send(:model_id_with_region,
197+
model_data['modelId'],
198+
model_data)
199+
expect(result).to eq('us.anthropic.claude-opus-4-1-20250805-v1:0')
200+
end
201+
202+
it 'extracts region prefix from various AWS regions' do
203+
regions_and_expected_prefixes = {
204+
'eu-west-3' => 'eu',
205+
'ap-south-1' => 'ap',
206+
'ca-central-1' => 'ca',
207+
'sa-east-1' => 'sa'
208+
}
209+
210+
regions_and_expected_prefixes.each do |region, expected_prefix|
211+
allow(RubyLLM.config).to receive(:bedrock_region).and_return(region)
212+
provider = RubyLLM::Providers::Bedrock.new(RubyLLM.config)
213+
provider.extend(described_class)
214+
215+
model_data = {
216+
'modelId' => 'anthropic.claude-opus-4-1-20250805-v1:0',
217+
'inferenceTypesSupported' => ['INFERENCE_PROFILE']
218+
}
219+
220+
result = provider.send(:model_id_with_region,
221+
model_data['modelId'],
222+
model_data)
223+
expect(result).to eq("#{expected_prefix}.anthropic.claude-opus-4-1-20250805-v1:0")
224+
end
225+
end
226+
end
227+
end
104228
end

0 commit comments

Comments
 (0)