okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
61c43ca
main
1"""Finalized traces survive buffer release, nested evaluation, and late edits.""" 2 3import json 4import unittest 5import urllib .parse 6 7from harness import Albedo ,Provider ,python ,text 8 9 10class TracesTests (unittest .TestCase ): 11def setUp (self ): 12self .code = "" 13self .provider = Provider ( 14lambda request : ( 15python (self .code ) 16if request ["messages" ][- 1 ].get ("role" )== "user" 17else text ("done" ) 18 ) 19 ) 20self .addCleanup (self .provider .close ) 21self .app = Albedo (self .provider ) 22self .app .__enter__ () 23self .addCleanup (self .app .__exit__ ,None ,None ,None ) 24self .session = self .app .session () 25 26def run_cell (self ,code ,status = "ok" ): 27self .code = code 28self .app .prompt (self .session ,"execute this cell" ).close () 29self .app .idle (self .session ) 30entry = [ 31entry 32for entry in self .app .history (self .session )["items" ] 33if entry ["kind" ]== "tool_result" and entry ["tool" ]["name" ]== "python" 34 ][- 1 ] 35 36def referenced_json (field ): 37 [reference ]= [ 38part ["reference" ] 39for part in entry ["content" ] 40if part ["kind" ]== "reference" and part ["reference" ]["field" ]== field 41 ] 42pieces ,offset ,token = [],0 ,None 43for _ in range (100 ): 44path = reference ["url" ] 45if token is not None : 46path += "?" + urllib .parse .urlencode ({"next" :token }) 47with self .app .api (path )as response : 48page = json .load (response ) 49for chunk in page ["parts" ]: 50if chunk ["field" ]!= field : 51continue 52self .assertEqual (chunk ["offset_bytes" ],offset ) 53self .assertEqual (chunk ["encoding" ],"utf8" ) 54pieces .append (chunk ["text" ]) 55offset += len (chunk ["text" ].encode ()) 56token = page ["next" ] 57if token is None : 58break 59else : 60self .fail (f" { field } content pagination did not terminate" ) 61self .assertEqual (offset ,reference ["bytes" ]) 62return json .loads ("" .join (pieces )) 63 64results = [ 65json .loads (part ["value" ]) 66for part in entry ["content" ] 67if part ["kind" ]== "json" and part ["field" ]== "result" 68 ] 69result = results [0 ]if results else json .loads (referenced_json ("result" )) 70traces = [part ["trace" ]for part in entry ["content" ]if part ["kind" ]== "trace" ] 71trace = traces [0 ]if traces else referenced_json ("trace" ) 72self .assertEqual (result ["status" ],status ,result ) 73return result ,trace 74 75def read_trace (self ,id ): 76result ,_ = self .run_cell ( 77f"import json\nprint(json.dumps(await cells.trace( { id !r } )))" 78 ) 79return json .loads (result ["output" ]) 80 81def test_top_level_and_nested_traces_preserve_changes_for_all_outcomes (self ): 82for status ,ending in ( 83 ("ok" ,"" ), 84 ("error" ,"raise ValueError('failed')" ), 85 ("interrupted" ,"raise __import__('asyncio').CancelledError()" ), 86 ): 87with self .subTest (status = status ): 88 (self .app .workspace / "note.txt" ).write_text ("before\n" ) 89 (self .app .workspace / "binary.dat" ).unlink (missing_ok = True ) 90source = ( 91""" 92import os 93open('temporary.txt', 'w').write('after\\n') 94os.replace('temporary.txt', 'note.txt') 95open('note.txt').read() 96open('binary.dat', 'wb').write(b'\\0binary') 97""" 98+ ending 99 ) 100original ,trace = self .run_cell (source ,status ) 101self .assertEqual (trace ,self .read_trace (original ["cell_id" ])) 102self .assert_changes (trace ) 103# The exact saved source runs as a separate nested cell. 104 (self .app .workspace / "note.txt" ).write_text ("before\n" ) 105 (self .app .workspace / "binary.dat" ).unlink () 106parent ,parent_trace = self .run_cell ( 107f"try:\n await cells.run( { original [ 'cell_id' ]!r } , allow_partial=True)\n" 108"except BaseException:\n pass\nprint(cells.last_id)" 109 ) 110child = parent ["output" ].strip ().splitlines ()[- 1 ] 111nested = self .read_trace (child ) 112self .assert_changes (nested ) 113self .run_cell ( 114f"assert (await cells.info( { child !r } ))['status'] == { status !r } " 115 ) 116self .assertEqual (parent_trace ["changes" ], []) 117 118def assert_changes (self ,trace ): 119changes = {change ["path" ].split ("/" )[- 1 ]:change for change in trace ["changes" ]} 120self .assertNotIn ("temporary.txt" ,changes ) 121self .assertIn ("-before" ,changes ["note.txt" ]["diff" ]) 122self .assertIn ("+after" ,changes ["note.txt" ]["diff" ]) 123self .assertEqual (changes ["binary.dat" ]["kind" ],"unavailable" ) 124self .assertTrue ( 125any ( 126activity ["kind" ]== "read" and activity ["target" ].endswith ("note.txt" ) 127for activity in trace ["activities" ] 128 ) 129 ) 130 131def test_many_edit_heavy_cells_release_buffers_and_keep_old_traces (self ): 132oldest = None 133oldest_trace = None 134for index in range (18 ): 135result ,trace = self .run_cell ( 136f"for index in range(20):\n open(f'edit-{{index}}.txt', 'w').write( { str ( index )!r } )\n" 137"open('edit-0.txt').read()" 138 ) 139self .assertTrue (trace ["truncated" ]) 140self .assertTrue (trace ["changes" ]) 141if oldest is None : 142oldest ,oldest_trace = result ["cell_id" ],trace 143result ,_ = self .run_cell (""" 144import __main__ as kernel 145completed = [capture for capture in kernel.ARCHIVES.values() if capture.trace.sealed] 146assert completed 147assert all(not capture.trace.before and not capture.trace.activities for capture in completed) 148print('released') 149""" ) 150self .assertIn ("released" ,result ["output" ]) 151self .assertEqual (self .read_trace (oldest ),oldest_trace ) 152 153def test_late_background_edits_execute_without_changing_finalized_trace (self ): 154result ,trace = self .run_cell (""" 155import asyncio 156import __main__ as kernel 157late_gate = asyncio.Event() 158async def late_edit(): 159await late_gate.wait() 160open('late.txt', 'w').write('late\\n') 161open('late.txt').read() 162late_task = asyncio.create_task(late_edit()) 163open('early.txt', 'w').write('early\\n') 164""" ) 165self .run_cell (f""" 166late_gate.set() 167await late_task 168capture = kernel.ARCHIVES[ { result [ "cell_id" ]!r } ] 169assert capture.trace.sealed 170assert not capture.trace.before and not capture.trace.activities 171""" ) 172self .assertEqual ((self .app .workspace / "late.txt" ).read_text (),"late\n" ) 173self .assertEqual (self .read_trace (result ["cell_id" ]),trace )