From 2d6b7d17e19303db36a94f395fe1271830921673 Mon Sep 17 00:00:00 2001 From: Manuel Meurer Date: Thu, 2 Oct 2025 21:21:07 +0200 Subject: [PATCH 1/2] fix handling activestorage attachments --- lib/ruby_llm/active_record/acts_as_legacy.rb | 4 +- lib/ruby_llm/active_record/chat_methods.rb | 4 +- spec/dummy/app/models/document.rb | 6 + .../20251002152808_create_documents.rb | 10 ++ spec/dummy/db/schema.rb | 8 +- ...les_a_single_attachment_in_ask_method.yml} | 0 ...vestorage_attached_many_in_ask_method.yml} | 0 ...tivestorage_attached_one_in_ask_method.yml | 123 ++++++++++++++++++ ...les_multiple_attachments_in_ask_method.yml | 123 ++++++++++++++++++ .../active_record/acts_as_attachment_spec.rb | 49 ++++--- spec/ruby_llm/active_record/acts_as_spec.rb | 14 +- 11 files changed, 315 insertions(+), 26 deletions(-) create mode 100644 spec/dummy/app/models/document.rb create mode 100644 spec/dummy/db/migrate/20251002152808_create_documents.rb rename spec/fixtures/vcr_cassettes/{activerecord_actsas_attachment_handling_handles_attachments_in_ask_method.yml => activerecord_actsas_attachment_handling_handles_a_single_attachment_in_ask_method.yml} (100%) rename spec/fixtures/vcr_cassettes/{activerecord_actsas_attachment_handling_handles_multiple_attachments.yml => activerecord_actsas_attachment_handling_handles_activestorage_attached_many_in_ask_method.yml} (100%) create mode 100644 spec/fixtures/vcr_cassettes/activerecord_actsas_attachment_handling_handles_activestorage_attached_one_in_ask_method.yml create mode 100644 spec/fixtures/vcr_cassettes/activerecord_actsas_attachment_handling_handles_multiple_attachments_in_ask_method.yml diff --git a/lib/ruby_llm/active_record/acts_as_legacy.rb b/lib/ruby_llm/active_record/acts_as_legacy.rb index 97679c126..d01b44ad8 100644 --- a/lib/ruby_llm/active_record/acts_as_legacy.rb +++ b/lib/ruby_llm/active_record/acts_as_legacy.rb @@ -302,7 +302,9 @@ def prepare_for_active_storage(attachments) case attachment when ActionDispatch::Http::UploadedFile, ActiveStorage::Blob attachment - when ActiveStorage::Attached::One, ActiveStorage::Attached::Many + when ActiveStorage::Attached::One + attachment.blob + when ActiveStorage::Attached::Many attachment.blobs when Hash attachment.values.map { |v| prepare_for_active_storage(v) } diff --git a/lib/ruby_llm/active_record/chat_methods.rb b/lib/ruby_llm/active_record/chat_methods.rb index 173f14bf6..e55af3c63 100644 --- a/lib/ruby_llm/active_record/chat_methods.rb +++ b/lib/ruby_llm/active_record/chat_methods.rb @@ -307,7 +307,9 @@ def prepare_for_active_storage(attachments) case attachment when ActionDispatch::Http::UploadedFile, ActiveStorage::Blob attachment - when ActiveStorage::Attached::One, ActiveStorage::Attached::Many + when ActiveStorage::Attached::One + attachment.blob + when ActiveStorage::Attached::Many attachment.blobs when Hash attachment.values.map { |v| prepare_for_active_storage(v) } diff --git a/spec/dummy/app/models/document.rb b/spec/dummy/app/models/document.rb new file mode 100644 index 000000000..5106652a4 --- /dev/null +++ b/spec/dummy/app/models/document.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +class Document < ApplicationRecord + has_one_attached :file + has_many_attached :files +end diff --git a/spec/dummy/db/migrate/20251002152808_create_documents.rb b/spec/dummy/db/migrate/20251002152808_create_documents.rb new file mode 100644 index 000000000..28ab5c6aa --- /dev/null +++ b/spec/dummy/db/migrate/20251002152808_create_documents.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class CreateDocuments < ActiveRecord::Migration[7.0] + def change + create_table :documents do |t| + t.string :title + t.timestamps + end + end +end diff --git a/spec/dummy/db/schema.rb b/spec/dummy/db/schema.rb index c4b0d8316..520a6143c 100644 --- a/spec/dummy/db/schema.rb +++ b/spec/dummy/db/schema.rb @@ -12,7 +12,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 20_250_602_134_116) do +ActiveRecord::Schema[8.0].define(version: 20_251_002_152_808) do create_table 'active_storage_attachments', force: :cascade do |t| t.string 'name', null: false t.string 'record_type', null: false @@ -49,6 +49,12 @@ t.index ['model_id'], name: 'index_chats_on_model_id' end + create_table 'documents', force: :cascade do |t| + t.string 'title' + t.datetime 'created_at', null: false + t.datetime 'updated_at', null: false + end + create_table 'messages', force: :cascade do |t| t.integer 'chat_id' t.string 'role' diff --git a/spec/fixtures/vcr_cassettes/activerecord_actsas_attachment_handling_handles_attachments_in_ask_method.yml b/spec/fixtures/vcr_cassettes/activerecord_actsas_attachment_handling_handles_a_single_attachment_in_ask_method.yml similarity index 100% rename from spec/fixtures/vcr_cassettes/activerecord_actsas_attachment_handling_handles_attachments_in_ask_method.yml rename to spec/fixtures/vcr_cassettes/activerecord_actsas_attachment_handling_handles_a_single_attachment_in_ask_method.yml diff --git a/spec/fixtures/vcr_cassettes/activerecord_actsas_attachment_handling_handles_multiple_attachments.yml b/spec/fixtures/vcr_cassettes/activerecord_actsas_attachment_handling_handles_activestorage_attached_many_in_ask_method.yml similarity index 100% rename from spec/fixtures/vcr_cassettes/activerecord_actsas_attachment_handling_handles_multiple_attachments.yml rename to spec/fixtures/vcr_cassettes/activerecord_actsas_attachment_handling_handles_activestorage_attached_many_in_ask_method.yml diff --git a/spec/fixtures/vcr_cassettes/activerecord_actsas_attachment_handling_handles_activestorage_attached_one_in_ask_method.yml b/spec/fixtures/vcr_cassettes/activerecord_actsas_attachment_handling_handles_activestorage_attached_one_in_ask_method.yml new file mode 100644 index 000000000..c8afb7197 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/activerecord_actsas_attachment_handling_handles_activestorage_attached_one_in_ask_method.yml @@ -0,0 +1,123 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.openai.com/v1/chat/completions + body: + encoding: UTF-8 + string: '{"model":"gpt-4.1-nano","messages":[{"role":"user","content":[{"type":"text","text":"What + do you see?"},{"type":"image_url","image_url":{"url":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAMBWAADAVgGB4Q5XAAA7zElEQVR42u29d7gkZ3Xn/zlvVXW4ae7koMRII2kklEAEkWQDC0YmrPFazgZsjBdY1mt71+bn3+6PXez1LhgvGAzCpMWA7cXGAfwY2zw2IhkUECAURnFGo8kz987NnSq85/dHVXVXVVeHOzPYwkvP00/3VFd3367zPd8T3/MK37v13VQ1fSqZO4Bm7ojId/1vle+Ju0/oAAZwM49OctwCQXKP/iWAQL4n+Jy2u8m9AmwCngxcD+xD9ZT1O5FttwPbaLTDpaV2MD/XCRYWbLS6SrS6Qvy4Spg+X0mPrcTHGk00isBa1FrUKhqlz/uPdc+L1KDqLdeMNirSsTBnVNrWsbxmKTqr3+9+T+hdLfeASWA38DzgJuApwCywDDQ1jLCtlkaNhtp2S1VVxXFUHAdxHEjukrnjpsdcxBiwESrpV2vGyEj3WPpUkydqVFqumI4HgK/wYCDcE1nnK78/43w7Qo4LRG9c6XyPAdZJ8U6i7TuAGxKhPx24KEP7vc8Iw1ibl1cyGr9CmGh+37GVFaK1HhOEq6vYdivV6q7WZzQ9xwbWWjpYWo6l4wgRYBEiBAtESCeCxyxyV4T8lcIXfFfnvUj45eX29xigRNudjPA3AJcDLwZeAlyBak1thIYhhAFqbWzjXQ/xPMR1cWY2xDTtd7B+B/GrGN9H/Q7aqWIrHaRSxVR8tFpFfR9brSK+j/E7aOCDVTAKKiASa7oIGEU0/n9ooGWsto2VqOt5Ss4LVagqsldhr8LNFu42oXwogE+/tzZxulG1vHkEEOT/EopPBV9LtPv5wMuw9pqosbYlmDsh/pHH8Q8dJDh+hOj0HHZtFQ18xHFxpqbxtu2gevGlVPdeTeWCi7ChxT9xnHBxoavdpSzQ9Qd6x4osQEbrgyjUtvXpEEmIFjW++5g/lrIDWAis8PUIftvC3wr4/3kICNx/wRTvJHcP2A5cC/yghuHzw8WFC9sHH3Wa93yTxr3fpPPwAwQnjmJXllG/DVGEoIl5FkQE47o4k5NUzruIiWc+l5mbXom3Z28cGvh+jwX8KraTskAHrVSxFR+ppmyRsID2WEBFiFDa1teOdiTCYpO/oKDxyfGBjIAKnoVnK3xC4QMR/M5bZ2sn/+tS+18mA5RQfOrQzQBXJPT+r6K11cta+x+ur9z+ZVa++kVaD9xHOHcC7XRAY6qXJKwzkqGP7nOJRaKKcV0qF+5m9uZXMfNvfoaw3aF9YD/h8hLR6tqYLNBGrSWKQtp+S1t+C6uRpEIu1/S8xqfPbRKTWsAKWTawEfyDFf69wMOihrcuN7/7ATDCi98DPAN4sVr7jGDu5KbVu+9i8da/Y+X2f6Tz+AFsq4GgGJFE8IJI72LkBU/ymmSeg6jFnd3I7I++mo2v+2UUQ2Pf/QQnjhOtrvVCwTIgrK4SrK7QbjVodhqEod/7baVCLlK9JOBgFADS57da4fWiPBIZeNti+7vTBJRouwfUgZ3AMxOH7rlq7U7/+FGz+NUvMP/Zv2T1G3cQzJ+KqV0EY2J6td00X+x8pcLVRBKSzf+JJrFZ/CBiCJeWWPw//xszvYFN//Y/MvOMZ9K45x6ajzyE6VQSJ7CDTanfr2E7HTqhrytrC/jtVvKBmd+Y0LrNUXue9m2R9nv0P+j4CxTeHwmvQTmS/T7nu1D4JhO6PRf4OeCXgddg7bXtY4dn5j77l/L4e3+b43/8Udbu/Rbh2mqs3cb0pNp36UFVuse6+d4MT4pk/xObC+20CY4dprrncmpXXEVl5y5MtUq4tIhttdAocfLCkNbCPIuPP8ryyaMSBZ0+9s3a+NQHsF2hZ5/3jvV8gx4AsgCxPVBcrFCN4PPPqbnR19rhdw8AMsJ3gC3Aq4D/F/h3wI3ADn9h3p37+7/hsXe/naOf+DBr++4lajVj4pYeuWvmamuJMVQkZxiLICmCABHsyjJYy+QNN2Kmpqls205l23ai5WXC5SU6C/Ocvu9bzN9/N52lRVBb/jvXKfgcAIQ8GMqPX6XwqMC9z5zwuL0VPvEBkBG+C+wC3gK8ObH11ajZYOG2r/DYe97BoY+8j5VvfwvbaibGWvoEmRWw5gSfEbZKARQFb6n7EanNUOzaCvXrn0Xlwt0xUqenqV98Ca35Ofvgxz9sFx950KgNKeAr9/cNFrz0sYMtRABlALBSjCDwVLg4Ev4aWL29/QQHQEH4FwD/HXg14Km1NB/bz+Mf/QD73/N2Tn/1i0Srq4n8pFR7VcgxgRZOygu6xEZIkQl6BUPttKjt2cvE9Td0XxTPY/Kyy2F2U3j07rt1rdES3/HwjUMkRiIxCSCzdn24xpdpv0pB6wcDABW2KxwUuPNpdfeJC4BE+JI4epcA/xP4UcAJV1c4+bef4eF3/AbH/uJP6Jw4AapdAWtBx1T6ta20kicF2k8FWar9BRBEEd7O85h67guRSrV3uuPI5muuM5uuuS6cv+++aO3kSScyLoE4+OLSMfHdF5fAuATGwYrBiohisJLmAyTx7KXPESyl/zJQxOeJwhYLnwaazhNc+C5wKfDbwA+ptabxyEMc+OB72H/LO1m+5240CHLKqhlJ6YAgNytQGRQJF8BUBEHfZ1nF3bKN6RfehJmcLn6hTD9pt7vred+nK/sf9VceO2DiyFJ6nr8IViQRvsGKgzUOagyIAWPikFUS75M0jVzQ+uHan/5/C/B14EHnCa75VwHvBG6Kmg058bnP8tBvv5Vjn/4UweJCX6tGn9eexu7pobxpzxyXHPPTFwFkQJCJCLq5g8QPcDZtYeZFL8OZ3VT622qbNzvnv/DFGrVb/vw933bUWhndTxB/USp8I4IrgmeEihGqxlAVwROJq1tJRquLy0x+IAVColhrgvy1+wQTfFb4TwXegepzm0ce5/CffIJDf/RRmocOxmlUYbCrrllLH2uJJKF8GudnAYFqXvJ9IFBsQicmpdf0s6R3ogYBGoRDf2dty5bK09/6W87Ezl2te97zzkpnackTY87omvUqyYKo4olQVTSKCYkQJQRCwFfFBwmBKAbDU0G3uE9Q4T8LeLeG4bWL37yTR9//Lk7+w98SNRq9X615wWfJoCsQ6SV5Sp05Lb6pJ3lJ6TIFk2hM1ZqPt7NvV5FeCrH/R8aFnyjCqDpX/exrJ6a3bu3c8Vtv1caJE5UzBYFNrp8FrGr3b1YUo0lbk2qcMVPVJDMoVnWHhfOcJ5Dw0wTP9wHvDNdWrz36mT/jwXf8BnP/+AVsp5MTXFaTpaeo+f/nwJFP9+bMQS40k377Ltnz8inh7nmqVC+6mNmX34yzYTYWeBRB4KPtFlFjFbuyTLS0SLQwT7hwWqY3bnQ37d7trxx6PGyePu2tt72sKHybCN+i2AQEVsmDo3fNa6je4z6BhO8BLwDe0z55fM/BT3yExz72AVpHj+SkKprR1wwTFLs3c5SfMEFceeu91s3sFphA0hCraA4UtIQJ0p9hNsxi6hMARGur2OW4umjbHbTTRjsdbPKonTa205EN01MTT7v5R/17/uLPm8cffLCWXIuzFr5Fk/9nX9euT6CqnsIl7hNI81+K6ttXHn7gkkfe/y6OfvpTBCvL5YInzwQygAnI0XMiXJW845hlla6w+x0B7eV8eiDI5Q8Ed+sOzMQkGoUEhx8nWlyMawF5oXfBkP7f8/3K1Tc8Q+qO6Rx84MFKFEXOMDboCjgj3LzGDxF+el782tPdJ4Dwq8ArNYresfDNO3c9+K7/wakv/D1RQvmS8fBzkVixnU7zQCmagFTYRSYYBIJe1aff19SENlTjeNwIiONRueBJmFqd8PQ8nUcfxjYbQwUf9w8EaBigUeg9afeFTkW0deDh/ZVOp+MWQaAZLR8mfJtcrxHCx8JO959Z+DXgNdbv/LcTt35u64PvfhuL37gztp3Su9hZn81oeR5Vct5/ni00AwIrYEhbr8YEQRaEGdqXxHaoVWR6kurFl4ExdB55CH//I4n2J4JPGkZskAg8jNAoRKMIjSKstWgUma2bN064l+4ODhx43G80214aoWqZfR9D+Knt7zsviVzdfybhO8AE8Kqo1fzNo3/zmY0PvOu3WHlwX87DKwpStBD+FDVe+tmiDARZczAIBLkQUbqZ/9znaWouVHG3bqe2ew/q+7Tuvgv/8EE0COPQMAx6go4i1EYZwfeea/yaTHpu5ZLzd3QOHTsZLDdalWHC79H9cOHnzusBYtL9ZxJ+HfgPYWPtzQc/+fGph275HRoHHxvu5esQwZN3DEX7Enal5oAxmEATEIhIzxGk4AAK1C69gsr5FxKcOEb7oX2ES0uxoGPNLhd+T+h9/3eiqHr+xqnIUdueb3Y8izolFF5w/NYRFcSAqLj/DMKfBd7kLy3+p4N/8rGph973v2gmnn6WxnWAABlC9bkQ7xwzgSbRAcWmC1VMrcbUU5+Os2Eja1/9Mu3jx5Imz4Jgi8IeCIakXVyts73uVQw2ONn0xaoaPROvf8DroMb9JxJ+2sGzCfh1f3npjY9+9P2Vhz/4bjqnTuXsPQUtlyH/LwVHIUQcBwRx0qRXO5AhTJCm8NO/VVVxt+9i6mnPhihi9c6v4a8ux59t+4Wf+38q8OyqIE0MS69U6WyuesYV6RxpdEw7sl4qyHEdv1JwxBfnO+cDlCy52gG8pXN67tWP/sHve498+L20M8JHy7W8WMQpo37KTPc6QWALIMgxQa6qmHBBtwAlTF51HRN7n0zn0EHW7vs2YaOBqu0Ttu1qdwRWUbVdQecf889VVaZdU71wshIebfrBSmBdi4qOKfwi/WvvPQvuP4HwPeA84Df9pcWfeOSj7zcPffDd+AsLSWK9RKharvXdrJwO+e5iTkD6wTQMBKK9foI0QWSzuQWJf1+cB1DMxCQbnvt83I2bWfjcZ2kdOVSg90S71cbZweTaFAXePZ6hwsI5UjfiXVivhIfxgwU/8iwq6xV+1zGMPzdw/wk0/3Lgf/pLCzc9/JH3mYc//F46Cwu58mpZcqcstcsQGy/DADECBP1JIS3Uf/NsICkTqFK/dC8zN9yIWsvybV/BX14CY2KQqCZC71HaGQg+pWsUcFD3gponnhAc74SOBUcHeP3DhG8VHOh8J30Ah7gv/x3B6sqL9//hR3joQ79HZ36uV7/XbDk1r/lmlJZLvzNY2vQxBgi6zzMgEFWsyTNB1z8gzgaaapWNz38x9T2X0TlyiJV77yaKYnofQud5IXcji149e9Q5As4OzxEXwiOdkJb2QDCuYwjgKUe+kwC4Bnhf2GzccOCTf8ADv/8u2onw+1K7BUEOC+O0hN51FAOM8AlyoElrBBKDQLO94tIDiFqoXngxm1/8Mkylyso37qR95HAMDmv7GHGYVqMDgDDsHDCbHFPxqm7nsXZg1xRv3HRw+vMNPO5+h+j/WcDvWd+//vHP/CkP3PJOmieO5/rss/a3TPgMcfBGefvrBcEg31PJgCCj/SKA67Lp+1/E5JXXoNay8OVbCTsdxHEyvYZ6ZoIvmIWC4HPnTwrVS6pueLAT+qcj66pihjmGueqbcsz9Dgj/+4APaBRdfuwLn2Pf+36H1cOPZ0KpDDVr/IdImTkoEbpmSmXrEWqfzS+cr4XmnzImEOnlCtQqtYsvZstLX4kzMUn7yGGWv3Fnov3aJ+RhdJ47ZwAoNKM1ZedUwL3YM3gQHAsjz8atAH0ZwezPdBRm9Bz5AJlY/2bgbcDuuW/czr2/+z9YfvgBMMlv1H5Pv1i9yxZ3yhI9A0O6s2CCXLGlPALtOoaKYipVttz0r5m57mkALH/jDlpHDiem35YwwFlQfla5dPDnGNS9wBHjqukcCq3bUXW1X/jd3+mCrSpt9xxq/vXA7wAXrOx/mH23/C9Of+tOsl9fRvvF/D5DwreBzts6QTDIfGhJc2nXMUx9AoX6nsvY/oofwZmcAmDpG3cSttvxhJA+wWcFNZzO1y34fh/D7DDUKq74j4bqN6xWKO+FwlP8yjk0AZPEizUuaM+f4oEP/i5HP/+32Mj2c6yWdesM8OYHMAHfISbI1/d7jRfZ6NCZmGDHK25m+snXAODPz7F421dj7bd61nS+3nOK0QUgs2j1ciOdA6r+olKRkss6oTQ9OO6eI+1/BfDSqN3iwF/8Hx77yz+JNUJ6jlOZg1fU/jKhFplAh9QAzsYn0LKIoBgiAhuufybb//WPYKo1AFbu/TZr+x+Nz7d2oI0/C63uhZPjnJM8n4TqpYbwoKU9r1Q0swzQAWpKQ2D+XDDAZuANam39yK2f44EPv4/WwgJiComWAgJFhydwyphgWEQwit4HCTaX6NH+5WKSqVNUt23n/J/+OSb3XNZ93/wXP0+wutKl/1LKH9OOK8OFOo7gs7+tAu4eg9Qt9qjihMlPqyjUlMiC754D7f9x4FlLD+3j/g+8h+UDj5Tm8vtq+YWUbpmHn3tegiY5RyFi1/OXAdoPiOuy7QdeytYX3oSYWJn8hdPMf/XLWJvks4cIcz0Cz047GVfgg24GnPMNTk3hcQvtmP5xIbAQuWcheIgXaL6pPT/n3v+RWzhxR2wLcxe+UJ/PmYGScEyGOX2F7M0gP4AR3n5OyFJu+9NscOq0Tj/5ai786Z+nsmlz9zNW7ruX1YcfxIqAteuj/CFaPQoUw4hy0LXbJlAx8JiFqdivWlRYdc9Q+OlkjtfYINi7/9N/yv5Pf4qw4yNmSJ6+jPbHsPGDMoBl54wb8mXz+2VgyAKhumkzF73qdWx4yvW59y/edQfB6iqIKdHqAQwwTPDZv7EkfCsLVdPnERBpbzqIEi8IibS3OigEpoB6/D1NOLMwMBX+tcBPnfrm19n3sQ/Rmp9HTK+HT0o0XEbk9XWcEm8JcEoTOgOSP4P8AJXyv0lcl+03vZxdr/gRjNeLqqJmg1Nf/DxRaBFHxg7rypxEzfbsky7syI14GSjowiiYTMdP7zOyz2cUXO0GOOOPiCks1Z4E3tg4fvRJ9374Fub33d93ZTUee9en7YOAMMwcjAMCO+T8su8YNCOgGKNufNoz2f3aN1LZsjX35648+AAL93w7HhhsbZx1K2h5ukJHc+yjubUE3YWbWrKYs+Rx2GsMCK1Nco4BJnr54AXOwAlMBzLdYIPglfv/+tM89rnPEoVh19Z3F2LogNBKxu/i0RFC7VubNwZoirSvAwypKtR3ncfu1/07Nlzz1L4L0VpYsPOLK9IJoqTZXMubVqFvANWQnhdKMDiwMCZljJfpVireKvFkyfR2XCA0Z6D9M8Br5u779qZ9f/gHtBeX8tSlvbSvFilX80gvuyAyREA6KGUrg8O8svfkhikVnqcnu9PTXPhTP8eOF78snvlbuE3vuVQnt++wYiHKaX+58FNNTB/77jJgkcuQhJkMcQLLzqlrXANIbseykdc4wk+1/3mdpcWbHvjjTzB37z1dbcnZnqzwS7wWHVHrZwwQlNL7AKDkqFYygh9gBsR12PmSl3PRz/w87tR0eepz1y4zu3evehbqErc99S1Nk7zAsyAoPpcBIJAxhC9jRAMmAUDmtgxjrkNL08dJ0uffHr3tH2ce+au/IPT9nv3JabfkmKDn7GQWOOhgCjwXILBlTFDUfCn//E1PfxZ73vArTJx/4WBbWKmy47k32tSv8SS+myECLGp9qYBluBaPE/qVvVZRqORnU9mRACi0c7vACxonjj9n35/8MStHj/acEu2lweO75kq/OadH+83BuQaBljGB5EeqZI9n7f70ZVdw6Zt+lQ1XX5evBCYzfUl6/QCZ3XuFmKqbp8h0hfgIbR5Xu2E8QIx6bUJzwo6AxXGjgLSxc7va6BcO3voP9cdv/TxqtVvmzXvl0l02qVmHRHqLKDTj5dkM9Y0TIo5yDLPfJSnMpT/cK8b+YqG2fQd7Xv8f2P7Cl3TtvoYhmgx37lJX0is+vWuX1jdvCRonTlSymmsGCEYKdY+ydMioxyEplIE3F6jntb8DnBgKgILjVwFeuXr02DP2feqTNObnemVdKWRWhO7SaTJLsLPHsq/JOQDBINha7Q/3tCQsRcHbtJGLX/tGzv+hH8NU4njftltECwtoGOQ93MS21SYm3JndFweNYycGTlzUASAoY69xBbreW812Y//0tgbMj8MAqfafr9a++tBXvugeuf02rI2HbvYt3MzG2ZphgMIyqiwDaAEE5ypE7EvuyIBoQ8GdmODCm3+ai1/zerwNszFHrq4QHD2C+p1S4atVxBjZcsUVevxrXxvq1EYMT4Z9J2+mX/shLgk0BgIgo/0e8QreH147fuzKB//yz2kvLna1K9eZK/m2p6y2a251TYF+C0wgA9igLKU7tLmzbABk0U9QcGo1Lnjlj3PpG36ZapLsCedP4e9/FNtplws/eS5iZPbCC8WpepENAochqds0W+dmLroyIicx5mvDwFfX2AEseak8E1iydv8y4FWHb/uac/i2jPYnwjPZXKvk/aYsCKz2NDznIBbMQfFcKROsltvNgc5jIU+c0r7xPHa+6Ae5/Bd/jckLd4MqwZFDtPfdh3bavetRcleNB8hNbZx1ahs22MbcvCNSLvzsFQ/pjTaXAbn9YfmP9QAlzfyVsM5awgJDTUA1Sfn+ZGdp6dL9f/PXNOfnchOyyDw3BaEgJWYgywTkncCcUDMgKO0nKJgDO4L2ixlIVTCuy84X/SBX/upbmN5zOVhL+8F9tO/9VryHwAjhp2zg2chs2LkraJyaH5mQSu8+8b5zRTbIhsrjgmBYwajaH/qltzkpMwGFsK9GvLDj5QsPPsDcXV/HFSHsXpis5kk8eKEwN0czDmGZdkN/bh7tB8Ewx9Cegc03rsu2G1/Alf/xvzD75GvRKKJ9z7do3vHVePCkJp29NpPSTH5zusSLrhkQM7V5k2FE5a5/54/YJKRAMIOylkMofxgYZLD2k0QAHR3AAJIIfwb4GRv4Tzr2j1/CP3WSquchUUQQRViNc99G+p2+7I9MfYO0GJFzAtOMhPZeKwOBDPAJKDiZZbmAXKinII7D9u//V1z95t9g47XXo0FA4ytfoHnn11A/yNn4+ItsQfjkct0iwvTMtBrXWLXWjBJ8UaChxmYh3d9GhqSwGcNkpNe/qlC1A9l9SSFysgAohH114u3TXtY6cZL5O27HiUI81+1+WRBF8WKD3KLJQm5d8zn29CtStiiCILtTB8VBjAOYAEPplFAtCfXEc9n+vBdy1a/9NzY95WnYTpu1v/87Gl/7UrxTWNaByTVzaO5LcsdFmJiaNNWJCdtaXTPIeILXQgUworeY0gx477iPI7S/mwV8XQkDpNq/ibjVa+vyg/toPPoIFceBwhTuIIq6o++FPOWnHr1NR6gV1gOWgSCldJGSMW/96zW7YJMBvgIF2t/xgh/g6l97K5uuvR7baLD0mU/RvONruaVcvfq99oUtWprOBNdxzNTG2bC5ujaSwsuErxmpdOjteCUDnMlRj5XBtr/LAFltL4Z9E8CzgRdqGLJ8z7fR1ZVY+5Ou1ywLBmq7TGAKIBjEBOm5g0BQFJ6UmQ7pz/Nn29C6GFAw1SrnveilXPUr/5mN1zyVcOE0i3/+SRp33dGXo86u1MkKWpUBxxRBzOTsrHDoiKar3Yc5gWXCz/4/He+aAmGUOcn+aQJMRiPzDcdyAMhEDTVgK/ATwGywvMzaQ/twVVHXhSjqK/XGTGCT5dCZvHsJCLLJHpthAilxDDOJxT5HUYZs5JDLDyiYWo0LX/bDXPUr/4WZy64gXFpk/uMfpvHNu3KbAqn2a73mWoi0sJInX/+dmpkWcRy1NpKhTqCOYIZC909Y4hvokHJ33ZbG/dlbCBzvAqBE+5+T3OmcOEZ49AgV1+nN1k+7XAoXPYxszifIOnApG5kCCLIXMOvs5QZDST7FIJmMYlkdVDKFpsrMDLtv/hn2vv6XmN69B//YEU79wYdo3nM32XV6+eyS5lmgZA1fFiip1lU8Tyr1mm2uNQb7ATqkR2GA2bAZ/8CUACH72Y7mOn6GAWCxyACp9m8j3pRhEqBz+BAsL1Nx3bhaQmY0Wkk8EoMgXo/WFUgmUrAjQEB+Y67yFict7yoqNpLUt29nz0+9lstf+ybq23fSPniAEx98L83770OM5DpwR2l4/7F8ezYKRq2p1Wu2sdYYSPvjhIiDXk+BYEoSX1ntd0cXEzSbOnELBZ8XpNqvUUjn8CGM30kAEJU7Mqqo9rKgRSawGcGaAgiKzl63ETPzKJpPNBaZILsAJdX+qYt2c+UbfoXdP/LTVDbM0nzgfo6995209j9Crm2ZMsFn/YHRgu+ygCC1WjV1b6RM+HoGgi8zC8Wikip4WprzL7t10jpAFgBp4ufZSQYQ2/EJ5ubideROzyfV7BJnLXe8wyiOmW2JdveNfymAgKzwB9S6i4Dpzg0yhs1PuZ4rX/8rnP+SV+DWJ1i58zaO3fJu2gcfi3PYfcLU8tCv4AxqXw4739ItwES1aowRokyXL2PSvTJ+/sDSPzxzMhq7u6dRNAHZXbUPpJ+tnQ5Rs4Gp1lDaeEkWO+sg9znGGRiEkcamoACCNGTLzgXI7dI5qI6e9fClP6p1alV2Pu8FXPWLb2bbM56DOC7Lt/0jh97+m/gnTyTCL4vxe0LtO7YOk0DSFeR6rg39XmGo6PGfidYPNAup8G2u2XPULU1A9kUBAhxOXqzYwAcb4UxOxvvVtJrJewvKUfCN4nn6EQIEUZgDgRZ8gtyiUCk4fvRif8kwQq6RMd1DfdMmLvmRn2Tvz76RmUv3IqrM/81fceT33oV/8ni3dt3v9OlwX6CMBUrCxC4LqJqq60YtPxgq/LMVfNahdIxQh3ixwHg3PytIlwFVU40iMA4mAQCq0G7lzEB2Vm6vCzbKxeFZEEgmNSwZMOTaqYvt5eSbK/NlY2Hmkj1c8epfYM+PvZralq1oFHHqM3/O4+98G8HiIpITfl7QOkSYfUAZdm7vP1J3HVnMvPWcCz7LvgKVikF8XU8ryWJSDewCQDO04HVT9iI4tTrO5BQiic4ZgzabeMUlNln7LYW9NyTOGFpr+yd/FsAgA1qjU7ro1gw0pvxdz34eV7/+l9h14wtwanU08Dn6sY9w+L2/G28XKzK2nS+1/f1p8v56drGEaowYRCNUzrngC99VcQ2ea+I5DOPfTgKtIgOECTXUEn8AcV2cqakEANLbJku1+3bNTepOd+KO4n16JGNxEnRZa/tHv2a0O5vEMWUhYhJVTG7bxiU/9KM8+Wdfz+zlVyDGEDUbHPnQ+zl0y7uJmk0kmdVXkOTgpgod3WKhQw+kfoCIa4yGUST6HRB8qv3GESqV+KpFZrw+o2TU1akvVoz//X6UA0Cahq53FbdSxZ2dxZ2aIjKmsPGyQLNBhfyeO90t2Ir76SRD733ADtg312YoPlcKziivW/HY9pSncvVr38CTXvJyqhvj7dmixhr73/abHPv4R+P+PZG0c3dswY3stBlysFiG9RyjrSg6J6Ff7pwM9dcqDiaZYK7S868GCl/SvY6k8/zA8gsFBrCJfCa7eeFqBW/zFpypqUT4xTytQquVy8D1snaZRJD0nEIVCKKYCcpWdNqMT5BtPDHA5I4dXPKyH+LKn/k5tl7zFExSmQwWTnPgnW/n6Mf/d1zRG7Q2SkcLb5Ta63jNWFJ3HFlOHO1zIviCQ1nzHFynp/XWgBpBShxBybGzIIIfZ9uiviggwNrJWLYWcT0q23fgbNjQX4bLJuVbLYQgtytX70vBiI01O5PSDYBoDBBYhcpEjV3PfBZXvern2P0DL6U6u7Hnzs6d4qG3/DonP/3nmRgz75/ouF10OuqMUeajd2WqjmBENFKVM3UCB6WQXddQ8Uyh5N2/S2pXBkhXFsnzE11PGnBjR0m5f+ukvXzfYwYboWGEMz1NZecuvE2b4wURJT8/RVbUakJA70voIc5ImPPqsn/nQBBYcFyHzZddxuWvvJm9N/84G/dclluj13r8IA+95f/h1Of+ptBhOlrgug7V13V0YaY/xRMRV0TDBADjCn6U8I0RahVTWha3RnCShFRG23MgMPEeN8eyUugywEWf+iuCw4es2gjCkGhmA94FF1HddR621SpvzMv9Jc38RG8Tb3Mq0p/QSd8ehBBmQKA2/pFTu3ZyyYtfwpN/8lXsfPozcGv13Fc3D+xn36/9EvNfurU7rqVYHF2v4EpP1DFdgewnJJFKxTG0rF13lm9g8Six+44ZvDN6KnST0/yu7ceI+EkUkAfA8p/9MebyZ+Mf+IaH2jiX6xzD3bKV+sV7CE6fplh+EYk3Mw7JgyDudpXuBovQX7jJMoGGEEVxZD+1fRsXfd/3c+XNP8YFz72RemYcS3pbuffb3P+rv8TSXXckU7ntWFLS8Q38eL5BbiBEf1GyZkSXzyYCKFQOq56D58pgY+RI4qr1FC8LAhM/t5J0A+cAEJ44ASf/0lVkFo23N9UwxN22nfr1z6C++2Kajzyco/4odQxVCXN/UhM3cYBksEva7bETz+Bu28SuG57FFT/8b3jS81/I5NZtJX4HLN/9Te77T7/I0jfuQlynZP35uMLLpn9Z100HGf/CORURkWRDsTPN8qWve66h6pmhvyNyBCOkgi5QfxcMVuKQPw8A/8hhEPGAjaSbHCXLoerXPpWJy/di223ahx9P+b1vP/Uw5xg2cYOgNERKUqaYiTozOy5m+w3P5sIXv4Rdz7yBiS1bGbCHrs7ffrve/e9/wbQffRhxTDKTdx2C0/HgMTwEHDNkJG3yFEJ03Vm+7DmOEeoVp6/cXebuizExADKefx4QrAmy2AeA4MhhEKrATAoAoojw5AmmHn2I+nXXM/nkqxHHoX34UH7ea0YtQqGXL2g2cLvtQwZ1FHGqVDdvpnbRbjY94wa2P/d5bHry1VRnZko1PmvYH/3DP5UHv3UvtZrLpFWqJt5GXf4ZYv1R74vrFUrFiLaj8R3B4jkiQr3q9OpYQ744SqZMGM05fcUwcFmEEgCcPEGSBJqJ+Seu6WMjmnfeTv266/E2b2bquqfibtxE67ED+CePEyLdnTHSHvMwOyqEeI/7ytQ07nnnM3nlVcxc91Rm9l5Jbfu23NClfnVV0j3bWydOyuLXvoEnNZaDNqsBuCLUjGHCMT0wSL+2jis4HdeA6Jg+BUhVcnWysbN86bFaJR/vD0OdTVqqnbgzPfED+sLA0yK9OkAXANbvpC1h1ZzaRRFrd3yV2Zt/AmfjJpzJSSYu30tl5078EyfwTxzDnztFuLhItLZG1Gqivh/TfKWKO7OBys6d1C7aTe38C/C2bMFUqoVfnPThJ+vv0y3W1cYsJCIs3P511h47yISp4xMQEhGp0ogimlGEI0LNxICoGMEdsNL4XMX648QREnfYiKG3OHScLF93VU/FoeLJWLDstt0nDECX+gu5APBFpN8HsL04X4qdMa0Dj9K87x6mn/f98QmOg7dxE97sRiYuvRTb6cS7Ywc+ai1iDOJ5mGoNU60irttN0KiNYoAke+qR7qCV7KrVNT/J7lpEEajl5Oe/RLDWwHEc6lRpaDPnZFpVmpHSjCyOgBcDQqsi4ogM6ZA9hyFjCaO4IK6gYcERHJblU6DiGWqeM4TFtE/4AKFRjO13AqXHCGExW+IC8T433R4NzZfGGw1Wvnwr08+5sddUkeb8XQ/H9bpj07t18+4kjTDeIDnKCzvW8KymDwKCJVhdZe6Ou7o/vIJHBweb6U3IgwE6qnSsigE8I1oRoSLgCmLG1Gw9ByFjsrZeW4VW8WEt4q5jqFWcZF/isR2VeKsYAyYJz7NhYMYXmDOIbzOfkTBABHFHcDXXDJGUQlfvugP/5HEqO8/rs9Wpw6hRGOfiwyijxXltLgq7FAiZ9wjK2iMHaBw5BibZrAFDVSq0tTV0VEp6qXyr4icZMlfQikjcuUMJGEbYjPUOb5DYpo4tfMckTl/GmRmH+rsMIBon4Po1P00SHVO6e2FlABAzwIzG5eC+RRKdY8do3HM3lR274l2wuztghxCGGRBkBG4H03oPABnNL2EEUWX+rrvpLK/kIo8KLmGGBWSAjhQjhEARX7tgwAWtFMBwrm+exMo8anGIMUK96iaZvvVRf9qTGIrGAzpzmp9zAo8BvGRxrmACYgaoQrbXowd722ryyC3v58qdF1DduCHeFTsV4kABZ2nd5hhh8Gu9EFStxfo+83ffH4Mj00cuCJ54BBoN1LxhtKwxGPBBWsmoo4rEbdHJpK9zMsgjGdmOC9rJdgoXh1xIHOvHHv/6qD8fCcQFMSf5BSUp4WbxPVkGGJg0WWl3WPzybcz80Sd50k/eHA9OSjS2K7xh2lzKDENeiyJQpXFynuWDRzLtv72kioODFYOlvL9AdHzKjkCa8fRkNQqeoNUYFOKcJRgcoCrQ1vJET5rj91wztrh1wB67NikNm0JBLsMA4QAAWABHsx+deIGRKvOrTSS0HPvs37H1GU+hunE2pv+Mxg6k/qGAiM8rMx+osvTwATqrje7qIs1YUUFwcJM2hhJdkcwqopKKXZm50GT1Q5g0PRmFiqC1hBmc/oLmWH5AjcxqzIIpqHlOJs27furPN9VovN4yhUAmDDRx+DdfCoDEBMygONk/QoClVoe1js+MqdA4fIRTt36J8154IzYIysO2Pm3OMkM5U3Tj/zQfkEQSCweOxOyUW3XfM1Ex2Rm0wAI5IpVyJ15GEKwSq0ugcSO9UbQi2BqYqnSXccs4c3pqghiNG3eLsX6t4ozQ+fVUNqAjNuME5moBASKLRY3ImoCNSTKo2/ceWctCs53MwxFsFHH8y19l06VPwqvXY88/1dispheBkA5ZjJJNlDPC7h/DEpeH/WabtbnFkqXfWqgvOEgGADqMNnWM8XIDwJGYCScFQ1Ww9TjbJ94IMHjJPch8XtXLCv/sqD97JMQmoWC2MpjmQkSLKfdsGLhJCxmRpbZPKwi7zgRGWD18lPm7vsW2q6+IfYHUnmdi9z5tzgrXav+xvuFLytrJeVrLa4n9tzn7Xxw3Ld1FZ8PpflTMPq7ORSANxVlLmQHshKB1kIr0Gug04wfU6K3H8twk1h9ZbxiP+rO8GIhNegJMMQzsiLCmpSYgZoBN2U/zrWWpHfRtdmBDy9w997Nx55a4cjdCm7vHbMmx4vClFDDA0tE5ojBMc5Mlgs+uUhbMOAQq/e1b6yPagQ6k01A0cfhsTWACnGpmdnA1+W7PNdSrTkYR1yv84YDuEGGTbGihM2hF4j0CygEgsCH7gSudED+yuS6eeCSLsHz0BCuHjrJh51Y0skO1uQuQ4oStLjD6ARF2ApZPLg68PJpfoI4lSX6M2+h5RlZ2tL+X+AzOatIVVI2BwFQSYlYdQ7Wb6Dmb4LL8uBCPrteUAXJNISwIsjIwDBTp5UL8yLLqRxlNkZz1DX2fUw/vZ2rDZK6oM0zwWQ0feh5KY2GN1mqrwN9lC9M1Y6Pjnrj1XsqBexCc5S0CGhovwTmtsE0ctrkua6J99YRzQf3dbCCWANuj/l5iaAWkVfwM9x92bUYJUdWu3VoJLGHGITDkW5EUYfHoCRoXncfk7HTPyStqeZ8PMOBY4b46v0IYhBlLOtwMgOaWTQ9cTaPrA8awcSzjfqYAs+KyQTycSHADZdmN4qTNulpMxrtZlA4RTlK3ybaD5d3gBABqwrRHawKgEymNwlKjYlJFRfFbbU4fPka9dlGvlDtoqOJQM9Cbwq0JS6wuNdCceAfZ/3xtLZ3COU659Uw0flhtf5ADulk8thq3G5rNWIMXGRacgGBEELle7e9FApqYmSQEjH2B1aRBqd8EoNZFZKMCq2FmXf/A4Cv+osUTc2zbvhHPc+Px8SMof5ADmD3Wboc0VtvFumSfBzDINESUTNDQ0cIadz3esAJhLrePsNW4bBavWJBh2ho8hHknoC32nFB/9uo0NOj2BGTCwOOhNYFjohIAiHgCs83I0gxtCY31d6OqQGOtweLJebZu3ZgAIE3iDHEKs8utS1bZrq62CYKotHKuJT8+yxDpKtfsZC10/GEM6zUJWp5AwiBsMx6bjFfamGFEmMLgWYcTTocW0Rl7/WW3DvEKbScJBRMAHKs4cPXjj/YDwAj1wDK9EtiBe/0WXTBNGknmT55m40QVYySzeXJG6Nnq4ohNkVWVlZVOvJx8Hfa/JGmTM1vjCn9dTDBA+z2E7U6FDeL2t2UVyrMTIlyghhPSZqU3s2Fsr3/QkUCjZA5TJgxE5ss+xU00vNYMo+lONLg7QinpwhVhZa3J6soqM5P17qi4Qduf9m2qXJjN4wcRjWZQMup9tP3PO0L9Gb9hgl6PXR8GnooYdpgKM8btW5JlCos0UhDUxOUCJjlOiwU6Z0H9mb4AjRXRkZ4jKFJeNXNPdyIQnFaong7xZcvW3SgQRcrppTUmqpWCkPNTNoZuq56c1mwG+IEdot1lnoiOdNhGhXzrZgSlD3A1MexyqkyK21eKLenOzXjnsaAuZAoPh5PaPKO9AXJ9AVg6hMxINRnOJRaR1VIArIZ2ZBiTJf+yJUlLa022bJii6rmZDaPyI1n6AdA/f6/RDBMzoUPi/35e0JLee+1fJzpyyOJYjFAy8WtaXHY4VeomzvsVhW7KF2nmKNqIcAGTVNRw1DaIsOukfs0xQEtDHDFpg4iPxHsElZoA4tUineEQYEBODtpByMJqg20bprrbpRaFO1jzNUkPKM1mMMTxG23/GaHhZ20SShzKjcZju1PFS3rxTEHTy5ZpFY9n/YLzzRRV43AwXMHHFrLX42UNFPA1ikPB2AFtAHPDALAGnAYuLWusQKWP+vtCwrUmGyZqcQIi4wsUBT1oS/RWO0roX0v0nLHtfxG79gxYgAGaXwzztjgVtjiVrrdt+vPvJUu1Cp069Pfw7zSTVMThQLhMU8MzSA8rK5GfmCKDCE1BlnUIABrEE8JuKP/4Yavs41dbfsBaq8N0rdIdIAkUvP8BziFKqxURWR2g3TrA/o9nG+1Z+gUU9kCoiGGHU2XW8ca283kwlJuB7Dnb3UlqxuOhYIEV2xm4o9ogVmiluYD471FhgBMooaKuRGXdIoPxVRJ6qbLYaFF3nVzkQGFH7ZzmpxVGGzNA/2cPtv9ZoAz13qV8G5axQVB475SJ7f2kcQdqcNnxPlNQwgZFEG1yalxjtvKAf5rTUWtd1UvfhkQo1XiUagvELzvP/Pxqd+3/48MoJvuv/GIpa50OzU6AjSxRFBFFNrlHmccod8xGliCICEJdl/3XEc3cSv8swrL5u2XPcxth09sWZ7NT4SJ3giknzu7Fd5PcpXAvO545ZgQn/b8RnL5zYp9ixqlxTW0bO9zJsWMDAZo2oG1DjDGImHljZK2sCpkdEfPtxBGsDqoD6BD6USCwluV2G7deG+zwlcT/HV81HiB2Du1/SVOIHaH9lGh8N753a2wwHs4we97V9v7MX3Fkiylfv9/HEkaEaVPluvoOHuyc5pC/jB3qACfLxVF8orhnQzgpIu2ytfBZABwh3lF62zg5gHJAwKofMOPFGkLGFyiNDBIgdHxJIthy+89Z2P/iFnNDTYH2A2PaeOx0a0wYpzSRY4Y6fQy184Mdw/5jdeNxzcR2qsblkfZpou4VK1eHSC1rGiCOAWFewyh0PHckAA4UAaDDcgAljx0bseYHTLluLjNYpvmqirUQhM7QIo+Oaf9H5zPG03gFPDFsdapscSq4xpQMXTo3dr4sS1iWNzASm4yrJrZSMw77Wqfw1RZAkG8RX47aMQCQSDxhx923DwXAEvC1skhg3E0M4wHGykoQUE1KSGUFn+yxMDJqrUhRDGdi/4c5g0VfgCGO4bRx2enWmDRuaWiX9/BzTRdDwkD6unXHAlHfOYYrJrZRdzzubhynZcPkd/X3v3c0Qo2J8wADKkwG4HWL3QUjn6O4ekR7TpItOEw2IxCbPhNo2ohWGOYcPltw/tLnQTgo072+/P84CSEn/T1a/ls8Mexy6+yuTDLteF1HzJQ4aE7iyHWPm4zDZ6T8nIzTl57rmJ5j6OTOzZyTfW/yuXvqm3nOzEXMOFXKSngCNKMgXjLuOMezE9YGMQDA14FvkWwaQWlzRcH+Sr9tjVDWbISLyU3Zzo5jVxRVIbJmhHbr+LP+xkiVONLfn+8gzDoe290qE91CDufUzptSB2+IszjIhCSPiHB+bRbXdblz+RCng2bfIK6ODQlFqThOyw4YhuQWir6LwKfKAaD5vXhHOFQNjZi0Gu8mmqESzZV/HbUqA7R7tBkYaf91AOVlKoaTxmWHW2Um6+EPSOQMFuRwwKzHzg9+X+Z4OqLPGHZ5s3yfV+O2xcc43l7O7bPQCDs0bcCUMxHqgBG9XfV73WJ3F5HPAg9lnQlNlhypxKNILOX3LKX6QENtj+5D228CrCnsBrjeMHA8+1+kRlfAM4btXo3dlUlmE0evR7HpvRevO2IKFG0ydN+jbadgBspyAc6wY6b3/6x5SM8TYxDHSe4GdQybalPcuO0yLp7aQrbnL7QRHY0Cx3XnYmdwtAkAeBT4IPAOjRW4u/3bOE5gVhhrolRsPBWsP/2LquOW2P/xw8D12P8e5AXPdTCuQyjCnAY4CC49R83DUBGDm6yycyQ+5hKDgkRjU8YwI6KA4XmDfkYosg4iiEk03xjEpMdMlwk2eJM8d8deqvMHeGjpeKy0alkJWm3XdU9GoY4GwOsWm3xo4wTAx4AXAz9gGV/4RRPRNtBWSyUqmckvLiKODHfyBpuB9dp/NQKug3Gc7lJzH6U4wyu7F48kDo6gOMmyazcRiIvgpVqPUBWXmnGoGCcBjaFqHDycLrgkwxTDKoXpMVK6NykATPy8+5iAIAFE3a3yrJ2XM1mpc/fcQQIbMddaXcOY02MzQAKC08DbFK6xsHNkGCj9g1rT4y1H1AttfwxiXAvifMftv8SCF9cp3YpcSoRf9leEqr1FqFqeEDPd/sm4K9cVgycGV+KVQJ4YasalblyqEtcSKuJQdzzqxsVLwOMag2uceNqHiSMFSe8Z4acj+XqMEAPxqTv34Hkudx3fTyPsBB0N2+GA/gJ30NWrOdGXWqH76yK8xyozg7S8rEycfWx5wqQv6mg+EI37UIc1PJ1Z/N89ZgR1HdQpF/x6Om60Dyzlqeb4b4gvtFUlwNLUsiuT31XFSMwmaQuXJzGTIPHzCbfChFuh5lYwYvAclwmvQt2tUnE9xAqO4+AZDzcBzLU79jBZnWDf3CG949hDeMYdHwCvW2zx4Y0TWjX8kbW61cJ/jVSmekLXsRNEHSPS8sRO+RkAiFG69D9KyOuw/wLWGKzjoI4ZNHzyrBov1xuKSi4RLSXXK57BEKrty3Fm9wWUzJ56QpwzqBhPXeMEjpiO65i1mlsNZmuTOl2daBkxpycqteiijduP7F841nTFGZohLb19eOMEruA1La/xld+ysHUc548CU9RDtduaoUj6m0wlEnc62SDEYuPRBpmUUny3hf/nU06ZSp4RIuNgXYMas24PYVhX4fpeW19fv3R7VjSIhzcJBulI3L+3HGq0pEpUddzIEXPaiDml2BNrQWfBMUanK/W2wEnXOAutMFw4vtZob6h5bKpP+g8vNVbnG6G9ac+T7Isue7J/fGVZr/zyV9YHgBQEUwZzOuImX/n/Ini6Frb0GfjY2/5NtzdDWw/VAVTcSYupOXmBjiP4FCxgRbDGEDkG6xj0DBdc6hAt17G0X5ItDqwPqCOiItISZE1hMdRoVcBWjetb9FTHhquuOFo3bsuIHHdETrZscLIZBZ0NXp1pt9YwIgsTrrc611lrLgcte/XG8/Wa868Mdtz62Yjf/zPkDTdzrm5jXbU/3DLBcR/qhp0+vClSXmNhVykbSP+lssAG30ZbW5FBjIo7o4jj5AWvjGKDSJTIgSgRvMrZznIqDpsQBEIBX9FGhLYNoq4YX2DBCAuh2lOB2kbVuFTEWTXIcSOcXI38uUhtsMGt6aTjrXjiLHnGrB7qLLcMotdO7bDXb9obPP7Nv7V7P9hBfqHKE+G2rit4y+wEHphVuC6EH7PKSxUuU0kniwxmBE81Om8tooIL7rQBkWHab7FEYomMEpp4GHJkGCn0OF8loUDHog2FjonniLQFFkSYC1TnFO1UxKhBltoanRQIJowbumJOOzBvYWEp6qzVjWu3OPWoZty1rV69fe3Mxf7a8a/rlt9YRt4kfLffzugX3LKxTlWQJSs7Ldyocb7gKcDuCCY03cRa8oLZ0o7C2aBmcOpOzAw9WrdiCSUiEiU01oZG/choOgjQF6QBLFt0CfAdxBqVJQOnVDjpY+cdJKpifIPMOcjpEF1sEDYnxdUZ8YKqOGs7TK29p7IzXAvn9eITj/F/++2sIfx7GyfYAO5q7CBeEcIlFs4zcKWBy5IZCUZgohJpbapdi0Qqq6GJwpAwUOycFXs8NHq07djTBrUGmqFwPBLWqla0otJ0kNMGltqia4FouNV6ep6tBU8Jt4fL0uH81sN877b+2/8Prqk5f/cNbQ4AAABJdEVYdGNvbW1lbnQARmlsZSBzb3VyY2U6IGh0dHA6Ly9jb21tb25zLndpa2ltZWRpYS5vcmcvd2lraS9GaWxlOlJ1YnlfbG9nby5wbmfRrEvyAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEyLTExLTEzVDEyOjQ3OjA2KzAwOjAwhERbHAAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxMi0xMS0xM1QxMjo0NzowNiswMDowMPUZ46AAAABGdEVYdHNvZnR3YXJlAEltYWdlTWFnaWNrIDYuNi45LTcgMjAxMi0wOC0xNyBRMTYgaHR0cDovL3d3dy5pbWFnZW1hZ2ljay5vcmecvblIAAAAGHRFWHRUaHVtYjo6RG9jdW1lbnQ6OlBhZ2VzADGn/7svAAAAGHRFWHRUaHVtYjo6SW1hZ2U6OmhlaWdodAA5OTYGfuckAAAAF3RFWHRUaHVtYjo6SW1hZ2U6OldpZHRoADk5NUPb5RMAAAAZdEVYdFRodW1iOjpNaW1ldHlwZQBpbWFnZS9wbmc/slZOAAAAF3RFWHRUaHVtYjo6TVRpbWUAMTM1MjgxMDgyNpOSGLMAAAASdEVYdFRodW1iOjpTaXplADE5N0tCQhlJFDQAAAAzdEVYdFRodW1iOjpVUkkAZmlsZTovLy90bXAvbG9jYWxjb3B5X2VlNzBiNTEyMjExNy0xLnBuZ6uX/8EAAAAASUVORK5CYII="}}]}],"stream":false}' + headers: + User-Agent: + - Faraday v2.13.4 + Authorization: + - Bearer + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Date: + - Wed, 24 Sep 2025 14:37:57 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Access-Control-Expose-Headers: + - X-Request-ID + Openai-Organization: + - "" + Openai-Processing-Ms: + - '535' + Openai-Project: + - proj_61L3Oqt640dKU0CASS2iOj8Q + Openai-Version: + - '2020-10-01' + X-Envoy-Upstream-Service-Time: + - '579' + X-Ratelimit-Limit-Input-Images: + - '50000' + X-Ratelimit-Limit-Requests: + - '500' + X-Ratelimit-Limit-Tokens: + - '200000' + X-Ratelimit-Remaining-Input-Images: + - '49999' + X-Ratelimit-Remaining-Requests: + - '499' + X-Ratelimit-Remaining-Tokens: + - '199229' + X-Ratelimit-Reset-Input-Images: + - 1ms + X-Ratelimit-Reset-Requests: + - 120ms + X-Ratelimit-Reset-Tokens: + - 231ms + X-Request-Id: + - "" + X-Openai-Proxy-Wasm: + - v0.1 + Cf-Cache-Status: + - DYNAMIC + Set-Cookie: + - "" + - "" + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + X-Content-Type-Options: + - nosniff + Server: + - cloudflare + Cf-Ray: + - "" + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: ASCII-8BIT + string: | + { + "id": "chatcmpl-CJKuyj0T0K8hS6h9OMPlLIzHzgLyl", + "object": "chat.completion", + "created": 1758724676, + "model": "gpt-4.1-nano-2025-04-14", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "This image appears to be a stylized, digital illustration of a red gemstone or ruby.", + "refusal": null, + "annotations": [] + }, + "logprobs": null, + "finish_reason": "stop" + } + ], + "usage": { + "prompt_tokens": 53, + "completion_tokens": 18, + "total_tokens": 71, + "prompt_tokens_details": { + "cached_tokens": 0, + "audio_tokens": 0 + }, + "completion_tokens_details": { + "reasoning_tokens": 0, + "audio_tokens": 0, + "accepted_prediction_tokens": 0, + "rejected_prediction_tokens": 0 + } + }, + "service_tier": "default", + "system_fingerprint": "fp_04d3664870" + } + recorded_at: Wed, 24 Sep 2025 14:37:57 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/fixtures/vcr_cassettes/activerecord_actsas_attachment_handling_handles_multiple_attachments_in_ask_method.yml b/spec/fixtures/vcr_cassettes/activerecord_actsas_attachment_handling_handles_multiple_attachments_in_ask_method.yml new file mode 100644 index 000000000..a71a85e54 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/activerecord_actsas_attachment_handling_handles_multiple_attachments_in_ask_method.yml @@ -0,0 +1,123 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.openai.com/v1/chat/completions + body: + encoding: UTF-8 + string: '{"model":"gpt-4.1-nano","messages":[{"role":"user","content":[{"type":"text","text":"Analyze + these"},{"type":"image_url","image_url":{"url":"data:image/png;base64,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"}},{"type":"file","file":{"filename":"sample20250924-283639-iclvv8.pdf","file_data":"data:application/pdf;base64,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ZXNjZW50IC0yMTMgL0ZsYWdzIDk2Ci9Gb250QkJveCBbLTQwOSAtMjE0IDEwOTkgOTUxXSAvRm9udE5hbWUgL1BVQUxYQStIZWx2ZXRpY2FOZXVlLUxpZ2h0SXRhbGljCi9JdGFsaWNBbmdsZSAtMTIgL1N0ZW1WIDY3IC9MZWFkaW5nIDI4IC9NYXhXaWR0aCAxMTIwIC9TdGVtSCA1OCAvWEhlaWdodCA1MjQKL0ZvbnRGaWxlMiAyMiAwIFIgPj4KZW5kb2JqCjI1IDAgb2JqClsgMjc4IDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMjc4IDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAKMCAwIDY4NSAwIDUzNyAwIDAgMCAwIDAgMCAwIDAgMCA2MzAgMCAwIDAgNTU2IDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDUxOQowIDAgMCA1MTkgMjU5IDAgNTM3IDE4NSAwIDAgMTg1IDgzMyA1MzcgMCA1NzQgMCAwIDQ4MSAwIDUzNyAwIDAgMCAwIDAgMCAwCjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAKMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMAowIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgNDQ0IF0KZW5kb2JqCjkgMCBvYmoKPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1RydWVUeXBlIC9CYXNlRm9udCAvUFVBTFhBK0hlbHZldGljYU5ldWUtTGlnaHRJdGFsaWMKL0ZvbnREZXNjcmlwdG9yIDI0IDAgUiAvV2lkdGhzIDI1IDAgUiAvRmlyc3RDaGFyIDMyIC9MYXN0Q2hhciAyMjIgL0VuY29kaW5nCi9NYWNSb21hbkVuY29kaW5nID4+CmVuZG9iagoxIDAgb2JqCjw8IC9UaXRsZSAoc2FtcGxlKSAvQXV0aG9yIChQaGlsaXAgSHV0Y2hpc29uKSAvQ3JlYXRvciAoUGFnZXMpIC9Qcm9kdWNlciAoTWFjIE9TIFggMTAuNS40IFF1YXJ0eiBQREZDb250ZXh0KQovQ3JlYXRpb25EYXRlIChEOjIwMDgwNzAxMDUyNDQ3WjAwJzAwJykgL01vZERhdGUgKEQ6MjAwODA3MDEwNTI0NDdaMDAnMDAnKQo+PgplbmRvYmoKeHJlZgowIDI2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAxNzkzMCAwMDAwMCBuIAowMDAwMDAzOTgyIDAwMDAwIG4gCjAwMDAwMDUwNzMgMDAwMDAgbiAKMDAwMDAwMDAyMiAwMDAwMCBuIAowMDAwMDAzOTYyIDAwMDAwIG4gCjAwMDAwMDQwODYgMDAwMDAgbiAKMDAwMDAwNTAzNyAwMDAwMCBuIAowMDAwMDA3OTU3IDAwMDAwIG4gCjAwMDAwMTc3NDAgMDAwMDAgbiAKMDAwMDAxMzY5MiAwMDAwMCBuIAowMDAwMDA0MjA5IDAwMDAwIG4gCjAwMDAwMDUwMTcgMDAwMDAgbiAKMDAwMDAwNTE1NiAwMDAwMCBuIAowMDAwMDA1MjA2IDAwMDAwIG4gCjAwMDAwMDc0NzIgMDAwMDAgbiAKMDAwMDAwNzQ5MyAwMDAwMCBuIAowMDAwMDA3NzU1IDAwMDAwIG4gCjAwMDAwMDgxNDYgMDAwMDAgbiAKMDAwMDAxMzE0NiAwMDAwMCBuIAowMDAwMDEzMTY3IDAwMDAwIG4gCjAwMDAwMTM0MjQgMDAwMDAgbiAKMDAwMDAxMzg3NyAwMDAwMCBuIAowMDAwMDE3MDE2IDAwMDAwIG4gCjAwMDAwMTcwMzcgMDAwMDAgbiAKMDAwMDAxNzMwMiAwMDAwMCBuIAp0cmFpbGVyCjw8IC9TaXplIDI2IC9Sb290IDEzIDAgUiAvSW5mbyAxIDAgUiAvSUQgWyA8NGU5NDk1MTVhYWYxMzI0OThmNjUwZTdiZGU2Y2RjMmY+Cjw0ZTk0OTUxNWFhZjEzMjQ5OGY2NTBlN2JkZTZjZGMyZj4gXSA+PgpzdGFydHhyZWYKMTgxMzIKJSVFT0YK"}}]}],"stream":false}' + headers: + User-Agent: + - Faraday v2.13.4 + Authorization: + - Bearer + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Date: + - Wed, 24 Sep 2025 14:37:47 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Access-Control-Expose-Headers: + - X-Request-ID + Openai-Organization: + - "" + Openai-Processing-Ms: + - '107297' + Openai-Project: + - proj_61L3Oqt640dKU0CASS2iOj8Q + Openai-Version: + - '2020-10-01' + X-Envoy-Upstream-Service-Time: + - '107463' + X-Ratelimit-Limit-Input-Images: + - '50000' + X-Ratelimit-Limit-Requests: + - '500' + X-Ratelimit-Limit-Tokens: + - '200000' + X-Ratelimit-Remaining-Input-Images: + - '49999' + X-Ratelimit-Remaining-Requests: + - '499' + X-Ratelimit-Remaining-Tokens: + - '198464' + X-Ratelimit-Reset-Input-Images: + - 1ms + X-Ratelimit-Reset-Requests: + - 120ms + X-Ratelimit-Reset-Tokens: + - 460ms + X-Request-Id: + - "" + X-Openai-Proxy-Wasm: + - v0.1 + Cf-Cache-Status: + - DYNAMIC + Set-Cookie: + - "" + - "" + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + X-Content-Type-Options: + - nosniff + Server: + - cloudflare + Cf-Ray: + - "" + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: ASCII-8BIT + string: | + { + "id": "chatcmpl-CJKunptcKCJFD8p1qhGYQzLMiG8A4", + "object": "chat.completion", + "created": 1758724665, + "model": "gpt-4.1-nano-2025-04-14", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "The image you shared appears to be a stylized red gemstone or diamond, composed of geometric facets that give it a shimmering, multidimensional appearance. This kind of design is often used in logos or icons to symbolize luxury, value, elegance, or quality.\n\nThere is no textual information directly embedded in this image, but visually, it strongly resembles a cut gemstone or a jewel. If you have specific questions or need an analysis regarding this image's usage, symbolism, or context, please let me know!", + "refusal": null, + "annotations": [] + }, + "logprobs": null, + "finish_reason": "stop" + } + ], + "usage": { + "prompt_tokens": 1003, + "completion_tokens": 100, + "total_tokens": 1103, + "prompt_tokens_details": { + "cached_tokens": 0, + "audio_tokens": 0 + }, + "completion_tokens_details": { + "reasoning_tokens": 0, + "audio_tokens": 0, + "accepted_prediction_tokens": 0, + "rejected_prediction_tokens": 0 + } + }, + "service_tier": "default", + "system_fingerprint": "fp_7c233bf9d1" + } + recorded_at: Wed, 24 Sep 2025 14:37:47 GMT +recorded_with: VCR 6.3.1 diff --git a/spec/ruby_llm/active_record/acts_as_attachment_spec.rb b/spec/ruby_llm/active_record/acts_as_attachment_spec.rb index 2a6033f03..4e9148956 100644 --- a/spec/ruby_llm/active_record/acts_as_attachment_spec.rb +++ b/spec/ruby_llm/active_record/acts_as_attachment_spec.rb @@ -47,30 +47,47 @@ def uploaded_file(path, type) expect(llm_message.content.attachments.first.mime_type).to eq('image/png') end - it 'handles multiple attachments' do + it 'handles ActiveStorage::Attached::One in ask method' do chat = Chat.create!(model: model) - image_upload = uploaded_file(image_path, 'image/png') - pdf_upload = uploaded_file(pdf_path, 'application/pdf') - - response = chat.ask('Analyze these', with: [image_upload, pdf_upload]) - - user_message = chat.messages.find_by(role: 'user') - expect(user_message.attachments.count).to eq(2) - expect(response.content).to be_present - end - - it 'handles attachments in ask method' do - chat = Chat.create!(model: model) - - image_upload = uploaded_file(image_path, 'image/png') + document = Document.create!(title: 'Test Document') + document.file.attach( + io: File.open(image_path), + filename: 'ruby.png', + content_type: 'image/png' + ) - response = chat.ask('What do you see?', with: image_upload) + response = chat.ask('What do you see?', with: document.file) user_message = chat.messages.find_by(role: 'user') expect(user_message.attachments.count).to eq(1) + expect(user_message.attachments.first.filename.to_s).to eq('ruby.png') expect(response.content).to be_present end + + # it 'handles ActiveStorage::Attached::Many in ask method' do + # chat = Chat.create!(model: model) + + # document = Document.create!(title: 'Test Document') + # document.files.attach( + # io: File.open(image_path), + # filename: 'ruby.png', + # content_type: 'image/png' + # ) + # document.files.attach( + # io: File.open(pdf_path), + # filename: 'sample.pdf', + # content_type: 'application/pdf' + # ) + + # response = chat.ask('Analyze these', with: document.files) + + # user_message = chat.messages.find_by(role: 'user') + # expect(user_message.attachments.count).to eq(2) + # filenames = user_message.attachments.map { |a| a.filename.to_s }.sort + # expect(filenames).to eq(['ruby.png', 'sample.pdf']) + # expect(response.content).to be_present + # end end describe 'attachment types' do diff --git a/spec/ruby_llm/active_record/acts_as_spec.rb b/spec/ruby_llm/active_record/acts_as_spec.rb index bb8b34234..7ac8e0ab6 100644 --- a/spec/ruby_llm/active_record/acts_as_spec.rb +++ b/spec/ruby_llm/active_record/acts_as_spec.rb @@ -448,28 +448,28 @@ def uploaded_file(path, type) expect(llm_message.content.attachments.first.mime_type).to eq('image/png') end - it 'handles multiple attachments' do + it 'handles a single attachment in ask method' do chat = Chat.create!(model: model) image_upload = uploaded_file(image_path, 'image/png') - pdf_upload = uploaded_file(pdf_path, 'application/pdf') - response = chat.ask('Analyze these', with: [image_upload, pdf_upload]) + response = chat.ask('What do you see?', with: image_upload) user_message = chat.messages.find_by(role: 'user') - expect(user_message.attachments.count).to eq(2) + expect(user_message.attachments.count).to eq(1) expect(response.content).to be_present end - it 'handles attachments in ask method' do + it 'handles multiple attachments in ask method' do chat = Chat.create!(model: model) image_upload = uploaded_file(image_path, 'image/png') + pdf_upload = uploaded_file(pdf_path, 'application/pdf') - response = chat.ask('What do you see?', with: image_upload) + response = chat.ask('Analyze these', with: [image_upload, pdf_upload]) user_message = chat.messages.find_by(role: 'user') - expect(user_message.attachments.count).to eq(1) + expect(user_message.attachments.count).to eq(2) expect(response.content).to be_present end From 7790e0f9b5255e6646a6aa464e219fc37f6e235f Mon Sep 17 00:00:00 2001 From: Manuel Meurer Date: Fri, 3 Oct 2025 09:31:14 +0200 Subject: [PATCH 2/2] fix handling ActiveStorage::Attached::Many --- lib/ruby_llm/active_record/acts_as_legacy.rb | 4 +- lib/ruby_llm/active_record/chat_methods.rb | 4 +- .../active_record/acts_as_attachment_spec.rb | 46 +++++++++---------- 3 files changed, 25 insertions(+), 29 deletions(-) diff --git a/lib/ruby_llm/active_record/acts_as_legacy.rb b/lib/ruby_llm/active_record/acts_as_legacy.rb index d01b44ad8..909303865 100644 --- a/lib/ruby_llm/active_record/acts_as_legacy.rb +++ b/lib/ruby_llm/active_record/acts_as_legacy.rb @@ -302,10 +302,8 @@ def prepare_for_active_storage(attachments) case attachment when ActionDispatch::Http::UploadedFile, ActiveStorage::Blob attachment - when ActiveStorage::Attached::One + when ActiveStorage::Attachment attachment.blob - when ActiveStorage::Attached::Many - attachment.blobs when Hash attachment.values.map { |v| prepare_for_active_storage(v) } else diff --git a/lib/ruby_llm/active_record/chat_methods.rb b/lib/ruby_llm/active_record/chat_methods.rb index e55af3c63..03311145e 100644 --- a/lib/ruby_llm/active_record/chat_methods.rb +++ b/lib/ruby_llm/active_record/chat_methods.rb @@ -307,10 +307,8 @@ def prepare_for_active_storage(attachments) case attachment when ActionDispatch::Http::UploadedFile, ActiveStorage::Blob attachment - when ActiveStorage::Attached::One + when ActiveStorage::Attachment attachment.blob - when ActiveStorage::Attached::Many - attachment.blobs when Hash attachment.values.map { |v| prepare_for_active_storage(v) } else diff --git a/spec/ruby_llm/active_record/acts_as_attachment_spec.rb b/spec/ruby_llm/active_record/acts_as_attachment_spec.rb index 4e9148956..37387ecae 100644 --- a/spec/ruby_llm/active_record/acts_as_attachment_spec.rb +++ b/spec/ruby_llm/active_record/acts_as_attachment_spec.rb @@ -65,29 +65,29 @@ def uploaded_file(path, type) expect(response.content).to be_present end - # it 'handles ActiveStorage::Attached::Many in ask method' do - # chat = Chat.create!(model: model) - - # document = Document.create!(title: 'Test Document') - # document.files.attach( - # io: File.open(image_path), - # filename: 'ruby.png', - # content_type: 'image/png' - # ) - # document.files.attach( - # io: File.open(pdf_path), - # filename: 'sample.pdf', - # content_type: 'application/pdf' - # ) - - # response = chat.ask('Analyze these', with: document.files) - - # user_message = chat.messages.find_by(role: 'user') - # expect(user_message.attachments.count).to eq(2) - # filenames = user_message.attachments.map { |a| a.filename.to_s }.sort - # expect(filenames).to eq(['ruby.png', 'sample.pdf']) - # expect(response.content).to be_present - # end + it 'handles ActiveStorage::Attached::Many in ask method' do + chat = Chat.create!(model: model) + + document = Document.create!(title: 'Test Document') + document.files.attach( + io: File.open(image_path), + filename: 'ruby.png', + content_type: 'image/png' + ) + document.files.attach( + io: File.open(pdf_path), + filename: 'sample.pdf', + content_type: 'application/pdf' + ) + + response = chat.ask('Analyze these', with: document.files) + + user_message = chat.messages.find_by(role: 'user') + expect(user_message.attachments.count).to eq(2) + filenames = user_message.attachments.map { |a| a.filename.to_s }.sort + expect(filenames).to eq(['ruby.png', 'sample.pdf']) + expect(response.content).to be_present + end end describe 'attachment types' do