okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
cba5ff8
main
1"""Real provider and session fixtures for execution, persistence, and recovery.""" 2 3import json 4import time 5import unittest 6import urllib .error 7import urllib .parse 8 9from harness import Albedo ,Provider ,operation_id ,python ,text 10 11 12def contents (item ): 13content = item .get ("content" ,"" ) 14if isinstance (content ,str ): 15return content 16return " " .join (part .get ("text" ,"" )for part in content if isinstance (part ,dict )) 17 18 19def script (request ): 20inputs = request .get ("messages" ,request .get ("input" , [])) 21user = next ( 22 (contents (item )for item in reversed (inputs )if item .get ("role" )== "user" ),"" 23 ) 24tools = sum ( 25item .get ("role" )== "tool" or item .get ("type" )== "function_call_output" 26for item in inputs 27 ) 28if "continue from branch" in user : 29return text ("finished" ) 30if "no first token" in user : 31return text ("never streamed" ,hang = True ) 32if "over 100 turns" in user : 33return text ("finished" )if tools >= 105 else python ("1" ) 34if "workspace probe" in user : 35code = ( 36"import os\nfrom pathlib import Path\n" 37"assert 'workspace_marker' not in globals()\n" 38"workspace_marker = os.getcwd()\nPath('cwd-probe').write_text(os.getcwd())" 39 ) 40done = ( 41inputs [- 1 ].get ("role" )== "tool" 42or inputs [- 1 ].get ("type" )== "function_call_output" 43 ) 44return text ("finished" )if done else python (code ) 45code = ( 46"import asyncio, os\nfrom pathlib import Path\n" 47"Path('example.txt').write_text('hello\\n')\n" 48"saved = Path('example.txt').read_text()\n" 49"assert 'ALBEDO_API_KEY' not in os.environ\n" 50"assert 'ALBEDO_TOKEN' not in os.environ\n" 51+ ( 52"Path('recovery-started').write_text('ready')\n" 53if "hang then recover" in user 54else "" 55 ) 56+ f"await asyncio.sleep( { 30 if 'hang' in user else 2 if 'first task' in user else 0.4 } )\nlen(saved)" 57 ) 58return ( 59text ("finished" ) 60if tools 61else python (code ,reasoning = "reasoning about the task" ) 62 ) 63 64 65class IntegrationScenario (unittest .TestCase ): 66def setUp (self ): 67self .provider = Provider (script ) 68self .addCleanup (self .provider .close ) 69 70def app_for (self ,protocol ,* ,prepare = None ,providers = None ): 71app = Albedo ( 72self .provider ,protocol = protocol ,prepare = prepare ,providers = providers 73 ) 74app .__enter__ () 75self .addCleanup (app .__exit__ ,None ,None ,None ) 76return app 77 78def read (self ,app ,path ): 79with app .api (path )as response : 80return json .load (response ) 81 82def change (self ,app ,session ,fields ): 83with app .api (f"/sessions/ { session } ?view=configuration" )as response : 84configuration = json .load (response ) 85etag = response .headers ["ETag" ] 86if "workspace" in fields : 87fields = {** fields ,"family_revision" :configuration ["family_revision" ]} 88with app .api ( 89f"/sessions/ { session } ?view=configuration" , 90fields , 91method = "PATCH" , 92headers = {"If-Match" :etag }, 93 )as response : 94return json .load (response ) 95 96def history (self ,app ,session ): 97page = app .history (session ) 98entries = page ["items" ] 99while page ["older" ]: 100page = self .read ( 101app , 102f"/sessions/ { session } /history?next= { urllib . parse . quote ( page [ 'older' ], safe = '' ) } " , 103 ) 104entries = page ["items" ]+ entries 105return entries 106 107def entry_text (self ,entry ): 108return "" .join ( 109part ["text" ]for part in entry ["content" ]if part ["kind" ]== "text" 110 ) 111 112def send (self ,app ,session ,message ): 113app .prompt (session ,message ).close () 114app .idle (session ,timeout = 90 ) 115 116def settings (self ,app ,protocol ,active = "alpha" ,** models ): 117other = "responses" if protocol == "chat_completions" else "chat_completions" 118configured = { 119"alpha" : ("alpha-2" ,models .get ("alpha" ,"fixture-alpha" ),protocol ), 120"beta" : ("beta-1" ,models .get ("beta" ,"fixture-beta" ),protocol ), 121"gamma" : ("gamma-1" ,models .get ("gamma" ,"fixture-gamma" ),other ), 122 } 123providers = { 124name : { 125"baseUrl" :self .provider .url + f"/ { name } /v1" , 126"apiKey" :key , 127"model" :model , 128"protocol" :binding , 129 } 130for name , (key ,model ,binding )in configured .items () 131 } 132 (app .home / "config.json" ).write_text ( 133json .dumps ({"active" :active ,"providers" :providers }) 134 ) 135 136def default_session (self ,app ): 137identity = operation_id () 138with app .api ( 139f"/sessions/ { identity } " , 140 {"kind" :"new" ,"workspace" :str (app .workspace )}, 141method = "PUT" , 142headers = {"If-None-Match" :"*" }, 143 )as response : 144self .assertEqual (json .load (response )["id" ],identity ) 145return identity 146 147def records (self ,start ): 148return self .provider .requests [start :] 149 150def wait_for_request (self ,start ): 151deadline = time .monotonic ()+ 15 152while len (self .provider .requests )<= start and time .monotonic ()< deadline : 153time .sleep (0.05 ) 154self .assertGreater (len (self .provider .requests ),start ) 155 156def assert_projected (self ,record ,protocol ,users ): 157items = record ["request" ][ 158"messages" if protocol == "chat_completions" else "input" 159 ] 160serialized = json .dumps (items ) 161for user in users : 162self .assertIn (user ,serialized ) 163self .assertIn ("finished" ,serialized ) 164if protocol == "chat_completions" : 165calls = [ 166call ["id" ] 167for item in items 168if item .get ("role" )== "assistant" 169for call in item .get ("tool_calls" , []) 170 ] 171outputs = [ 172item ["tool_call_id" ]for item in items if item .get ("role" )== "tool" 173 ] 174else : 175calls = [ 176item ["call_id" ]for item in items if item .get ("type" )== "function_call" 177 ] 178outputs = [ 179item ["call_id" ] 180for item in items 181if item .get ("type" )== "function_call_output" 182 ] 183self .assertTrue (any (call .startswith ("fixture-call-" )for call in calls )) 184self .assertTrue (set (calls )& set (outputs )) 185 186def restart (self ,app ): 187app .restart (crash = True )