okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
79b55b3
main
1"""Image tool outputs reach the model, and compaction reclaims the ones it evicts.""" 2 3import base64 4import hashlib 5import json 6import sqlite3 7import unittest 8import uuid 9 10from harness import Albedo ,Provider ,error ,exclusive ,operation_id ,python ,text 11 12 13PNG = "iVBORw0KGgoAAAANSUhEUgAAAAIAAAADCAIAAAA2iEnWAAAAAElFTkSuQmCC" 14SHOWN = "iVBORw0KGgoAAAANSUhEUgAAABEAAAADCAIAAAD6EXM9AAAAAElFTkSuQmCC" 15UPLOADED = "iVBORw0KGgoAAAANSUhEUgAAABIAAAADCAIAAAARJsg+AAAAAElFTkSuQmCC" 16MARKER = "image elided and is no longer available" 17 18 19class ImageToolOutputTest (unittest .TestCase ): 20def test_python_image_is_attached_and_invalid_image_is_reported (self ): 21def reply (request ): 22inputs = request ["input" ] 23if inputs [- 1 ].get ("type" )== "function_call_output" : 24return text ("done" ) 25user = next ( 26item ["content" ] 27for item in reversed (inputs ) 28if item .get ("role" )== "user" 29 ) 30if user == "show a valid image" : 31return python ( 32"import base64\nshow_image(base64.b64decode('" + PNG + "'))" 33 ) 34return python ("show_image(b'\\x89PNG\\r\\n\\x1a\\nnot a header')" ) 35 36def tool_output (provider ): 37inputs = provider .requests [- 1 ]["request" ]["input" ] 38return next ( 39item for item in inputs if item .get ("type" )== "function_call_output" 40 ) 41 42provider = Provider (reply ) 43try : 44with Albedo (provider ,protocol = "responses" )as app : 45session = app .session () 46app .prompt (session ,"show a valid image" ).close () 47app .idle (session ) 48output = tool_output (provider ) 49self .assertIn ("attached image/png" ,json .dumps (output )) 50request = provider .requests [- 1 ]["request" ] 51self .assertIn (PNG ,json .dumps (request )) 52 53second = app .session () 54app .prompt (second ,"show an invalid image" ).close () 55app .idle (second ) 56output = tool_output (provider ) 57self .assertIn ("image_errors" ,json .dumps (output )) 58self .assertNotIn (PNG ,json .dumps (output )) 59finally : 60provider .close () 61 62# exclusive: restarts the daemon 63@exclusive 64def test_top_level_and_nested_images_survive_restart (self ): 65code = "" 66 67def reply (request ): 68if request ["input" ][- 1 ].get ("type" )== "function_call_output" : 69return text ("done" ) 70return python (code )if code else text ("history restored" ) 71 72provider = Provider (reply ) 73try : 74with Albedo (provider ,protocol = "responses" )as app : 75session = app .session () 76 77def run (source ): 78nonlocal code 79code = source 80app .prompt (session ,"run image scenario" ).close () 81app .idle (session ) 82result = next ( 83json .loads (part ["value" ]) 84for entry in reversed (app .history (session )["items" ]) 85if entry ["kind" ]== "tool_result" 86and entry ["tool" ]["name" ]== "python" 87for part in entry ["content" ] 88if part ["kind" ]== "json" and part ["field" ]== "result" 89 ) 90self .assertEqual (result ["status" ],"ok" ,result ) 91return result 92 93first = run (f"show_image(__import__('base64').b64decode( { SHOWN !r } ))" ) 94run (f"await cells.run( { first [ 'cell_id' ]!r } , allow_partial=True)" ) 95attachment = "data:image/png;base64," + SHOWN 96self .assertEqual ( 97json .dumps (provider .requests [- 1 ]["request" ]).count (attachment ),2 98 ) 99code = "" 100app .restart () 101app .prompt (session ,"recall delivered images" ).close () 102app .idle (session ) 103self .assertEqual ( 104json .dumps (provider .requests [- 1 ]["request" ]).count (attachment ),2 105 ) 106finally : 107provider .close () 108 109def test_compaction_elides_evicted_tool_images_only (self ): 110# Unique hashes keep global image reclamation assertions fixture-local. 111suffix = uuid .uuid4 ().bytes 112shown_image = base64 .b64encode (base64 .b64decode (SHOWN )+ suffix ).decode () 113uploaded_image = base64 .b64encode (base64 .b64decode (UPLOADED )+ suffix ).decode () 114 115def reply (request ): 116inputs = request ["input" ] 117user = next ( 118 ( 119str (item .get ("content" ,"" )) 120for item in reversed (inputs ) 121if item .get ("role" )== "user" 122 ), 123"" , 124 ) 125if "<newly-evicted-history>" in user : 126return text ("older conversation summary" ) 127if inputs [- 1 ].get ("type" )!= "function_call_output" and user == "show" : 128return python ( 129"show_image(__import__('base64').b64decode('" + shown_image + "'))" 130 ) 131return text ("done" ) 132 133provider = Provider (reply ) 134try : 135with Albedo (provider ,protocol = "responses" )as app : 136shown = hashlib .sha256 (shown_image .encode ()).hexdigest () 137uploaded = hashlib .sha256 (uploaded_image .encode ()).hexdigest () 138database = f"file: { app . home / 'albedo.sqlite' } ?mode=ro" 139 140def converse (sid ,prompts ): 141for prompt in prompts : 142app .prompt (sid ,prompt ).close () 143app .idle (sid ) 144with app .api (f"/sessions/ { sid } /compaction" , {})as response : 145self .assertEqual (json .load (response )["state" ],"compacted" ) 146app .idle (sid ) 147 148def rows (sid ,table ,needle ): 149with sqlite3 .connect (database ,uri = True )as db : 150return db .execute ( 151f"SELECT count(*) FROM { table } WHERE session=? " 152"AND instr(payload, CAST(? AS BLOB))>0" , 153 (sid ,needle ), 154 ).fetchone ()[0 ] 155 156old = app .session () 157app .api ( 158f"/sessions/ { old } /inputs/ { operation_id () } " , 159 { 160"kind" :"message" , 161"text" :"keep this upload" , 162"images" : [ 163 { 164"mime_type" :"image/png" , 165"data" :uploaded_image , 166 } 167 ], 168 }, 169method = "PUT" , 170 ).close () 171app .idle (old ) 172converse (old , ["show" ,"second" ,"third" ,"fourth" ]) 173self .assertEqual (rows (old ,"transcript" ,shown ),0 ) 174self .assertEqual (rows (old ,"cells" ,shown ),0 ) 175self .assertEqual (rows (old ,"transcript" ,MARKER ),1 ) 176self .assertEqual (rows (old ,"cells" ,MARKER ),1 ) 177self .assertEqual (rows (old ,"transcript" ,uploaded ),1 ) 178with sqlite3 .connect (database ,uri = True )as db : 179stored = {row [0 ]for row in db .execute ("SELECT hash FROM images" )} 180self .assertNotIn (shown ,stored ) 181self .assertIn (uploaded ,stored ) 182app .prompt (old ,"after compaction" ).close () 183app .idle (old ) 184self .assertIn ( 185"older conversation summary" , 186json .dumps (provider .requests [- 1 ]["request" ]["input" ]), 187 ) 188 189recent = app .session () 190converse (recent , ["first" ,"second" ,"third" ,"show" ]) 191self .assertEqual (rows (recent ,"transcript" ,shown ),1 ) 192self .assertEqual (rows (recent ,"cells" ,shown ),1 ) 193self .assertEqual (rows (recent ,"cells" ,shown_image ),0 ) 194finally : 195provider .close () 196 197# exclusive: changes global rolling settings and restarts the daemon 198@exclusive 199def test_automatic_compaction_elides_evicted_tool_images (self ): 200fail_summary = True 201attempts = [] 202cleaned_before_request = [] 203 204def reply (request ): 205inputs = request ["input" ] 206user = next ( 207 ( 208str (item .get ("content" ,"" )) 209for item in reversed (inputs ) 210if item .get ("role" )== "user" 211 ), 212"" , 213 ) 214if "<newly-evicted-history>" in user : 215attempts .append (fail_summary ) 216if fail_summary : 217return error (400 ,"summary refused" ) 218return text ("older conversation summary" ) 219if "[older conversation summary;" in json .dumps (inputs ): 220cleaned_before_request .append ( 221 (rows ("transcript" ,MARKER ),rows ("cells" ,MARKER )) 222 ) 223return error (400 ,"provider refused after committed compaction" ) 224if inputs [- 1 ].get ("type" )!= "function_call_output" and user == "show" : 225return python ( 226"show_image(__import__('base64').b64decode('" + SHOWN + "'))" 227 ) 228return text ("done" ) 229 230provider = Provider (reply ) 231try : 232with Albedo (provider ,protocol = "responses" )as app : 233settings_path = app .home / "extensions.json" 234original = settings_path .read_text () 235 236def restore (): 237settings_path .write_text (original ) 238app .restart () 239 240self .addCleanup (restore ) 241settings = json .loads (original ) 242settings ["rolling" ]= {"contextWindowTokens" :50000 } 243settings_path .write_text (json .dumps (settings )) 244app .restart () 245session = app .session () 246shown = hashlib .sha256 (SHOWN .encode ()).hexdigest () 247database = f"file: { app . home / 'albedo.sqlite' } ?mode=ro" 248 249def rows (table ,needle ): 250with sqlite3 .connect (database ,uri = True )as db : 251return db .execute ( 252f"SELECT count(*) FROM { table } WHERE session=? " 253"AND instr(payload, CAST(? AS BLOB))>0" , 254 (session ,needle ), 255 ).fetchone ()[0 ] 256 257app .prompt (session ,"show" ).close () 258app .idle (session ) 259self .assertEqual (rows ("transcript" ,shown ),1 ) 260self .assertEqual (rows ("cells" ,shown ),1 ) 261pad = "history " * 8000 262for prompt in (pad ,pad ,pad ): 263app .prompt (session ,prompt ).close () 264app .idle (session ) 265self .assertTrue ( 266attempts ,"the automatic trigger must attempt compaction" 267 ) 268self .assertEqual (rows ("transcript" ,shown ),1 ) 269self .assertEqual (rows ("cells" ,shown ),1 ) 270fail_summary = False 271app .prompt (session ,"retry compaction" ).close () 272app .idle (session ) 273self .assertTrue ( 274cleaned_before_request ,"expected a post-compaction request" 275 ) 276self .assertTrue (all (pair == (1 ,1 )for pair in cleaned_before_request )) 277self .assertEqual (rows ("transcript" ,shown ),0 ) 278self .assertEqual (rows ("cells" ,shown ),0 ) 279self .assertEqual (rows ("transcript" ,MARKER ),1 ) 280self .assertEqual (rows ("cells" ,MARKER ),1 ) 281summaries = len (attempts ) 282app .prompt (session ,"after compaction" ).close () 283app .idle (session ) 284self .assertEqual ( 285len (attempts ),summaries ,"reuse the committed summary" 286 ) 287self .assertIn ( 288"older conversation summary" , 289json .dumps (provider .requests [- 1 ]["request" ]["input" ]), 290 ) 291finally : 292provider .close ()