2626
2727# Parse environment variables with error handling
2828try :
29- S3VECTOR_BUCKET = os .environ [' S3VECTOR_BUCKET' ]
30- S3VECTOR_INDEX = os .environ [' S3VECTOR_INDEX' ]
31- S3VECTOR_DIMENSIONS = int (os .environ [' S3VECTOR_DIMENSIONS' ])
32- MODEL_ID = os .environ [' MODEL_ID' ]
33- TOP_K = int (os .environ [' TOP_K' ])
34- THRESHOLD = float (os .environ [' THRESHOLD' ])
29+ S3VECTOR_BUCKET = os .environ [" S3VECTOR_BUCKET" ]
30+ S3VECTOR_INDEX = os .environ [" S3VECTOR_INDEX" ]
31+ S3VECTOR_DIMENSIONS = int (os .environ [" S3VECTOR_DIMENSIONS" ])
32+ MODEL_ID = os .environ [" MODEL_ID" ]
33+ TOP_K = int (os .environ [" TOP_K" ])
34+ THRESHOLD = float (os .environ [" THRESHOLD" ])
3535except (KeyError , ValueError , IndexError ) as e :
3636 logger .error (f"Failed to parse environment variables: { e } " )
3737 raise
3838
3939# Initialize clients
40- s3vectors = boto3 .client (' s3vectors' )
40+ s3vectors = boto3 .client (" s3vectors" )
4141bedrock_client = bedrock .BedrockClient ()
4242
43+
4344def lambda_handler (event , context ):
4445 """
4546 Process a document to find similar examples using S3 Vectors similarity search.
@@ -61,7 +62,7 @@ def lambda_handler(event, context):
6162 }
6263 ]
6364 """
64-
65+
6566 try :
6667 logger .info ("=== DYNAMIC FEW-SHOT LAMBDA INVOKED ===" )
6768 logger .debug (f"Complete input event: { json .dumps (event , indent = 2 )} " )
@@ -73,7 +74,9 @@ def lambda_handler(event, context):
7374
7475 logger .info (f"=== INPUT VALUES ===" )
7576 logger .info (f"Class label: { class_label if class_label else 'Not specified' } " )
76- logger .info (f"Document text: { len (document_text ) if document_text else "0" } bytes" )
77+ logger .info (
78+ f"Document text: { len (document_text ) if document_text else '0' } bytes"
79+ )
7780 logger .info (f"Image content: { len (image_content )} images" )
7881
7982 # Decode input data
@@ -91,15 +94,18 @@ def lambda_handler(event, context):
9194
9295 logger .info ("=== DYNAMIC FEW-SHOT LAMBDA COMPLETED ===" )
9396 return result
94-
97+
9598 except Exception as e :
9699 logger .error (f"=== DYNAMIC FEW-SHOT LAMBDA ERROR ===" )
97100 logger .error (f"Error type: { type (e ).__name__ } " )
98101 logger .error (f"Error message: { str (e )} " )
99- logger .error (f"Input event keys: { list (event .keys ()) if 'event' in locals () else 'Unknown' } " )
102+ logger .error (
103+ f"Input event keys: { list (event .keys ()) if 'event' in locals () else 'Unknown' } "
104+ )
100105 # In demo, we'll fail gracefully with detailed error info
101106 raise Exception (f"Dynamic few-shot Lambda failed: { str (e )} " )
102107
108+
103109def _decode_images (image_content ):
104110 """Base64 decode image content to bytes"""
105111 result = []
@@ -108,14 +114,16 @@ def _decode_images(image_content):
108114 result .append (image_data )
109115 return result
110116
117+
111118def _encode_images (image_content ):
112119 """Base64 encode image content to JSON-serializable string"""
113120 result = []
114121 for image_bytes in image_content :
115- image_base64 = base64 .b64encode (image_bytes ).decode ("utf-8" )
116- result .append (image_base64 )
122+ image_base64 = base64 .b64encode (image_bytes ).decode ("utf-8" )
123+ result .append (image_base64 )
117124 return result
118125
126+
119127def _s3vectors_find_similar_items (image_data ):
120128 """Find similar items for input"""
121129 # find similar items based on image similarity only
@@ -133,21 +141,21 @@ def _s3vectors_find_similar_items(image_data):
133141
134142 # Only process this example if it has a non-empty attributesPrompt
135143 if not attributes_prompt or not attributes_prompt .strip ():
136- logger .info (
137- f"Skipping example with empty attributesPrompt: { key } "
138- )
144+ logger .info (f"Skipping example with empty attributesPrompt: { key } " )
139145 continue
140146
141147 attributes = _extract_metadata (metadata , distance )
142148 result .append (attributes )
143149
144150 # sort results by distance score (lowest to highest - lower is more similar)
145- sorted_result = sorted (result , key = lambda example : example ['distance' ], reverse = False )
151+ sorted_result = sorted (
152+ result , key = lambda example : example ["distance" ], reverse = False
153+ )
146154
147155 # filter result by distance score
148156 filtered_result = []
149157 for example in sorted_result :
150- if example [' distance' ] > THRESHOLD :
158+ if example [" distance" ] > THRESHOLD :
151159 logger .info (
152160 f"Skipping example with distance { example ['distance' ]} above threshold { THRESHOLD } : { key } "
153161 )
@@ -156,6 +164,7 @@ def _s3vectors_find_similar_items(image_data):
156164
157165 return filtered_result
158166
167+
159168def _s3vectors_find_similar_items_from_image (page_image ):
160169 """Search for similar items using image query"""
161170 embedding = bedrock_client .generate_embedding (
@@ -169,11 +178,12 @@ def _s3vectors_find_similar_items_from_image(page_image):
169178 queryVector = {"float32" : embedding },
170179 topK = TOP_K ,
171180 returnDistance = True ,
172- returnMetadata = True
181+ returnMetadata = True ,
173182 )
174183 logger .debug (f"S3 vectors lookup result: { response ['vectors' ]} " )
175184 return response ["vectors" ]
176185
186+
177187def _merge_examples (examples , new_examples ):
178188 """
179189 Merge in-place new examples into the result list, avoiding duplicates.
@@ -185,7 +195,7 @@ def _merge_examples(examples, new_examples):
185195 for new_example in new_examples :
186196 key = new_example ["key" ]
187197 new_distance = new_example .get ("distance" , 1.0 )
188-
198+
189199 # update example
190200 if examples .get (key ):
191201 existing_distance = examples [key ].get ("distance" , 1.0 )
@@ -195,9 +205,10 @@ def _merge_examples(examples, new_examples):
195205 else :
196206 examples [key ] = {
197207 "distance" : new_distance ,
198- "metadata" : new_example .get ("metadata" )
208+ "metadata" : new_example .get ("metadata" ),
199209 }
200210
211+
201212def _extract_metadata (metadata , distance ):
202213 """Create result object from S3 vectors metadata"""
203214 # Result object attributes
@@ -215,6 +226,7 @@ def _extract_metadata(metadata, distance):
215226
216227 return attributes
217228
229+
218230def _get_image_data_from_s3_path (image_path ):
219231 """
220232 Load images from image path
@@ -237,9 +249,7 @@ def _get_image_data_from_s3_path(image_path):
237249 # Direct S3 URI
238250 image_bytes = s3 .get_binary_content (image_file_path )
239251 else :
240- raise ValueError (
241- f"Invalid file path { image_path } - expecting S3 path"
242- )
252+ raise ValueError (f"Invalid file path { image_path } - expecting S3 path" )
243253
244254 image_content .append (image_bytes )
245255 except Exception as e :
@@ -248,6 +258,7 @@ def _get_image_data_from_s3_path(image_path):
248258
249259 return image_content
250260
261+
251262def _get_image_files_from_s3_path (image_path ):
252263 """
253264 Get list of image files from an S3 path.
@@ -260,9 +271,7 @@ def _get_image_files_from_s3_path(image_path):
260271 """
261272 # Handle S3 URIs
262273 if not image_path .startswith ("s3://" ):
263- raise ValueError (
264- f"Invalid file path { image_path } - expecting S3 URI"
265- )
274+ raise ValueError (f"Invalid file path { image_path } - expecting S3 URI" )
266275
267276 # Check if it's a direct file or a prefix
268277 if image_path .endswith (
0 commit comments