okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
9dd3a56
main
1//// The kernel reads image headers itself so `show_image` can refuse an image 2//// over the model's edge, or one cut short, at the call, and the daemon reads 3//// them again as the authority. Two parsers in two languages drift apart 4//// silently, and E2E only ever sends one well-formed PNG, so these fixtures 5//// hold both to one answer. 6 7import albedo/daemon/image 8import albedo/openai_api/types 9 10import albedo/daemon/store 11import albedo/harness/extensions/python/kernel as python 12import gleam/bit_array 13import gleam/int 14import gleam/json 15import gleam/list 16import gleam/option .{ type Option , None , Some } 17import gleam/string 18import gleeunit/should 19import harness/kernel_fixture 20 21/// Name, canonical base64, and the MIME type and size its header says. 22const fixtures = [ 23#( 24"png" , 25"iVBORw0KGgoAAAANSUhEUgAACQAAAAS8CAIAAABW4yLdAAAAAElFTkSuQmCC" , 26Some (#( "image/png" , 2304 , 1212 )), 27), 28#( 29"png bytes after iend" , 30"iVBORw0KGgoAAAANSUhEUgAACQAAAAS8CAIAAABW4yLdAAAAAElFTkSuQmCCdGFpbA==" , 31Some (#( "image/png" , 2304 , 1212 )), 32), 33#( 34"png stops before iend" , 35"iVBORw0KGgoAAAANSUhEUgAACQAAAAS8CAIAAABW4yLd" , 36None , 37), 38#( "png header only" , "iVBORw0KGgoAAAANSUhEUgAACQAAAAS8CAIAAAA=" , None ), 39#( "png zero width" , "iVBORw0KGgoAAAANSUhEUgAAAAAAAAAFCAIAAAA=" , None ), 40#( "png truncated" , "iVBORw0KGgoAAAANSUhEUgAAAAM=" , None ), 41#( 42"jpeg baseline" , 43"/9j/4AAQSkZJRgABAQAAAQABAAD/wAAICAHgAoAD/9k=" , 44Some (#( "image/jpeg" , 640 , 480 )), 45), 46#( 47"jpeg progressive" , 48"/9j/wgAICAu4D6AD/9k=" , 49Some (#( "image/jpeg" , 4000 , 3000 )), 50), 51#( 52"jpeg fill bytes and restart" , 53"/9j/0P///+AAEEpGSUYAAQEAAAEAAQAAAAD/wQAICAAJAAcD/9k=" , 54Some (#( "image/jpeg" , 7 , 9 )), 55), 56#( 57"jpeg without end of image" , 58"/9j/4AAQSkZJRgABAQAAAQABAAD/wAAICAHgAoAD" , 59None , 60), 61#( "jpeg scan before frame" , "/9j/2gAEAAD/wAAICAABAAED" , None ), 62#( "jpeg truncated segment" , "/9j/4QAWeHh4eHh4" , None ), 63#( "jpeg zero height" , "/9j/wAAICAAAAAoD" , None ), 64#( "jpeg short length" , "/9j/4AAB/8AACAgAAgACAw==" , None ), 65#( 66"webp vp8x" , 67"UklGRhYAAABXRUJQVlA4WAoAAAAAAAAAtwsAEwAA" , 68Some (#( "image/webp" , 3000 , 20 )), 69), 70#( 71"webp vp8l" , 72"UklGRhIAAABXRUJQVlA4TAYAAAAvAMgDAAA=" , 73Some (#( "image/webp" , 2049 , 16 )), 74), 75#( 76"webp vp8" , 77"UklGRhYAAABXRUJQVlA4IAoAAAAAAACdASqABzgE" , 78Some (#( "image/webp" , 1920 , 1080 )), 79), 80#( 81"webp chunk before image" , 82"UklGRh4AAABXRUJQSUNDUAMAAABhYmMAVlA4TAYAAAAvBEABAAA=" , 83Some (#( "image/webp" , 5 , 6 )), 84), 85#( 86"webp vp8 without start code" , 87"UklGRhYAAABXRUJQVlA4IAoAAAAAAAAAAAAFAAUA" , 88None , 89), 90#( "webp riff size mismatch" , "UklGRhIAAABXRUJQVlA4TAYAAAAvBEABAAAAAA==" , None ), 91#( "webp no image chunk" , "UklGRhAAAABXRUJQRVhJRgQAAABhYmNk" , None ), 92#( "garbage" , "bm90IGFuIGltYWdlIGF0IGFsbA==" , None ), 93] 94 95pub fn the_kernel_reads_image_headers_the_way_the_daemon_does_test () -> Nil { 96let expected = list . map ( fixtures , fn ( fixture ) { #( fixture . 0 , fixture . 2 ) }) 97list . map ( fixtures , fn ( fixture ) { 98#( fixture . 0 , case image . inspect ( fixture . 1 ) { 99Ok (#( mime , width , height , _ )) -> Some (#( mime , width , height )) 100Error ( _ ) -> None 101}) 102}) 103|> should . equal ( expected ) 104 105let assert Ok ( store ) = store . start ( ":memory:" , "" ) 106let assert Ok ( kernel ) = kernel_fixture . local ( store , "/tmp" ) 107let encoded = json . array ( fixtures , fn ( fixture ) { json . string ( fixture . 1 ) }) 108let assert Ok ( read ) = 109python . execute ( 110kernel , 111"headers" , 112"import base64, json\nfrom albedo_capture import image_size as size\n" 113<> "for data in json.loads(" 114<> string . inspect ( json . to_string ( encoded )) 115<> "):\n print(json.dumps(size(base64.b64decode(data))))" , 11610_000 , 117) 118list . zip ( 119list . map ( fixtures , fn ( fixture ) { fixture . 0 }), 120string . split ( string . trim_end ( read . output ), "\n" ), 121) 122|> should . equal ( 123list . map ( expected , fn ( pair ) { #( pair . 0 , header_json ( pair . 1 )) }), 124) 125let assert Ok ( _ ) = python . stop ( kernel ) 126store . close ( store ) 127} 128 129fn header_json ( header : Option (#( String , Int , Int ))) -> String { 130case header { 131Some (#( mime , width , height )) -> 132"[\"" 133<> mime 134<> "\", " 135<> int . to_string ( width ) 136<> ", " 137<> int . to_string ( height ) 138<> "]" 139None -> "null" 140} 141} 142 143@external ( erlang , "albedo_images" , "encode_base64" ) 144fn encode_base64 ( bytes : BitArray ) -> String 145 146/// A whole 2x3 PNG: signature, IHDR, and IEND, in 45 bytes. 147const png = "iVBORw0KGgoAAAANSUhEUgAAAAIAAAADCAIAAAA2iEnWAAAAAElFTkSuQmCC" 148 149pub fn canonical_base64_and_metadata_must_agree_test () -> Nil { 150image . inspect ( png <> "AA==" ) |> should . equal ( Ok (#( "image/png" , 2 , 3 , 46 ))) 151image . inspect ( png <> "AB==" ) |> should . equal ( Error ( Nil )) 152image . inspect ( png <> "AAA=" ) |> should . equal ( Ok (#( "image/png" , 2 , 3 , 47 ))) 153image . inspect ( png <> "AAB=" ) |> should . equal ( Error ( Nil )) 154image . inspect ( png <> "AA==AAAA" ) |> should . equal ( Error ( Nil )) 155image . valid_payload ( "image/png" , png , 2 , 3 , 45 ) |> should . be_true 156image . valid_payload ( "image/png" , png , 2 , 4 , 45 ) |> should . be_false 157image . valid_payload ( "image/jpeg" , png , 2 , 3 , 45 ) |> should . be_false 158image . valid_payload ( "image/png" , png , 2 , 3 , 46 ) |> should . be_false 159} 160 161pub fn late_jpeg_headers_and_webp_riff_sizes_require_full_decode_test () -> Nil { 162let padding = bit_array . from_string ( string . repeat ( "x" , 60_000 )) 163let jpeg = << 1640xFF , 1650xD8 , 1660xFF , 1670xE0 , 16860_002 : size ( 16 ), 169padding : bits , 1700xFF , 1710xC0 , 1728 : size ( 16 ), 1738 , 1743 : size ( 16 ), 1752 : size ( 16 ), 1763 , 177>> 178let data = encode_base64 ( jpeg ) 179image . valid_payload ( "image/jpeg" , data , 2 , 3 , bit_array . byte_size ( jpeg )) 180|> should . be_true 181let chunks = << 182"JUNK" : utf8 , 18360_000 : little - size ( 32 ), 184padding : bits , 185"VP8L" : utf8 , 1866 : little - size ( 32 ), 1870x2F , 18881_924 : little - size ( 32 ), 1890 , 190>> 191let size = 4 + bit_array . byte_size ( chunks ) 192let webp = << "RIFF" : utf8 , size : little - size ( 32 ), "WEBP" : utf8 , chunks : bits >> 193image . valid_payload ( 194"image/webp" , 195encode_base64 ( webp ), 1965 , 1976 , 198bit_array . byte_size ( webp ), 199) 200|> should . be_true 201let bad_size = << "RIFF" : utf8 , 4 : little - size ( 32 ), "WEBP" : utf8 , chunks : bits >> 202image . inspect ( encode_base64 ( bad_size )) |> should . equal ( Error ( Nil )) 203} 204 205pub fn canonical_payload_size_enforces_the_decoded_limit_test () -> Nil { 206let assert Ok ( whole ) = bit_array . base64_decode ( png ) 207let tail = types . max_image_bytes - bit_array . byte_size ( whole ) 208let padding = bit_array . from_string ( string . repeat ( "x" , tail )) 209let bytes = << whole : bits , padding : bits >> 210image . inspect ( encode_base64 ( bytes )) 211|> should . equal ( Ok (#( "image/png" , 2 , 3 , types . max_image_bytes ))) 212image . inspect ( encode_base64 (<< bytes : bits , 0 >>)) |> should . equal ( Error ( Nil )) 213}