Class: Protege::StoredFile

Inherits:
Object
  • Object
show all
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

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(blob) ⇒ StoredFile

Returns a new instance of StoredFile.

Parameters:

  • blob (ActiveStorage::Blob)

    the stored blob to wrap



54
55
56
# File 'lib/protege/types/stored_file.rb', line 54

def initialize(blob)
  @blob = blob
end

Instance Attribute Details

#blobActiveStorage::Blob (readonly)

Returns the wrapped blob.

Returns:

  • (ActiveStorage::Blob)

    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.

Parameters:

  • blob_id (Integer, String)

    the ActiveStorage::Blob id

Returns:

  • (StoredFile, nil)

    the wrapped blob, or nil when the id is unknown



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).

Returns:

  • (Boolean)

    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

#filenameString

Returns the original filename as a plain string.

Returns:

  • (String)

    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.

Returns:

  • (Boolean)

    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_typeString

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.

Returns:

  • (String)

    the bare, lowercased media type



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

#summaryString

A concise one-line description for the model — filename, type, human size, and the blob id the tools accept.

Returns:

  • (String)

    e.g. "invoice.pdf (application/pdf, 42 KB, id: 12)"



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.

Returns:

  • (Boolean)

    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_attachmentGateway::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.

Returns:



80
81
82
# File 'lib/protege/types/stored_file.rb', line 80

def to_mail_attachment
  Gateway::Mail::Attachment.new(filename:, content_type:, bytes: download)
end

#to_partImagePart, ...

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.

Returns:



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).

Returns:

  • (Boolean)

    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