Module: RubyLLM::Protocols::Converse::Media

Defined in:
lib/ruby_llm/protocols/converse/media.rb

Overview

Media formatting for Bedrock Converse content blocks.

Constant Summary collapse

SUPPORTED_DOCUMENT_FORMATS =
%w[pdf csv doc docx xls xlsx html txt md].freeze
SUPPORTED_AUDIO_FORMATS =
%w[mp3 opus wav aac flac mp4 ogg mkv mka x-aac m4a mpeg mpga pcm webm].freeze
SUPPORTED_VIDEO_FORMATS =
%w[mkv mov mp4 webm flv mpeg mpg wmv three_gp].freeze
AUDIO_FORMAT_ALIASES =
{
  'x-flac' => 'flac',
  'x-m4a' => 'm4a'
}.freeze
VIDEO_FORMAT_ALIASES =
{
  'quicktime' => 'mov',
  'x-matroska' => 'mkv',
  'x-ms-wmv' => 'wmv',
  '3gp' => 'three_gp',
  '3gpp' => 'three_gp'
}.freeze

Class Method Summary collapse

Class Method Details

.document_format(attachment) ⇒ Object



158
159
160
# File 'lib/ruby_llm/protocols/converse/media.rb', line 158

def document_format(attachment)
  attachment.extension || attachment.format
end

.format_attachment(attachment, used_document_names:, citations: false) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/ruby_llm/protocols/converse/media.rb', line 20

def format_attachment(attachment, used_document_names:, citations: false)
  if attachment.provider_file?
    return format_provider_file_attachment(attachment, used_document_names:, citations:)
  end

  case attachment.type
  when :image
    format_image_attachment(attachment)
  when :audio
    format_audio_attachment(attachment)
  when :video
    format_video_attachment(attachment)
  when :pdf, :document
    format_document_attachment(attachment, used_document_names:, citations:)
  when :text
    if citations
      format_text_document_attachment(attachment, used_document_names:)
    else
      format_text_attachment(attachment)
    end
  else
    raise UnsupportedAttachmentError, attachment.mime_type
  end
end

.format_audio_attachment(attachment) ⇒ Object



71
72
73
74
75
76
77
78
79
80
# File 'lib/ruby_llm/protocols/converse/media.rb', line 71

def format_audio_attachment(attachment)
  {
    audio: {
      format: media_format(attachment, AUDIO_FORMAT_ALIASES, SUPPORTED_AUDIO_FORMATS),
      source: {
        bytes: attachment.encoded
      }
    }
  }
end

.format_content(content, attachments = [], used_document_names: nil, citations: false) ⇒ Object



10
11
12
13
14
15
16
17
18
# File 'lib/ruby_llm/protocols/converse/media.rb', line 10

def format_content(content, attachments = [], used_document_names: nil, citations: false)
  used_document_names ||= {}
  blocks = []
  blocks << { text: content } unless content.nil? || content.empty?
  attachments.each do |attachment|
    blocks << format_attachment(attachment, used_document_names:, citations:)
  end
  blocks
end

.format_document_attachment(attachment, used_document_names:, citations: false) ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/ruby_llm/protocols/converse/media.rb', line 105

def format_document_attachment(attachment, used_document_names:, citations: false)
  format = document_format(attachment)

  raise UnsupportedAttachmentError, attachment.mime_type unless supported_document_format?(attachment)

  document_name = unique_document_name(sanitize_document_name(attachment.filename), used_document_names)
  document = {
    format: format,
    name: document_name,
    source: {
      bytes: attachment.encoded
    }
  }
  document[:citations] = { enabled: true } if citations
  { document: document }
end

.format_image_attachment(attachment) ⇒ Object



60
61
62
63
64
65
66
67
68
69
# File 'lib/ruby_llm/protocols/converse/media.rb', line 60

def format_image_attachment(attachment)
  {
    image: {
      format: attachment.format,
      source: {
        bytes: attachment.encoded
      }
    }
  }
end

.format_provider_file_attachment(attachment, used_document_names:, citations: false) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/ruby_llm/protocols/converse/media.rb', line 137

def format_provider_file_attachment(attachment, used_document_names:, citations: false)
  raise UnsupportedAttachmentError, attachment.mime_type unless supported_document_format?(attachment)

  document_name = unique_document_name(sanitize_document_name(attachment.filename), used_document_names)
  document = {
    format: document_format(attachment),
    name: document_name,
    source: {
      s3Location: {
        uri: attachment.provider_file_uri || attachment.provider_file_id
      }
    }
  }
  document[:citations] = { enabled: true } if citations
  { document: document }
end

.format_text_attachment(attachment) ⇒ Object



101
102
103
# File 'lib/ruby_llm/protocols/converse/media.rb', line 101

def format_text_attachment(attachment)
  { text: attachment.for_llm }
end

.format_text_document_attachment(attachment, used_document_names:) ⇒ Object

Text attachments become citable documents when citations are enabled. Bedrock rejects text-format documents sent as bytes when citations are on, so the text rides in the source's text member.



125
126
127
128
129
130
131
132
133
134
135
# File 'lib/ruby_llm/protocols/converse/media.rb', line 125

def format_text_document_attachment(attachment, used_document_names:)
  document_name = unique_document_name(sanitize_document_name(attachment.filename), used_document_names)
  {
    document: {
      format: 'txt',
      name: document_name,
      source: { text: attachment.content },
      citations: { enabled: true }
    }
  }
end

.format_video_attachment(attachment) ⇒ Object



82
83
84
85
86
87
88
89
90
91
# File 'lib/ruby_llm/protocols/converse/media.rb', line 82

def format_video_attachment(attachment)
  {
    video: {
      format: media_format(attachment, VIDEO_FORMAT_ALIASES, SUPPORTED_VIDEO_FORMATS),
      source: {
        bytes: attachment.encoded
      }
    }
  }
end

.media_format(attachment, aliases, supported) ⇒ Object



93
94
95
96
97
98
99
# File 'lib/ruby_llm/protocols/converse/media.rb', line 93

def media_format(attachment, aliases, supported)
  format = attachment.format.to_s
  format = aliases.fetch(format, format)
  raise UnsupportedAttachmentError, attachment.mime_type unless supported.include?(format)

  format
end

.sanitize_document_name(filename) ⇒ Object



162
163
164
165
166
# File 'lib/ruby_llm/protocols/converse/media.rb', line 162

def sanitize_document_name(filename)
  base = File.basename(filename.to_s, '.*')
  safe = base.gsub(/[^a-zA-Z0-9_-]/, '_')
  safe.empty? ? 'document' : safe
end

.supported_document_format?(attachment) ⇒ Boolean

Returns:

  • (Boolean)


154
155
156
# File 'lib/ruby_llm/protocols/converse/media.rb', line 154

def supported_document_format?(attachment)
  SUPPORTED_DOCUMENT_FORMATS.include?(document_format(attachment))
end

.unique_document_name(base_name, used_names) ⇒ Object



168
169
170
171
172
173
174
# File 'lib/ruby_llm/protocols/converse/media.rb', line 168

def unique_document_name(base_name, used_names)
  count = used_names[base_name].to_i
  used_names[base_name] = count + 1
  return base_name if count.zero?

  "#{base_name}_#{count + 1}"
end