From e55a5332c45443ecda429423962ed3a194f9d987 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Lilleb=C3=B8?= Date: Thu, 7 May 2026 11:18:41 +0200 Subject: [PATCH 1/2] Fix "JSON.generate: UTF-8 string passed as BINARY" warning for text attachments All four content-loading paths in Attachment return ASCII-8BIT-tagged strings (File.binread, binmode tempfile reads, ActiveStorage downloads, Faraday response bodies). For text/* attachments the bytes are valid UTF-8 but carry the binary tag, which propagates into the request body and trips json's deprecation warning (hard error in json 3.0). Re-tag the content as UTF-8 once the mime type is known to be text-like. --- lib/ruby_llm/attachment.rb | 2 ++ spec/fixtures/multilingual.txt | 1 + spec/ruby_llm/attachment_spec.rb | 6 ++++++ 3 files changed, 9 insertions(+) create mode 100644 spec/fixtures/multilingual.txt diff --git a/lib/ruby_llm/attachment.rb b/lib/ruby_llm/attachment.rb index f5098365f..471c1d62f 100644 --- a/lib/ruby_llm/attachment.rb +++ b/lib/ruby_llm/attachment.rb @@ -53,6 +53,8 @@ def content nil end + @content&.force_encoding(Encoding::UTF_8) if text? + @content end diff --git a/spec/fixtures/multilingual.txt b/spec/fixtures/multilingual.txt new file mode 100644 index 000000000..a6042ec87 --- /dev/null +++ b/spec/fixtures/multilingual.txt @@ -0,0 +1 @@ +Hei på deg diff --git a/spec/ruby_llm/attachment_spec.rb b/spec/ruby_llm/attachment_spec.rb index 2bcd8cd92..fa96e706b 100644 --- a/spec/ruby_llm/attachment_spec.rb +++ b/spec/ruby_llm/attachment_spec.rb @@ -22,4 +22,10 @@ expect(status.success?).to be(true), stderr expect(stdout.strip).to eq('ruby.txt,text/plain') end + + it 'serializes non-ASCII text/* attachment content as JSON' do + attachment = RubyLLM::Attachment.new('spec/fixtures/multilingual.txt') + + expect { JSON.generate(text: attachment.content) }.not_to output.to_stderr + end end From a9fc8490bfb914aa53d902e5d4e96d4be5f17c06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Lilleb=C3=B8?= Date: Thu, 7 May 2026 11:33:14 +0200 Subject: [PATCH 2/2] Address rubocop offenses Disable Metrics/PerceivedComplexity inline on Attachment#content, matching the established pattern in lib/ruby_llm/error.rb, models.rb, and stream_accumulator.rb. Use described_class in the new spec. --- lib/ruby_llm/attachment.rb | 2 +- spec/ruby_llm/attachment_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ruby_llm/attachment.rb b/lib/ruby_llm/attachment.rb index 471c1d62f..36251e136 100644 --- a/lib/ruby_llm/attachment.rb +++ b/lib/ruby_llm/attachment.rb @@ -37,7 +37,7 @@ def active_storage? @source.is_a?(ActiveStorage::Attached::Many) end - def content + def content # rubocop:disable Metrics/PerceivedComplexity return @content if defined?(@content) && !@content.nil? if url? diff --git a/spec/ruby_llm/attachment_spec.rb b/spec/ruby_llm/attachment_spec.rb index fa96e706b..2096200f3 100644 --- a/spec/ruby_llm/attachment_spec.rb +++ b/spec/ruby_llm/attachment_spec.rb @@ -24,7 +24,7 @@ end it 'serializes non-ASCII text/* attachment content as JSON' do - attachment = RubyLLM::Attachment.new('spec/fixtures/multilingual.txt') + attachment = described_class.new('spec/fixtures/multilingual.txt') expect { JSON.generate(text: attachment.content) }.not_to output.to_stderr end