Action Text 埋め込み添付ファイルの抽出を RubyLLM に対応
Action Text のリッチテキストは HTML を返すだけでなく、埋め込まれた Active Storage の添付ファイルを保持しますが、これまで RubyLLM::Content への抽出では除外されていました。結果として LLM へ送信するテキストに不要なタグが混入し、添付ファイルも失われていました。今回の PR はその欠損を解消し、埋め込み添付ファイルを Content の添付として取得できるようにします。
背景
Action Text を has_rich_text :content で利用したモデルでは、message.content が ActionText::RichText オブジェクトとなり、to_plain_text が利用できないケースがありました。従来の extract_content は HTML 文字列をそのまま返すか、テキスト属性のみを対象にしていたため、埋め込み画像やファイルが無視されていました。#365 での議論を受け、埋め込み添付の抽出 を共通化する必要が生じました。
技術的な変更
lib/ruby_llm/active_record/attachment_helpers.rb に、Action Text 専用のヘルパーメソッドが追加されました。plain_text_content はオブジェクトが to_plain_text を実装していれば呼び出し、実装が無い場合はそのまま返します。action_text_attachment_sources は body.attachables から個々の添付オブジェクトを取得し、action_text_attachable_sources で ActiveStorage::Blob へ変換します。さらに、content_attachments? と add_content_attachments が、Action Text と従来の Active Storage 両方の添付を統合的に処理できるようにしています。
def plain_text_content(content_value)
return content_value.to_plain_text if content_value.respond_to?(:to_plain_text)
content_value
end
def action_text_attachment_sources(content_value)
return [] unless content_value.respond_to?(:body)
body = content_value.body
return [] unless body.respond_to?(:attachables)
body.attachables.flat_map { |attachable| action_text_attachable_sources(attachable) }.compact
end
lib/ruby_llm/active_record/message_methods.rb の extract_content が新ヘルパーを利用するように改修されました。まず plain_text_content でテキスト化し、次に action_text_attachment_sources で添付リストを取得します。content_attachments? が true であれば RubyLLM::Content を生成し、add_content_attachments で取得した添付をすべて追加します。これにより、HTML タグは除去されつつ、画像やファイルが正しく保持されます。
content_text = plain_text_content(content_value)
action_text_attachments = action_text_attachment_sources(content_value)
return content_text unless content_attachments?(action_text_attachments)
RubyLLM::Content.new(content_text).tap do |content_obj|
@_tempfiles = []
add_content_attachments(content_obj, action_text_attachments)
end
テストのために spec/dummy/db/migrate/20260506120000_create_action_text_tables.action_text.rb が追加され、action_text_rich_texts テーブルを作成します。Rails 標準の Action Text マイグレーションをコピーしただけで、テスト環境でリッチテキストモデルが動作できるようにしています。
class CreateActionTextTables < ActiveRecord::Migration[7.0]
def change
create_table :action_text_rich_texts do |t|
t.string :name, null: false
t.text :body
t.references :record, null: false, polymorphic: true, index: false
t.timestamps
t.index %i[record_type record_id name], name: :index_action_text_rich_texts_uniqueness, unique: true
end
end
end
同様に spec/dummy/db/schema.rb へテーブル定義が追記され、テスト実行時にスキーマが正しく反映されます。これらの変更はライブラリ本体には影響せず、テスト専用のサンドボックスとして機能します。
設計判断
Action Text 用のロジックは既存の AttachmentHelpers に統合された点が重要です。新規モジュールを導入せず、plain_text_content から add_content_attachments までのフローを拡張したことで、コードベースの一貫性と再利用性が維持されています。外部依存は Active Storage と Action Text のみで、他の部分への侵入は最小限に抑えられました。
extract_content の公開インターフェイスは変更されていません。呼び出し側は引き続き RubyLLM::Content オブジェクトを受け取り、添付ファイルが自動的に内部に組み込まれるだけです。これにより既存ユーザーはアップグレード時にコード修正を不要とし、後方互換性が保たれます。
テスト用マイグレーションは dummy アプリ に限って提供され、実稼働環境のマイグレーションに混入しません。Action Text が必須でないプロジェクトでも本体ライブラリは影響を受けず、オプション的に機能を利用できる設計となっています。
まとめ
本 PR は Action Text の埋め込み添付ファイルを RubyLLM::Content に安全に取り込む機構を追加し、テキスト抽出の正確性と添付情報の完全性を向上させました。既存 API の変更はなく、テスト環境専用のマイグレーションだけが新たに導入されるため、ユーザーへの影響は最小限です。結果として LLM への入力がクリーンかつリッチになることが期待されます。