okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
12f1695
main
1//// Run explicitly with: gleam run -m manual/stream_rate_benchmark 2//// Streams from test/manual/mock_openai_server.py (start it first; its origin 3//// from ALBEDO_BENCH_ORIGIN, default http://127.0.0.1:8765, and for https the 4//// CA to trust from ALBEDO_BENCH_CA) through the real transport and reducers, 5//// and prints VM CPU and reductions per token and send-to-callback latency. 6//// Run with the daemon's ERL_FLAGS (the vm_defaults in 7//// priv/bin/albedo-daemon: two schedulers, no busy waiting), so scheduler 8//// spinning does not count as work. 9//// 10//// By default each protocol streams 2000 deltas at 500 per second, then 11//// 20000 unthrottled (ALBEDO_BENCH_UNTHROTTLED=0 skips those). 12//// ALBEDO_BENCH_PARALLEL=N instead streams N at once, each at 13//// ALBEDO_BENCH_RATE (default 1000) per second for four seconds. 14//// ALBEDO_BENCH_PROTOCOL=responses|chat_completions|claude|vertex|antigravity 15//// runs one protocol, ALBEDO_BENCH_TOOLS=P streams P percent of the deltas as tool 16//// call arguments, and ALBEDO_BENCH_MSACC=1 prints microstate accounting. 17 18import albedo/harness/extensions/antigravity/catalog 19import albedo/harness/extensions/antigravity/stream as antigravity 20import albedo/harness/extensions/claude/stream as claude 21import albedo/harness/extensions/vertex/stream as vertex 22import albedo/openai_api as openai 23import albedo/openai_api/types 24import gleam/float 25import gleam/int 26import gleam/io 27import gleam/json 28import gleam/list 29import gleam/option .{ None } 30import gleam/result 31import gleam/string 32import gleam/string_tree 33 34@external ( erlang , "albedo_openai_rate_bench" , "env" ) 35fn env ( name : String , default : String ) -> String 36 37@external ( erlang , "albedo_openai_rate_bench" , "token" ) 38fn token () -> Nil 39 40@external ( erlang , "albedo_openai_rate_bench" , "trust" ) 41fn trust ( ca_file : String ) -> Nil 42 43@external ( erlang , "albedo_openai_rate_bench" , "measure" ) 44fn measure ( 45origin : String , 46runs : List (#( String , fn () -> Result ( types . Turn , types . Error ))), 47) -> #( List ( Result ( types . Turn , types . Error )), List (#( String , Float ))) 48 49const runs = 3 50 51type Protocol { 52OpenAI ( types . Protocol ) 53Claude 54Vertex 55Antigravity 56} 57 58pub fn main () -> Nil { 59let origin = env ( "ALBEDO_BENCH_ORIGIN" , "http://127.0.0.1:8765" ) 60trust ( env ( "ALBEDO_BENCH_CA" , "" )) 61let only = env ( "ALBEDO_BENCH_PROTOCOL" , "" ) 62let assert Ok ( tools ) = int . parse ( env ( "ALBEDO_BENCH_TOOLS" , "0" )) 63let assert Ok ( parallel ) = int . parse ( env ( "ALBEDO_BENCH_PARALLEL" , "0" )) 64let assert Ok ( rate ) = int . parse ( env ( "ALBEDO_BENCH_RATE" , "1000" )) 65let unthrottled = env ( "ALBEDO_BENCH_UNTHROTTLED" , "1" ) == "1" 66[ 67OpenAI ( types . Responses ), 68OpenAI ( types . ChatCompletions ), 69Claude , 70Vertex , 71Antigravity , 72] 73|> list . filter ( fn ( protocol ) { only == "" || only == name ( protocol ) }) 74|> list . each ( fn ( protocol ) { 75case parallel , unthrottled { 760 , True -> { 77bench ( origin , protocol , tools , 1 , 500 , 2000 ) 78bench ( origin , protocol , tools , 1 , 0 , 20_000 ) 79} 800 , False -> bench ( origin , protocol , tools , 1 , 500 , 2000 ) 81streams , _ -> bench ( origin , protocol , tools , streams , rate , rate * 4 ) 82} 83}) 84} 85 86fn name ( protocol : Protocol ) -> String { 87case protocol { 88OpenAI ( protocol ) -> types . protocol_name ( protocol ) 89Claude -> "claude" 90Vertex -> "vertex" 91Antigravity -> "antigravity" 92} 93} 94 95fn bench ( 96origin : String , 97protocol : Protocol , 98tools : Int , 99streams : Int , 100rate : Int , 101tokens : Int , 102) -> Nil { 103let runs_once = 104int . range ( 0 , streams , [], list . prepend ) 105|> list . map ( fn ( i ) { 106let id = int . to_string ( i ) 107let base = 108string . join ( 109[ 110origin , 111int . to_string ( rate ), 112int . to_string ( tokens ), 113"t" <> int . to_string ( tools ), 114"s" <> id , 115"v1" , 116], 117"/" , 118) 119#( id , fn () { stream ( protocol , base ) }) 120}) 121// one warm-up run loads modules and opens the connections the runs reuse 122let _ = measure ( origin , runs_once ) 123let results = 124list . repeat ( Nil , runs ) |> list . map ( fn ( _ ) { measure ( origin , runs_once ) }) 125let label = 126name ( protocol ) 127<> case tools { 1280 -> "" 129_ -> " (" <> int . to_string ( tools ) <> "% tool args)" 130} 131<> case streams { 1321 -> "" 133_ -> " " <> int . to_string ( streams ) <> " streams" 134} 135<> " @ " 136<> case rate { 1370 -> "unthrottled" 138_ -> int . to_string ( rate ) <> " tok/s" 139} 140let failure = 141list . flat_map ( results , fn ( run ) { run . 0 }) |> list . find ( result . is_error ) 142case failure { 143Ok ( Error ( error )) -> io . println ( label <> ": " <> string . inspect ( error )) 144_ -> io . println ( label <> " " <> median ( list . map ( results , fn ( r ) { r . 1 }))) 145} 146} 147 148fn stream ( protocol : Protocol , base : String ) -> Result ( types . Turn , types . Error ) { 149let on_event = fn ( event ) { 150case event { 151types . ArgumentsDelta ( _ , _ , "" ) -> Nil 152types . TextDelta (..) |types . ArgumentsDelta (..) -> token () 153_ -> Nil 154} 155types . Continue 156} 157case protocol { 158OpenAI ( protocol ) -> 159openai . stream ( 160openai . client ( protocol , base , "" ), 161openai . request ( "mock" , [ types . User ( "go" )]), 162on_event , 163) 164Claude -> 165openai . exchange ( 166exchange ( base <> "/messages" ), 167claude . reducer ( "mock" , []), 168on_event , 169) 170Vertex -> 171openai . exchange ( exchange ( base <> "/vertex" ), vertex . reducer (), on_event ) 172Antigravity -> { 173let model = catalog . model ( "/nonexistent" , "gemini-3-pro" , None ) 174openai . exchange ( 175exchange ( base <> "/antigravity" ), 176antigravity . reducer ( model ), 177on_event , 178) 179} 180} 181} 182 183fn exchange ( url : String ) -> openai . Exchange { 184openai . Exchange ( 185url , 186[#( "content-type" , "application/json" )], 187string_tree . from_string ( "{}" ), 18860_000 , 1898 * 1024 * 1024 , 190require_event_stream : True , 191) 192} 193 194/// Each metric's median across runs, as one JSON object. 195fn median ( runs : List ( List (#( String , Float )))) -> String { 196let assert [ first , ..] = runs 197list . map ( first , fn ( metric ) { 198let values = 199list . filter_map ( runs , fn ( run ) { list . key_find ( run , metric . 0 ) }) 200|> list . sort ( float . compare ) 201let assert Ok ( middle ) = 202list . drop ( values , list . length ( values ) / 2 ) |> list . first 203#( metric . 0 , json . float ( float . to_precision ( middle , 2 ))) 204}) 205|> json . object 206|> json . to_string 207}