@@ -5,100 +5,58 @@ module RubyLLM
55 # Stores data in a standard internal format, letting providers
66 # handle their own formatting needs.
77 class Content
8- def initialize ( text = nil , attachments = { } ) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
9- @parts = [ ]
10- @parts << { type : 'text' , text : text } unless text . nil? || text . empty?
8+ attr_reader :text , :attachments
119
12- Array ( attachments [ :image ] ) . each do |source |
13- @parts << attach_image ( source )
14- end
15-
16- Array ( attachments [ :audio ] ) . each do |source |
17- @parts << attach_audio ( source )
18- end
10+ def initialize ( text = nil , attachments = { } )
11+ @text = text
12+ @attachments = [ ]
1913
20- Array ( attachments [ :pdf ] ) . each do |source |
21- @parts << attach_pdf ( source )
22- end
14+ process_attachments ( attachments )
15+ raise ArgumentError , 'Text and attachments cannot be both nil' if @text . nil? && @attachments . empty?
2316 end
2417
25- def to_a
26- return if @parts . empty?
27-
28- @parts
18+ def add_image ( source )
19+ @attachments << Attachments ::Image . new ( source )
20+ self
2921 end
3022
31- def format
32- return @parts . first [ :text ] if @parts . size == 1 && @parts . first [ :type ] == 'text'
33-
34- to_a
23+ def add_audio ( source )
24+ @attachments << Attachments ::Audio . new ( source )
25+ self
3526 end
3627
37- private
38-
39- def attach_image ( source ) # rubocop:disable Metrics/MethodLength
40- source = File . expand_path ( source ) unless source . start_with? ( 'http' )
41-
42- return { type : 'image' , source : { url : source } } if source . start_with? ( 'http' )
43-
44- data = Base64 . strict_encode64 ( File . read ( source ) )
45- mime_type = mime_type_for ( source )
46-
47- {
48- type : 'image' ,
49- source : {
50- type : 'base64' ,
51- media_type : mime_type ,
52- data : data
53- }
54- }
28+ def add_pdf ( source )
29+ @attachments << Attachments ::PDF . new ( source )
30+ self
5531 end
5632
57- def attach_audio ( source )
58- source = File . expand_path ( source ) unless source . start_with? ( 'http' )
59- data = encode_file ( source )
60- format = File . extname ( source ) . delete ( '.' ) || 'wav'
61-
62- {
63- type : 'input_audio' ,
64- input_audio : {
65- data : data ,
66- format : format
67- }
68- }
69- end
70-
71- def attach_pdf ( source )
72- source = File . expand_path ( source ) unless source . start_with? ( 'http' )
73-
74- pdf_data = {
75- type : 'pdf' ,
76- source : source
77- }
78-
79- # For local files, validate they exist
80- unless source . start_with? ( 'http' )
81- raise Error , "PDF file not found: #{ source } " unless File . exist? ( source )
82-
83- # Preload file content for providers that need it
84- pdf_data [ :content ] = File . read ( source )
33+ def format
34+ if @text && @attachments . empty?
35+ @text
36+ else
37+ self
8538 end
86-
87- pdf_data
8839 end
8940
90- def encode_file ( source )
91- if source . start_with? ( 'http' )
92- response = Faraday . get ( source )
93- Base64 . strict_encode64 ( response . body )
94- else
95- Base64 . strict_encode64 ( File . read ( source ) )
41+ # For Rails serialization
42+ def as_json
43+ hash = { text : @text }
44+ unless @attachments . empty?
45+ hash [ :attachments ] = @attachments . map do |a |
46+ { type : a . type , source : a . source }
47+ end
9648 end
49+ hash
9750 end
9851
99- def mime_type_for ( path )
100- ext = File . extname ( path ) . delete ( '.' )
101- "image/#{ ext } "
52+ private
53+
54+ def process_attachments ( attachments )
55+ return unless attachments . is_a? ( Hash )
56+
57+ Array ( attachments [ :image ] ) . each { |source | add_image ( source ) }
58+ Array ( attachments [ :audio ] ) . each { |source | add_audio ( source ) }
59+ Array ( attachments [ :pdf ] ) . each { |source | add_pdf ( source ) }
10260 end
10361 end
10462end
0 commit comments