diff --git a/lib/ruby_llm/protocols/anthropic/streaming.rb b/lib/ruby_llm/protocols/anthropic/streaming.rb index e964ac2d4..b264043fd 100644 --- a/lib/ruby_llm/protocols/anthropic/streaming.rb +++ b/lib/ruby_llm/protocols/anthropic/streaming.rb @@ -9,6 +9,12 @@ class Anthropic module Streaming private + def stream_response(payload, additional_headers = {}, &) + # Avoid Net::HTTP's auto-inflate: Cloudflare's gzip flushes + # infrequently for SSE, batching chunks until each flush. + super(payload, additional_headers.merge('Accept-Encoding' => 'identity'), &) + end + def stream_url completion_url end diff --git a/spec/ruby_llm/protocols/anthropic/streaming_spec.rb b/spec/ruby_llm/protocols/anthropic/streaming_spec.rb index 5342768b6..3790d08b0 100644 --- a/spec/ruby_llm/protocols/anthropic/streaming_spec.rb +++ b/spec/ruby_llm/protocols/anthropic/streaming_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe RubyLLM::Protocols::Anthropic::Streaming do + include_context 'with configured RubyLLM' + let(:protocol) do RubyLLM::Protocols::Anthropic.allocate.tap do |object| object.instance_variable_set(:@model, instance_double(RubyLLM::Model, id: 'claude-sonnet-4-5')) @@ -21,4 +23,21 @@ expect(chunk.finish_reason).to eq('end_turn') end + + it 'sends Accept-Encoding: identity on streaming requests' do + captured = nil + + stub_request(:post, %r{api\.anthropic\.com/v1/messages}) + .with { |req| captured = req.headers['Accept-Encoding'] } + .to_return( + status: 200, + body: '', + headers: { 'Content-Type' => 'text/event-stream' } + ) + + chat = RubyLLM.chat(model: 'claude-haiku-4-5', provider: :anthropic) + chat.ask('hi') { |_chunk| nil } + + expect(captured).to eq('identity') + end end