@@ -3,6 +3,9 @@ import { PutCommand, UpdateCommand, paginateQuery, TransactWriteCommand } from '
3
3
import { getBytesFromKey } from './s3' ;
4
4
import sharp from 'sharp' ;
5
5
import { ddb , TableName } from './ddb' ;
6
+ import { existsSync , mkdirSync , writeFileSync } from 'fs' ;
7
+ import path from 'path' ;
8
+ import { fileTypeFromBuffer } from 'file-type' ;
6
9
7
10
// Maximum input token count before applying middle-out strategy
8
11
export const MAX_INPUT_TOKEN = 80_000 ;
@@ -199,29 +202,80 @@ const preProcessMessageContent = async (content: Message['content']) => {
199
202
} ;
200
203
201
204
const imageCache : Record < string , Buffer > = { } ;
205
+ // Image sequence number, reset when the process is killed
206
+ let imageSeqNo = 0 ;
207
+
208
+ // Ensure the images directory exists
209
+ const ensureImagesDirectory = ( ) => {
210
+ const imagesDir = path . join ( process . env . HOME || '' , '.remote_swe_workspace' , 'images' ) ;
211
+ if ( ! existsSync ( imagesDir ) ) {
212
+ mkdirSync ( imagesDir , { recursive : true } ) ;
213
+ }
214
+ return imagesDir ;
215
+ } ;
216
+
217
+ // Save image to local filesystem and return the path
218
+ const saveImageToLocalFs = async ( imageBuffer : Buffer ) : Promise < string > => {
219
+ const imagesDir = ensureImagesDirectory ( ) ;
220
+
221
+ // Since we're converting to webp above, we know the extension
222
+ const extension = 'webp' ;
223
+
224
+ // Create path with sequence number
225
+ const fileName = `image${ imageSeqNo } .${ extension } ` ;
226
+ const filePath = path . join ( imagesDir , fileName ) ;
227
+
228
+ // Write image to file
229
+ writeFileSync ( filePath , imageBuffer ) ;
230
+
231
+ // Increment sequence number for next image
232
+ imageSeqNo ++ ;
233
+
234
+ // Return the path in the format specified in the issue
235
+ return `.remote_swe_workspace/images/${ fileName } ` ;
236
+ } ;
237
+
202
238
const postProcessMessageContent = async ( content : string ) => {
203
- return await Promise . all (
204
- JSON . parse ( content ) . map ( async ( c : any ) => {
205
- if ( ! ( 'image' in c ) ) return c ;
206
- // embed images
207
- const s3Key = c . image . source . s3Key ;
208
- let webp : Buffer ;
209
- if ( s3Key in imageCache ) {
210
- webp = imageCache [ s3Key ] ;
211
- } else {
212
- const file = await getBytesFromKey ( s3Key ) ;
213
- // using sharp, convert file to webp
214
- webp = await sharp ( file ) . webp ( { lossless : false , quality : 80 } ) . toBuffer ( ) ;
215
- imageCache [ s3Key ] = webp ;
216
- }
217
- return {
218
- image : {
219
- format : 'webp' ,
220
- source : {
221
- bytes : webp ,
222
- } ,
239
+ const contentArray = JSON . parse ( content ) ;
240
+ const resultArray = [ ] ;
241
+
242
+ for ( const c of contentArray ) {
243
+ if ( ! ( 'image' in c ) ) {
244
+ resultArray . push ( c ) ;
245
+ continue ;
246
+ }
247
+
248
+ // Process image
249
+ const s3Key = c . image . source . s3Key ;
250
+ let imageBuffer : Buffer ;
251
+
252
+ if ( s3Key in imageCache ) {
253
+ imageBuffer = imageCache [ s3Key ] ;
254
+ } else {
255
+ const file = await getBytesFromKey ( s3Key ) ;
256
+ // Convert file to webp
257
+ imageBuffer = await sharp ( file ) . webp ( { lossless : false , quality : 80 } ) . toBuffer ( ) ;
258
+ imageCache [ s3Key ] = imageBuffer ;
259
+ }
260
+
261
+ // Add image to result
262
+ resultArray . push ( {
263
+ image : {
264
+ format : 'webp' ,
265
+ source : {
266
+ bytes : imageBuffer ,
223
267
} ,
224
- } ;
225
- } )
226
- ) ;
268
+ } ,
269
+ } ) ;
270
+
271
+ // Save image to local filesystem
272
+ const localPath = await saveImageToLocalFs ( imageBuffer ) ;
273
+
274
+ // Add a text block after the image with the path information
275
+ resultArray . push ( {
276
+ text : `the image is stored locally on ${ localPath } ` ,
277
+ } ) ;
278
+ }
279
+
280
+ return resultArray ;
227
281
} ;
0 commit comments