okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
c6501f5
main
1//// A request pairs every tool_use with its tool_result: a late result moves 2//// back beside its call, a call no result answers gets `unpaired_result`, 3//// and a result whose call the fold dropped stays out of the request. Turn 4//// starts recover pending calls before any request is built, so no e2e 5//// request reaches the builder with the inputs that broke pairing here; the 6//// fold damage this repairs is only visible at the request boundary. 7 8import albedo/daemon/message_content 9import albedo/daemon/tool_results 10import albedo/openai_api/replay 11import albedo/openai_api/types 12import gleam/json 13import gleam/list 14import gleam/option .{ None } 15import gleam/string 16import gleeunit/should 17 18fn calls ( ids : List ( String )) -> types . Input { 19let assert Ok ( item ) = 20json . parse ( 21json . to_string ( replay . message ( 22json . string ( "" ), 23"" , 24None , 25list . map ( ids , fn ( id ) { types . ToolCall ( id , "python" , "{}" ) }), 26)), 27types . replay_decoder ( types . ChatCompletions ), 28) 29types . Replay ( item ) 30} 31 32fn note ( text : String ) -> types . Input { 33types . User ( text ) 34} 35 36/// Each input as what a request sees it as: the calls a provider item makes, 37/// a tool result's id, or a plain row. 38fn shape ( inputs : List ( types . Input )) -> List (#( String , String )) { 39list . map ( inputs , fn ( input ) { 40case input { 41types . ToolOutput ( id , _ , _ ) -> #( "result" , id ) 42types . Replay ( _ ) -> { 43let ids = 44message_content . calls ( input ) 45|> list . map ( fn ( call ) { call . id }) 46|> string . join ( "," ) 47#( "calls" , ids ) 48} 49types . User ( text ) -> #( "user" , text ) 50types . UserImage ( text , _ ) -> #( "user" , text ) 51types . Assistant ( text ) -> #( "assistant" , text ) 52} 53}) 54} 55 56pub fn a_call_nothing_answers_gets_a_result_test () -> Nil { 57let inputs = [ calls ([ "toolu_a" ]), note ( "a note saved while the call ran" )] 58shape ( tool_results . pair ( inputs )) 59|> should . equal ([ 60#( "calls" , "toolu_a" ), 61#( "result" , "toolu_a" ), 62#( "user" , "a note saved while the call ran" ), 63]) 64let assert [ _ , types . ToolOutput ( _ , text , []), _ ] = tool_results . pair ( inputs ) 65text |> should . equal ( tool_results . unpaired_result ) 66} 67 68pub fn a_result_separated_from_its_call_moves_beside_it_test () -> Nil { 69let inputs = [ 70calls ([ "toolu_a" ]), 71note ( "a note saved while the call ran" ), 72types . ToolOutput ( "toolu_a" , "ran" , []), 73] 74shape ( tool_results . pair ( inputs )) 75|> should . equal ([ 76#( "calls" , "toolu_a" ), 77#( "result" , "toolu_a" ), 78#( "user" , "a note saved while the call ran" ), 79]) 80} 81 82pub fn a_result_the_fold_dropped_leaves_its_call_a_placeholder_test () -> Nil { 83let inputs = [ 84calls ([ "toolu_a" , "toolu_b" ]), 85types . ToolOutput ( "toolu_a" , "ran" , []), 86note ( "later" ), 87] 88shape ( tool_results . pair ( inputs )) 89|> should . equal ([ 90#( "calls" , "toolu_a,toolu_b" ), 91#( "result" , "toolu_a" ), 92#( "result" , "toolu_b" ), 93#( "user" , "later" ), 94]) 95} 96 97pub fn a_result_whose_call_the_fold_dropped_stays_out_test () -> Nil { 98let inputs = [ 99note ( "the call was replaced by a summary" ), 100types . ToolOutput ( "toolu_gone" , "ran" , []), 101] 102shape ( tool_results . pair ( inputs )) 103|> should . equal ([#( "user" , "the call was replaced by a summary" )]) 104} 105 106pub fn pairing_is_idempotent_test () -> Nil { 107let inputs = [ 108calls ([ "toolu_a" , "toolu_b" ]), 109note ( "between" ), 110types . ToolOutput ( "toolu_b" , "ran" , []), 111types . ToolOutput ( "toolu_b" , "ran twice" , []), 112] 113let paired = tool_results . pair ( inputs ) 114tool_results . pair ( paired ) |> should . equal ( paired ) 115shape ( paired ) 116|> should . equal ([ 117#( "calls" , "toolu_a,toolu_b" ), 118#( "result" , "toolu_b" ), 119#( "result" , "toolu_a" ), 120#( "user" , "between" ), 121]) 122}