From 0d107996ffc8767abe542360505fcea3e5619181 Mon Sep 17 00:00:00 2001 From: Supernova Date: Wed, 14 Oct 2026 19:16:06 +0000 Subject: [PATCH 1/2] Price us. Claude ids at the regional rate and fix Sonnet 5.5 cache reads Anthropic's pricing page (read 14 Oct 2026) says regional and multi-region Bedrock endpoints carry a 10% premium over global endpoints, and that Sonnet 5.5 and Opus 5.5 cache hits are 0.05x input. The registry priced us. ids at the global rate and Sonnet 5.5 cache reads at 0.1x. The AWS bill for 5-12 Oct 2026 shows us. Sonnet 5 at 2.20/11/0.22/2.75 and us. Sonnet 5.5 at 2.20/11/0.11/2.75, which matches the regional rule. Set 12 us. Claude entries to the regional price in both pricing fields, and set the eu., global. and bare bedrock Sonnet 5.5 cache reads to 0.05x input. Haiku 4.5, Opus 4.1 and Sonnet 4 are unchanged. Co-Authored-By: Claude Opus 5.5 Co-authored-by: Sam Boland --- lib/ruby_llm/models.json | 204 +++++++++--------- .../models_bedrock_regional_pricing_spec.rb | 96 +++++++++ spec/ruby_llm/models_fable_5_1_spec.rb | 21 +- spec/ruby_llm/models_opus_5_5_spec.rb | 13 +- spec/ruby_llm/models_sonnet_5_5_spec.rb | 29 ++- 5 files changed, 232 insertions(+), 131 deletions(-) create mode 100644 spec/ruby_llm/models_bedrock_regional_pricing_spec.rb diff --git a/lib/ruby_llm/models.json b/lib/ruby_llm/models.json index ad3ad7427..9f37c75ef 100644 --- a/lib/ruby_llm/models.json +++ b/lib/ruby_llm/models.json @@ -12182,7 +12182,7 @@ "standard": { "input_per_million": 2, "output_per_million": 10, - "cache_read_input_per_million": 0.2, + "cache_read_input_per_million": 0.1, "cache_write_input_per_million": 2.5 } } @@ -12209,7 +12209,7 @@ "cost": { "input": 2, "output": 10, - "cache_read": 0.2, + "cache_read": 0.1, "cache_write": 2.5 }, "limit": { @@ -14033,7 +14033,7 @@ "standard": { "input_per_million": 2.2, "output_per_million": 11, - "cache_read_input_per_million": 0.22, + "cache_read_input_per_million": 0.11, "cache_write_input_per_million": 2.75 } } @@ -14060,7 +14060,7 @@ "cost": { "input": 2.2, "output": 11, - "cache_read": 0.22, + "cache_read": 0.11, "cache_write": 2.75 }, "limit": { @@ -14893,7 +14893,7 @@ "standard": { "input_per_million": 2, "output_per_million": 10, - "cache_read_input_per_million": 0.2, + "cache_read_input_per_million": 0.1, "cache_write_input_per_million": 2.5 } } @@ -14920,7 +14920,7 @@ "cost": { "input": 2, "output": 10, - "cache_read": 0.2, + "cache_read": 0.1, "cache_write": 2.5 }, "limit": { @@ -19413,10 +19413,10 @@ "pricing": { "text_tokens": { "standard": { - "input_per_million": 10, - "output_per_million": 50, - "cache_read_input_per_million": 1, - "cache_write_input_per_million": 12.5 + "input_per_million": 11, + "output_per_million": 55, + "cache_read_input_per_million": 1.1, + "cache_write_input_per_million": 13.75 } } }, @@ -19465,10 +19465,10 @@ } ], "cost": { - "input": 10, - "output": 50, - "cache_read": 1, - "cache_write": 12.5 + "input": 11, + "output": 55, + "cache_read": 1.1, + "cache_write": 13.75 }, "limit": { "context": 1000000, @@ -19504,10 +19504,10 @@ "pricing": { "text_tokens": { "standard": { - "input_per_million": 10, - "output_per_million": 50, - "cache_read_input_per_million": 0.25, - "cache_write_input_per_million": 12.5 + "input_per_million": 11, + "output_per_million": 55, + "cache_read_input_per_million": 0.275, + "cache_write_input_per_million": 13.75 } } }, @@ -19556,10 +19556,10 @@ } ], "cost": { - "input": 10, - "output": 50, - "cache_read": 0.25, - "cache_write": 12.5 + "input": 11, + "output": 55, + "cache_read": 0.275, + "cache_write": 13.75 }, "limit": { "context": 1000000, @@ -19770,10 +19770,10 @@ "pricing": { "text_tokens": { "standard": { - "input_per_million": 5, - "output_per_million": 25, - "cache_read_input_per_million": 0.5, - "cache_write_input_per_million": 6.25 + "input_per_million": 5.5, + "output_per_million": 27.5, + "cache_read_input_per_million": 0.55, + "cache_write_input_per_million": 6.875 } } }, @@ -19824,10 +19824,10 @@ } ], "cost": { - "input": 5, - "output": 25, - "cache_read": 0.5, - "cache_write": 6.25 + "input": 5.5, + "output": 27.5, + "cache_read": 0.55, + "cache_write": 6.875 }, "limit": { "context": 200000, @@ -19865,10 +19865,10 @@ "pricing": { "text_tokens": { "standard": { - "input_per_million": 5, - "output_per_million": 25, - "cache_read_input_per_million": 0.5, - "cache_write_input_per_million": 6.25 + "input_per_million": 5.5, + "output_per_million": 27.5, + "cache_read_input_per_million": 0.55, + "cache_write_input_per_million": 6.875 } } }, @@ -19920,10 +19920,10 @@ } ], "cost": { - "input": 5, - "output": 25, - "cache_read": 0.5, - "cache_write": 6.25 + "input": 5.5, + "output": 27.5, + "cache_read": 0.55, + "cache_write": 6.875 }, "limit": { "context": 1000000, @@ -19960,10 +19960,10 @@ "pricing": { "text_tokens": { "standard": { - "input_per_million": 5, - "output_per_million": 25, - "cache_read_input_per_million": 0.5, - "cache_write_input_per_million": 6.25 + "input_per_million": 5.5, + "output_per_million": 27.5, + "cache_read_input_per_million": 0.55, + "cache_write_input_per_million": 6.875 } } }, @@ -20012,10 +20012,10 @@ } ], "cost": { - "input": 5, - "output": 25, - "cache_read": 0.5, - "cache_write": 6.25 + "input": 5.5, + "output": 27.5, + "cache_read": 0.55, + "cache_write": 6.875 }, "limit": { "context": 1000000, @@ -20052,10 +20052,10 @@ "pricing": { "text_tokens": { "standard": { - "input_per_million": 5, - "output_per_million": 25, - "cache_read_input_per_million": 0.5, - "cache_write_input_per_million": 6.25 + "input_per_million": 5.5, + "output_per_million": 27.5, + "cache_read_input_per_million": 0.55, + "cache_write_input_per_million": 6.875 } } }, @@ -20104,10 +20104,10 @@ } ], "cost": { - "input": 5, - "output": 25, - "cache_read": 0.5, - "cache_write": 6.25 + "input": 5.5, + "output": 27.5, + "cache_read": 0.55, + "cache_write": 6.875 }, "limit": { "context": 1000000, @@ -20142,10 +20142,10 @@ "pricing": { "text_tokens": { "standard": { - "input_per_million": 5, - "output_per_million": 25, - "cache_read_input_per_million": 0.5, - "cache_write_input_per_million": 6.25 + "input_per_million": 5.5, + "output_per_million": 27.5, + "cache_read_input_per_million": 0.55, + "cache_write_input_per_million": 6.875 } } }, @@ -20169,10 +20169,10 @@ } ], "cost": { - "input": 5, - "output": 25, - "cache_read": 0.5, - "cache_write": 6.25 + "input": 5.5, + "output": 27.5, + "cache_read": 0.55, + "cache_write": 6.875 }, "limit": { "context": 1000000, @@ -20208,10 +20208,10 @@ "pricing": { "text_tokens": { "standard": { - "input_per_million": 4, - "output_per_million": 20, - "cache_read_input_per_million": 0.2, - "cache_write_input_per_million": 5 + "input_per_million": 4.4, + "output_per_million": 22, + "cache_read_input_per_million": 0.22, + "cache_write_input_per_million": 5.5 } } }, @@ -20235,10 +20235,10 @@ } ], "cost": { - "input": 4, - "output": 20, - "cache_read": 0.2, - "cache_write": 5 + "input": 4.4, + "output": 22, + "cache_read": 0.22, + "cache_write": 5.5 }, "limit": { "context": 1000000, @@ -20328,10 +20328,10 @@ "pricing": { "text_tokens": { "standard": { - "input_per_million": 3, - "output_per_million": 15, - "cache_read_input_per_million": 0.3, - "cache_write_input_per_million": 3.75 + "input_per_million": 3.3, + "output_per_million": 16.5, + "cache_read_input_per_million": 0.33, + "cache_write_input_per_million": 4.125 } } }, @@ -20374,10 +20374,10 @@ } ], "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75 + "input": 3.3, + "output": 16.5, + "cache_read": 0.33, + "cache_write": 4.125 }, "limit": { "context": 200000, @@ -20415,10 +20415,10 @@ "pricing": { "text_tokens": { "standard": { - "input_per_million": 3, - "output_per_million": 15, - "cache_read_input_per_million": 0.3, - "cache_write_input_per_million": 3.75 + "input_per_million": 3.3, + "output_per_million": 16.5, + "cache_read_input_per_million": 0.33, + "cache_write_input_per_million": 4.125 } } }, @@ -20470,10 +20470,10 @@ } ], "cost": { - "input": 3, - "output": 15, - "cache_read": 0.3, - "cache_write": 3.75 + "input": 3.3, + "output": 16.5, + "cache_read": 0.33, + "cache_write": 4.125 }, "limit": { "context": 1000000, @@ -20510,10 +20510,10 @@ "pricing": { "text_tokens": { "standard": { - "input_per_million": 2, - "output_per_million": 10, - "cache_read_input_per_million": 0.2, - "cache_write_input_per_million": 2.5 + "input_per_million": 2.2, + "output_per_million": 11, + "cache_read_input_per_million": 0.22, + "cache_write_input_per_million": 2.75 } } }, @@ -20540,10 +20540,10 @@ } ], "cost": { - "input": 2, - "output": 10, - "cache_read": 0.2, - "cache_write": 2.5 + "input": 2.2, + "output": 11, + "cache_read": 0.22, + "cache_write": 2.75 }, "limit": { "context": 1000000, @@ -20579,10 +20579,10 @@ "pricing": { "text_tokens": { "standard": { - "input_per_million": 2, - "output_per_million": 10, - "cache_read_input_per_million": 0.2, - "cache_write_input_per_million": 2.5 + "input_per_million": 2.2, + "output_per_million": 11, + "cache_read_input_per_million": 0.11, + "cache_write_input_per_million": 2.75 } } }, @@ -20606,10 +20606,10 @@ } ], "cost": { - "input": 2, - "output": 10, - "cache_read": 0.2, - "cache_write": 2.5 + "input": 2.2, + "output": 11, + "cache_read": 0.11, + "cache_write": 2.75 }, "limit": { "context": 1000000, diff --git a/spec/ruby_llm/models_bedrock_regional_pricing_spec.rb b/spec/ruby_llm/models_bedrock_regional_pricing_spec.rb new file mode 100644 index 000000000..933ff8a3e --- /dev/null +++ b/spec/ruby_llm/models_bedrock_regional_pricing_spec.rb @@ -0,0 +1,96 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe RubyLLM::Models do + include_context 'with configured RubyLLM' + + # A us. inference profile is a regional Bedrock endpoint. Anthropic's pricing + # page (platform.claude.com/docs/en/about-claude/pricing, read 2026-10-14), + # under "Regional and multi-region endpoint pricing for Claude 4.5 models and + # beyond", says: "Bedrock offers two endpoint types: global endpoints + # (dynamic routing for maximum availability) and regional endpoints + # (guaranteed data routing through specific geographic regions)." and + # "Regional and multi-region endpoints include a 10% premium over global + # endpoints." + # + # Cache reads use the same page's footnotes. Footnote 1: "Cache hits and + # refreshes on Claude Fable 5.1 and Claude Mythos 5.1 are priced at 0.025x + # the base input price." Footnote 2: "Cache hits and refreshes on Claude + # Opus 5.5 and Claude Sonnet 5.5 are priced at 0.05x the base input price." + # "All other models use the standard 0.1x multiplier." + # + # The AWS bill for 2026-10-05 to 2026-10-12 (Cost Explorer, cost divided by + # usage, USW2-MP:USW2_* usage types) matches this rule: Sonnet 5 on us. + # billed 2.20 / 11 / 0.22 / 2.75 and Sonnet 5.5 on us. billed + # 2.20 / 11 / 0.11 / 2.75 (input/output/cache_read/cache_write per million + # tokens). + # + # Unchanged on purpose: + # - Haiku 4.5 billed 1.034 / 5.17 / 0.1034 / 1.2925, which is 94% of its + # regional rate, for a reason not yet known. Its global price + # (1 / 5 / 0.10 / 1.25) is closer to the bill, so it stays at that price. + # - Opus 4.1 is older than Claude 4.5, so it has no regional premium. + # - Sonnet 4 has no pricing in the registry. + sonnet45 = { input: 3.3, output: 16.5, cache_read: 0.33, cache_write: 4.125 } + opus45 = { input: 5.5, output: 27.5, cache_read: 0.55, cache_write: 6.875 } + regional = { + 'us.anthropic.claude-sonnet-4-5-20250929-v1:0' => sonnet45, + 'us.anthropic.claude-sonnet-4-6' => sonnet45, + 'us.anthropic.claude-opus-4-5-20251101-v1:0' => opus45, + 'us.anthropic.claude-opus-4-6-v1' => opus45, + 'us.anthropic.claude-opus-4-7' => opus45, + 'us.anthropic.claude-opus-4-8' => opus45, + 'us.anthropic.claude-opus-5' => opus45, + 'us.anthropic.claude-sonnet-5' => { input: 2.2, output: 11, cache_read: 0.22, cache_write: 2.75 }, + 'us.anthropic.claude-sonnet-5-5' => { input: 2.2, output: 11, cache_read: 0.11, cache_write: 2.75 }, + 'us.anthropic.claude-opus-5-5' => { input: 4.4, output: 22, cache_read: 0.22, cache_write: 5.5 }, + 'us.anthropic.claude-fable-5' => { input: 11, output: 55, cache_read: 1.1, cache_write: 13.75 }, + 'us.anthropic.claude-fable-5-1' => { input: 11, output: 55, cache_read: 0.275, cache_write: 13.75 } + } + + unchanged = { + 'us.anthropic.claude-haiku-4-5-20251001-v1:0' => { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 }, + 'us.anthropic.claude-opus-4-1-20250805-v1:0' => { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 } + } + + # Match on provider and raw id: RubyLLM.models.find(id, :bedrock) + # renormalizes the region prefix to the configured bedrock_region. + def bedrock_entry(id) + RubyLLM.models.all.find { |m| m.provider == 'bedrock' && m.id == id } + end + + def standard_pricing(model) + model.pricing.text_tokens.standard.to_h.slice( + :input_per_million, :output_per_million, + :cache_read_input_per_million, :cache_write_input_per_million + ) + end + + def expected_standard(cost) + { + input_per_million: cost[:input], + output_per_million: cost[:output], + cache_read_input_per_million: cost[:cache_read], + cache_write_input_per_million: cost[:cache_write] + } + end + + regional.merge(unchanged).each do |id, cost| + it "prices #{id} at #{cost.values.join(' / ')} in both price fields" do + model = bedrock_entry(id) + + expect(model).not_to be_nil + expect(model.metadata[:cost]).to eq(cost) + expect(standard_pricing(model)).to eq(expected_standard(cost)) + end + end + + it 'keeps us.anthropic.claude-sonnet-4-20250514-v1:0 without pricing' do + model = bedrock_entry('us.anthropic.claude-sonnet-4-20250514-v1:0') + + expect(model).not_to be_nil + expect(model.metadata[:cost]).to be_nil + expect(model.pricing.to_h).to eq({}) + end +end diff --git a/spec/ruby_llm/models_fable_5_1_spec.rb b/spec/ruby_llm/models_fable_5_1_spec.rb index 87a5ee8df..6c9d8e216 100644 --- a/spec/ruby_llm/models_fable_5_1_spec.rb +++ b/spec/ruby_llm/models_fable_5_1_spec.rb @@ -5,21 +5,18 @@ RSpec.describe RubyLLM::Models do include_context 'with configured RubyLLM' - # cache_read_input_per_million (0.25 on the base/global/US entries, 0.275 on - # EU) is NOT independently verified against Anthropic's live pricing page. - # It comes from Anthropic's Claude Fable 5.1 launch announcement - # (anthropic.com/claude-fable-and-mythos-5-1) and same-day AWS/Microsoft - # partner blog posts reporting a ~75% cache-read price cut versus Claude - # Fable 5's cache_read_input_per_million of 1 (i.e. roughly 1/4 of Fable 5's - # rate, scaled per-region the same way Fable 5's own regional entries are). - # Base input/output pricing ($10/$50) and cache_write_input_per_million - # (12.5, i.e. 1.25x input, unchanged from Fable 5) are confirmed unchanged - # by the same sources. Revisit the cache_read figures once models.dev or - # Anthropic's pricing page publish real data for claude-fable-5-1. + # Prices are per million tokens (input/output/cache_read/cache_write). + # Anthropic's pricing page (platform.claude.com/docs/en/about-claude/pricing, + # read 2026-10-14) lists Claude Fable 5.1 at 10 / 50 / 0.25 / 12.5 on global + # endpoints. The 0.25 cache read is verified by its footnote 1: "Cache hits + # and refreshes on Claude Fable 5.1 and Claude Mythos 5.1 are priced at + # 0.025x the base input price." The eu. and us. entries are regional + # endpoints and are recorded at 1.1x: the same page says "Regional and + # multi-region endpoints include a 10% premium over global endpoints." { 'claude-fable-5-1' => { input: 10, output: 50, cache_read: 0.25, cache_write: 12.5 }, 'global.anthropic.claude-fable-5-1' => { input: 10, output: 50, cache_read: 0.25, cache_write: 12.5 }, - 'us.anthropic.claude-fable-5-1' => { input: 10, output: 50, cache_read: 0.25, cache_write: 12.5 }, + 'us.anthropic.claude-fable-5-1' => { input: 11, output: 55, cache_read: 0.275, cache_write: 13.75 }, 'eu.anthropic.claude-fable-5-1' => { input: 11, output: 55, cache_read: 0.275, cache_write: 13.75 } }.each do |id, cost| it "registers #{id} with the documented effort values and pricing" do diff --git a/spec/ruby_llm/models_opus_5_5_spec.rb b/spec/ruby_llm/models_opus_5_5_spec.rb index 0eed9c190..6b7d58a38 100644 --- a/spec/ruby_llm/models_opus_5_5_spec.rb +++ b/spec/ruby_llm/models_opus_5_5_spec.rb @@ -9,16 +9,15 @@ # id is recorded at the same rate structure as that id's Opus 5 entry. Base # rates (input/output/cache_read/cache_write per million tokens) come from # AWS's price list ("Standard, Global" tier for Claude Opus 5.5 on Amazon - # Bedrock), and only the eu. entry is recorded at 1.1x base, matching the - # 1.1x the eu.anthropic.claude-opus-5 entry already uses. This does not - # correct the separate, pre-existing question of whether us. (and other - # non-eu regional prefixes) should also be at 1.1x on Bedrock's own price - # list; that is out of scope here and applies equally to the existing Opus - # 5 entries. + # Bedrock). The eu. and us. entries are regional endpoints and are recorded + # at 1.1x base: Anthropic's pricing page + # (platform.claude.com/docs/en/about-claude/pricing, read 2026-10-14) says + # "Regional and multi-region endpoints include a 10% premium over global + # endpoints." { 'claude-opus-5-5' => { input: 4, output: 20, cache_read: 0.20, cache_write: 5 }, 'global.anthropic.claude-opus-5-5' => { input: 4, output: 20, cache_read: 0.20, cache_write: 5 }, - 'us.anthropic.claude-opus-5-5' => { input: 4, output: 20, cache_read: 0.20, cache_write: 5 }, + 'us.anthropic.claude-opus-5-5' => { input: 4.4, output: 22, cache_read: 0.22, cache_write: 5.5 }, 'eu.anthropic.claude-opus-5-5' => { input: 4.4, output: 22, cache_read: 0.22, cache_write: 5.5 } }.each do |id, cost| it "registers #{id} with the documented effort values and pricing" do diff --git a/spec/ruby_llm/models_sonnet_5_5_spec.rb b/spec/ruby_llm/models_sonnet_5_5_spec.rb index ae773dd55..76a7d6ff5 100644 --- a/spec/ruby_llm/models_sonnet_5_5_spec.rb +++ b/spec/ruby_llm/models_sonnet_5_5_spec.rb @@ -5,11 +5,19 @@ RSpec.describe RubyLLM::Models do include_context 'with configured RubyLLM' - # Pricing is unchanged from each id's Claude Sonnet 5 entry: every id except - # eu. is 2 / 10 / 0.20 / 2.5 (input/output/cache_read/cache_write per million - # tokens), and eu. is 2.2 / 11 / 0.22 / 2.75. Source: the AWS Marketplace rate - # card for the Claude Sonnet 5.5 offer accepted on the appfolio-ci-dev-tooling - # account, global standard tier, checked 2026-10-05. + # Prices (input/output/cache_read/cache_write per million tokens) come from + # Anthropic's pricing page (platform.claude.com/docs/en/about-claude/pricing, + # read 2026-10-14): global list price is 2 / 10 / 0.10 / 2.5, "Regional and + # multi-region endpoints include a 10% premium over global endpoints", and + # "Cache hits and refreshes on Claude Opus 5.5 and Claude Sonnet 5.5 are + # priced at 0.05x the base input price." The AWS bill for 2026-10-05 to + # 2026-10-12 (Cost Explorer, USW2-MP:USW2_* usage types) shows us. at + # 2.20 / 11 / 0.11 / 2.75, which matches that rule. + # + # The global. and bare bedrock entries are at the global price with the + # 0.05x cache read, and eu. is at the regional price. The au., jp., + # anthropic and vertexai entries are not called by Supernova and keep their + # earlier values (cache read still at 0.1x input). # # Unlike Sonnet 5, these entries have no toggle reasoning option: Sonnet 5.5 # rejects thinking: {type: "disabled"} (verified live on the @@ -17,15 +25,16 @@ # container) and only accepts adaptive thinking with output_config.effort, # the same as Opus 5.5. standard = { input: 2, output: 10, cache_read: 0.20, cache_write: 2.5 } - eu = { input: 2.2, output: 11, cache_read: 0.22, cache_write: 2.75 } + global = { input: 2, output: 10, cache_read: 0.1, cache_write: 2.5 } + regional = { input: 2.2, output: 11, cache_read: 0.11, cache_write: 2.75 } { %w[anthropic claude-sonnet-5-5] => standard, %w[vertexai claude-sonnet-5-5] => standard, - %w[bedrock anthropic.claude-sonnet-5-5] => standard, - %w[bedrock global.anthropic.claude-sonnet-5-5] => standard, - %w[bedrock us.anthropic.claude-sonnet-5-5] => standard, - %w[bedrock eu.anthropic.claude-sonnet-5-5] => eu, + %w[bedrock anthropic.claude-sonnet-5-5] => global, + %w[bedrock global.anthropic.claude-sonnet-5-5] => global, + %w[bedrock us.anthropic.claude-sonnet-5-5] => regional, + %w[bedrock eu.anthropic.claude-sonnet-5-5] => regional, %w[bedrock au.anthropic.claude-sonnet-5-5] => standard, %w[bedrock jp.anthropic.claude-sonnet-5-5] => standard }.each do |(provider, id), cost| From a1d2f6dcf5ba353baa32224978b5e0371cd32333 Mon Sep 17 00:00:00 2001 From: Supernova Date: Wed, 14 Oct 2026 19:24:17 +0000 Subject: [PATCH 2/2] Exclude Faraday 2.0.0, which has no default adapter faraday-net_http 3.5.0 was released on 2026-10-11. No Faraday from 2.0.1 to 2.14.4 accepts it: they all require faraday-net_http >= 2.0, < 3.5. Faraday 2.0.0 declares no faraday-net_http dependency at all, so Bundler keeps faraday-net_http 3.5.0 and falls back to Faraday 2.0.0. That pair fails at connection setup with ":net_http is not registered on Faraday::Adapter", which breaks every CI test job. Exclude Faraday 2.0.0 in the gemspec so Bundler picks a Faraday 2.x that pins a compatible faraday-net_http. This is the same change as upstream crmne/ruby_llm commit ef2bebdb ("Exclude Faraday 2.0.0, which has no default adapter"). The gemspec spec now checks that the requirement accepts Faraday 1.10.3 and 2.14.4 and rejects 2.0.0. Co-Authored-By: Claude Opus 5.5 --- ruby_llm.gemspec | 2 +- spec/ruby_llm_gemspec_spec.rb | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/ruby_llm.gemspec b/ruby_llm.gemspec index 18ca04f77..45215d4c4 100644 --- a/ruby_llm.gemspec +++ b/ruby_llm.gemspec @@ -58,7 +58,7 @@ Gem::Specification.new do |spec| # Runtime dependencies spec.add_dependency 'base64' spec.add_dependency 'event_stream_parser', '~> 1' - spec.add_dependency 'faraday', ENV['FARADAY_VERSION'] || '>= 1.10.0' + spec.add_dependency 'faraday', ENV['FARADAY_VERSION'] || ['>= 1.10.0', '!= 2.0.0'] spec.add_dependency 'faraday-multipart', '>= 1' spec.add_dependency 'faraday-net_http', '>= 1' spec.add_dependency 'faraday-retry', '>= 1' diff --git a/spec/ruby_llm_gemspec_spec.rb b/spec/ruby_llm_gemspec_spec.rb index 48573b32b..2988a97f5 100644 --- a/spec/ruby_llm_gemspec_spec.rb +++ b/spec/ruby_llm_gemspec_spec.rb @@ -11,7 +11,14 @@ def runtime_dependency(name) end it 'keeps faraday compatible with Ruby < 4.0' do - expect(runtime_dependency('faraday').requirement.to_s).to eq('>= 1.10.0') + expect(runtime_dependency('faraday').requirement).to be_satisfied_by(Gem::Version.new('1.10.3')) + end + + it 'excludes Faraday 2.0.0, which ships without its default adapter' do + requirement = runtime_dependency('faraday').requirement + + expect(requirement).not_to be_satisfied_by(Gem::Version.new('2.0.0')) + expect(requirement).to be_satisfied_by(Gem::Version.new('2.14.4')) end it 'keeps faraday-retry compatible with Faraday v1 and v2' do