@@ -37,17 +37,21 @@ const base64Only = new Set([
37
37
EModelEndpoint . bedrock ,
38
38
] ) ;
39
39
40
+ const blobStorageSources = new Set ( [ FileSources . azure , FileSources . s3 ] ) ;
41
+
40
42
/**
41
43
* Encodes and formats the given files.
42
44
* @param {Express.Request } req - The request object.
43
45
* @param {Array<MongoFile> } files - The array of files to encode and format.
44
46
* @param {EModelEndpoint } [endpoint] - Optional: The endpoint for the image.
45
47
* @param {string } [mode] - Optional: The endpoint mode for the image.
46
- * @returns {Promise<Object > } - A promise that resolves to the result object containing the encoded images and file details.
48
+ * @returns {Promise<{ text: string; files: MongoFile[]; image_urls: MessageContentImageUrl[] } > } - A promise that resolves to the result object containing the encoded images and file details.
47
49
*/
48
50
async function encodeAndFormat ( req , files , endpoint , mode ) {
49
51
const promises = [ ] ;
52
+ /** @type {Record<FileSources, Pick<ReturnType<typeof getStrategyFunctions>, 'prepareImagePayload' | 'getDownloadStream'>> } */
50
53
const encodingMethods = { } ;
54
+ /** @type {{ text: string; files: MongoFile[]; image_urls: MessageContentImageUrl[] } } */
51
55
const result = {
52
56
text : '' ,
53
57
files : [ ] ,
@@ -59,6 +63,7 @@ async function encodeAndFormat(req, files, endpoint, mode) {
59
63
}
60
64
61
65
for ( let file of files ) {
66
+ /** @type {FileSources } */
62
67
const source = file . source ?? FileSources . local ;
63
68
if ( source === FileSources . text && file . text ) {
64
69
result . text += `${ ! result . text ? 'Attached document(s):\n```md' : '\n\n---\n\n' } # "${ file . filename } "\n${ file . text } \n` ;
@@ -70,18 +75,51 @@ async function encodeAndFormat(req, files, endpoint, mode) {
70
75
}
71
76
72
77
if ( ! encodingMethods [ source ] ) {
73
- const { prepareImagePayload } = getStrategyFunctions ( source ) ;
78
+ const { prepareImagePayload, getDownloadStream } = getStrategyFunctions ( source ) ;
74
79
if ( ! prepareImagePayload ) {
75
80
throw new Error ( `Encoding function not implemented for ${ source } ` ) ;
76
81
}
77
82
78
- encodingMethods [ source ] = prepareImagePayload ;
83
+ encodingMethods [ source ] = { prepareImagePayload, getDownloadStream } ;
79
84
}
80
85
81
- const preparePayload = encodingMethods [ source ] ;
86
+ const preparePayload = encodingMethods [ source ] . prepareImagePayload ;
87
+ /* We need to fetch the image and convert it to base64 if we are using S3/Azure Blob storage. */
88
+ if ( blobStorageSources . has ( source ) ) {
89
+ try {
90
+ const downloadStream = encodingMethods [ source ] . getDownloadStream ;
91
+ const stream = await downloadStream ( req , file . filepath ) ;
92
+ const streamPromise = new Promise ( ( resolve , reject ) => {
93
+ /** @type {Uint8Array[] } */
94
+ const chunks = [ ] ;
95
+ stream . on ( 'readable' , ( ) => {
96
+ let chunk ;
97
+ while ( null !== ( chunk = stream . read ( ) ) ) {
98
+ chunks . push ( chunk ) ;
99
+ }
100
+ } ) ;
101
+
102
+ stream . on ( 'end' , ( ) => {
103
+ const buffer = Buffer . concat ( chunks ) ;
104
+ const base64Data = buffer . toString ( 'base64' ) ;
105
+ resolve ( base64Data ) ;
106
+ } ) ;
107
+ stream . on ( 'error' , ( error ) => {
108
+ reject ( error ) ;
109
+ } ) ;
110
+ } ) ;
111
+ const base64Data = await streamPromise ;
112
+ promises . push ( [ file , base64Data ] ) ;
113
+ } catch ( error ) {
114
+ logger . error (
115
+ `Error processing blob storage file stream for ${ file . name } base64 payload:` ,
116
+ error ,
117
+ ) ;
118
+ continue ;
119
+ }
82
120
83
- /* Google & Anthropic don't support passing URLs to payload */
84
- if ( source !== FileSources . local && base64Only . has ( endpoint ) ) {
121
+ /* Google & Anthropic don't support passing URLs to payload */
122
+ } else if ( source !== FileSources . local && base64Only . has ( endpoint ) ) {
85
123
const [ _file , imageURL ] = await preparePayload ( req , file ) ;
86
124
promises . push ( [ _file , await fetchImageToBase64 ( imageURL ) ] ) ;
87
125
continue ;
0 commit comments