Class: Protege::StoredFile
- Inherits:
-
Object
- Object
- Protege::StoredFile
- Extended by:
- Forwardable
- Defined in:
- lib/protege/types/stored_file.rb
Overview
Domain wrapper around a stored ActiveStorage::Blob — the single source of truth for how the
engine interprets a file: its kind (image / document / text / viewable), a one-line human summary,
and how it renders into the shapes other layers speak. It renders itself two ways: #to_part
produces the provider's multimodal content block (an ImagePart / DocumentPart) that inference
feeds a vision model, and #to_mail_attachment produces the transport payload the send path
attaches to outbound mail. ReadAttachmentTool and ThreadHistoryResolver read its kind and
summary. MIME classification (and the parameter-stripping that makes image/jpeg; name=… match)
lives here and nowhere else, so provider and tool authors classify files without re-deriving the
rules.
A file's durable identity is its blob — the per-message ActiveStorage::Attachment is just a
join that is recreated whenever a file rides on a new message. So this wraps the blob directly:
the #id it reports (and the id in #summary) is the blob id, which is the currency the
read_attachment, send_email, and create_file tools all speak.
Constant Summary collapse
- IMAGE_TYPES =
Content types the vision models accept as inline images (jpeg/png/gif/webp). Other image types (HEIC, BMP, TIFF, SVG) are deliberately excluded — the models can't view them.
%w[image/png image/jpeg image/jpg image/gif image/webp].freeze
- DOCUMENT_TYPES =
Content types sent as a native document block. PDF is the only type the models read as a document today; listed as a set so a newly-supported type is a one-line addition.
%w[application/pdf].freeze
- TEXT_TYPES =
Non-+text/*+ content types whose payloads are safe to return as text.
%w[application/json application/xml application/csv].freeze
Instance Attribute Summary collapse
-
#blob ⇒ ActiveStorage::Blob
readonly
The wrapped blob.
Class Method Summary collapse
-
.find(blob_id) ⇒ StoredFile?
Look up a stored file by its blob id, or
nilwhen no blob matches.
Instance Method Summary collapse
-
#document? ⇒ Boolean
True when the file is a document a model can read natively (PDF).
-
#filename ⇒ String
The original filename as a plain string.
-
#image? ⇒ Boolean
True when the file is an image a vision model can view.
-
#initialize(blob) ⇒ StoredFile
constructor
A new instance of StoredFile.
-
#media_type ⇒ String
The bare MIME type, lowercased and stripped of header parameters (e.g.
"image/jpeg; name=p.jpg"→"image/jpeg"), so classification and part media types match cleanly. -
#summary ⇒ String
A concise one-line description for the model — filename, type, human size, and the blob id the tools accept.
-
#text? ⇒ Boolean
True when the file's content can be returned as plain text.
-
#to_mail_attachment ⇒ Gateway::Mail::Attachment
Build the outbound mail payload for this file — the typed attachment the send path attaches to a message.
-
#to_part ⇒ ImagePart, ...
Render this file as the provider content part a vision model reads, or
nilwhen its type can't be viewed (video, Office formats, archives). -
#viewable? ⇒ Boolean
True when the file can be shown to the model as a vision part (image or PDF).
Constructor Details
#initialize(blob) ⇒ StoredFile
Returns a new instance of StoredFile.
54 55 56 |
# File 'lib/protege/types/stored_file.rb', line 54 def initialize(blob) @blob = blob end |
Instance Attribute Details
#blob ⇒ ActiveStorage::Blob (readonly)
Returns the wrapped blob.
48 49 50 |
# File 'lib/protege/types/stored_file.rb', line 48 def blob @blob end |
Class Method Details
.find(blob_id) ⇒ StoredFile?
Look up a stored file by its blob id, or nil when no blob matches. Nil-safe (uses find_by,
not find), so a tool turns a missing id into a failed Result rather than raising. Single-
argument lookup, so it stays positional by convention.
41 42 43 44 |
# File 'lib/protege/types/stored_file.rb', line 41 def find(blob_id) blob = ActiveStorage::Blob.find_by(id: blob_id) blob && new(blob) end |
Instance Method Details
#document? ⇒ Boolean
Returns true when the file is a document a model can read natively (PDF).
90 91 92 |
# File 'lib/protege/types/stored_file.rb', line 90 def document? DOCUMENT_TYPES.include?(media_type) end |
#filename ⇒ String
Returns the original filename as a plain string.
59 60 61 |
# File 'lib/protege/types/stored_file.rb', line 59 def filename blob.filename.to_s end |
#image? ⇒ Boolean
Returns true when the file is an image a vision model can view.
85 86 87 |
# File 'lib/protege/types/stored_file.rb', line 85 def image? IMAGE_TYPES.include?(media_type) end |
#media_type ⇒ String
The bare MIME type, lowercased and stripped of header parameters (e.g.
"image/jpeg; name=p.jpg" → "image/jpeg"), so classification and part media types match cleanly.
108 109 110 |
# File 'lib/protege/types/stored_file.rb', line 108 def media_type content_type.to_s.split(';').first.to_s.strip.downcase end |
#summary ⇒ String
A concise one-line description for the model — filename, type, human size, and the blob id the tools accept.
116 117 118 |
# File 'lib/protege/types/stored_file.rb', line 116 def summary "#{filename} (#{content_type}, #{ActiveSupport::NumberHelper.number_to_human_size(byte_size)}, id: #{id})" end |
#text? ⇒ Boolean
Returns true when the file's content can be returned as plain text.
95 96 97 |
# File 'lib/protege/types/stored_file.rb', line 95 def text? media_type.start_with?('text/') || TEXT_TYPES.include?(media_type) end |
#to_mail_attachment ⇒ Gateway::Mail::Attachment
Build the outbound mail payload for this file — the typed attachment the send path attaches to a message. Downloads the bytes for transport; the blob itself remains the durable identity.
80 81 82 |
# File 'lib/protege/types/stored_file.rb', line 80 def Gateway::Mail::Attachment.new(filename:, content_type:, bytes: download) end |
#to_part ⇒ ImagePart, ...
Render this file as the provider content part a vision model reads, or nil when its type can't
be viewed (video, Office formats, archives). Images become an ImagePart, PDFs a DocumentPart;
the bytes are downloaded and Base64-encoded only when there is a part to build.
68 69 70 71 72 73 74 |
# File 'lib/protege/types/stored_file.rb', line 68 def to_part if image? ImagePart.new(media_type:, data: encoded_bytes) elsif document? DocumentPart.new(filename:, media_type:, data: encoded_bytes) end end |
#viewable? ⇒ Boolean
Returns true when the file can be shown to the model as a vision part (image or PDF).
100 101 102 |
# File 'lib/protege/types/stored_file.rb', line 100 def viewable? image? || document? end |