@@ -77,7 +77,9 @@ export const hasVisionInput = (payload: ResponsesPayload): boolean => {
7777 return values . some ( ( item ) => containsVisionContent ( item ) )
7878}
7979
80- const IMAGE_DATA_URL_PATTERN = / ^ d a t a : ( i m a g e \/ [ a - z A - Z 0 - 9 . + - ] + ) ; b a s e 6 4 , ( .* ) $ / s
80+ const DATA_URL_PREFIX = "data:"
81+ const BASE64_MARKER = ";base64,"
82+ const IMAGE_MEDIA_TYPE_PATTERN = / ^ i m a g e \/ [ a - z A - Z 0 - 9 . + - ] + $ /
8183
8284export const sanitizeOversizedInputImages = (
8385 payload : ResponsesPayload ,
@@ -163,13 +165,24 @@ const getInputImageDataUrl = (
163165 return null
164166 }
165167
166- const match = record . image_url . match ( IMAGE_DATA_URL_PATTERN )
167- if ( ! match ) {
168+ const imageUrl = record . image_url
169+ const base64MarkerIndex = imageUrl . indexOf ( BASE64_MARKER )
170+ if (
171+ ! imageUrl . startsWith ( DATA_URL_PREFIX )
172+ || base64MarkerIndex <= DATA_URL_PREFIX . length
173+ ) {
174+ return null
175+ }
176+
177+ const mediaType = imageUrl . slice ( DATA_URL_PREFIX . length , base64MarkerIndex )
178+ if ( ! IMAGE_MEDIA_TYPE_PATTERN . test ( mediaType ) ) {
168179 return null
169180 }
170181
171- const mediaType = match [ 1 ]
172- const decodedBytes = Buffer . byteLength ( match [ 2 ] , "base64" )
182+ const decodedBytes = getBase64DecodedByteLength (
183+ imageUrl ,
184+ base64MarkerIndex + BASE64_MARKER . length ,
185+ )
173186
174187 return {
175188 decodedBytes,
@@ -178,6 +191,19 @@ const getInputImageDataUrl = (
178191 }
179192}
180193
194+ const getBase64DecodedByteLength = (
195+ value : string ,
196+ base64Start : number ,
197+ ) : number => {
198+ const base64Length = value . length - base64Start
199+ const padding =
200+ value . endsWith ( "==" ) ? 2
201+ : value . endsWith ( "=" ) ? 1
202+ : 0
203+
204+ return Math . max ( 0 , Math . floor ( ( base64Length * 3 ) / 4 ) - padding )
205+ }
206+
181207const replaceInputImageWithText = (
182208 image : InputImageDataUrl ,
183209 reason : string ,
0 commit comments