okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
5f9679f
main
1//// Opt-in live integration: gleam run -m manual/coding 2//// Uses ALBEDO_BENCH_URL/MODEL/KEY and ALBEDO_LIVE_WORKSPACE/DATABASE/PROTOCOL. 3//// No credentials are retained in the kernel environment or output. 4 5import albedo/harness/extensions/work/ledger as work 6import albedo/harness/runtime 7import albedo/openai_api as openai 8import albedo/openai_api/types 9import gleam/io 10import gleam/json 11import gleam/list 12import gleam/option .{ Some } 13import gleam/result 14import gleam/string 15 16@external ( erlang , "albedo_openai_bench" , "env" ) 17fn env ( name : String ) -> String 18 19@external ( erlang , "albedo_live_coding" , "take_key" ) 20fn take_key () -> String 21 22@external ( erlang , "albedo_live_coding" , "now" ) 23fn now () -> Int 24 25@external ( erlang , "albedo_live_coding" , "with_runtime" ) 26fn with_runtime ( host : runtime . Runtime , run : fn () -> a) -> a 27 28pub fn main () -> Nil { 29let key = take_key () 30let workspace = env ( "ALBEDO_LIVE_WORKSPACE" ) 31case 32key == "" 33|| workspace == "" 34|| env ( "ALBEDO_BENCH_URL" ) == "" 35|| env ( "ALBEDO_BENCH_MODEL" ) == "" 36|| env ( "ALBEDO_LIVE_DATABASE" ) == "" 37{ 38True -> io . println ( "missing live-test environment; no requests made" ) 39False -> { 40let protocol = case env ( "ALBEDO_LIVE_PROTOCOL" ) { 41"chat_completions" -> types . ChatCompletions 42_ -> types . Responses 43} 44let client = 45types . Client ( 46.. openai . client ( protocol , env ( "ALBEDO_BENCH_URL" ), key ), 47timeout_ms : 90_000 , 48) 49let assert Ok ( host ) = runtime . start ( env ( "ALBEDO_LIVE_DATABASE" )) 50use <- with_runtime ( host ) 51let assert Ok ( session ) = runtime . open_session ( host , "coding" , workspace ) 52let request = 53types . Request ( 54.. openai . request ( env ( "ALBEDO_BENCH_MODEL" ), [ types . User ( task )]), 55instructions : Some ( instructions ), 56tools : runtime . tools ( session ), 57max_output_tokens : Some ( 4096 ), 58) 59let start = now () 60let result = run ( client , request , host , session , 1 , 0 ) 61let ledger = 62work . list ( runtime . ledger ( host ), workspace , 0 , 50 ) |> result . unwrap ([]) 63log ([ 64#( "event" , json . string ( "summary" )), 65#( "elapsed_ms" , json . int ( now () - start )), 66#( 67"result" , 68json . string ( 69string . inspect ( result ) |> string . replace ( key , "[redacted]" ), 70), 71), 72#( "ledger" , json . array ( ledger , work . to_json )), 73]) 74let assert Ok ( _ ) = result 75Nil 76} 77} 78} 79 80fn run ( 81client : types . Client , 82request : types . Request , 83host : runtime . Runtime , 84session : runtime . Session , 85step : Int , 86tools : Int , 87) -> Result (#( Int , Int ), String ) { 88case step > 20 || tools > 30 { 89True -> Error ( "live test turn/tool limit reached" ) 90False -> { 91let start = now () 92use turn <- result . try ( 93openai . stream ( client , request , fn ( _ ) { types . Continue }) 94|> result . map_error ( fn ( error ) { 95string . inspect ( error ) |> string . replace ( client . api_key , "[redacted]" ) 96}), 97) 98log ([ 99#( "event" , json . string ( "model_turn" )), 100#( "step" , json . int ( step )), 101#( "elapsed_ms" , json . int ( now () - start )), 102#( "calls" , json . int ( list . length ( turn . tool_calls ))), 103#( "finish" , json . string ( string . inspect ( turn . finish ))), 104#( "usage" , json . string ( string . inspect ( turn . usage ))), 105]) 106case turn . tool_calls { 107[] -> { 108log ([ 109#( "event" , json . string ( "final" )), 110#( "output" , json . array ( turn . output , types . replay_json )), 111]) 112case turn . finish { 113types . Complete -> Ok (#( step , tools )) 114_ -> Error ( "model stopped without completion" ) 115} 116} 117calls -> { 118use outputs <- result . try ( 119list . try_map ( calls , fn ( call ) { 120log ([ 121#( "event" , json . string ( "tool_call" )), 122#( "id" , json . string ( call . id )), 123#( "arguments" , json . string ( call . arguments )), 124]) 125use output <- result . try ( runtime . invoke ( 126host , 127session , 128call , 129types . any_images , 130)) 131case output { 132types . ToolOutput ( _ , text , _ ) -> 133log ([ 134#( "event" , json . string ( "tool_result" )), 135#( "body" , json . string ( text )), 136]) 137_ -> Nil 138} 139Ok ( output ) 140}), 141) 142let input = 143list . append ( 144request . input , 145list . append ( list . map ( turn . output , types . Replay ), outputs ), 146) 147run ( 148client , 149types . Request (.. request , input : input ), 150host , 151session , 152step + 1 , 153tools + list . length ( calls ), 154) 155} 156} 157} 158} 159} 160 161fn log ( fields : List (#( String , json . Json ))) -> Nil { 162fields |> json . object |> json . to_string |> io . println 163} 164 165const instructions = 166"You are testing albedo by solving a real coding task. Your only tool is python. Its namespace persists across calls, and top-level await is supported. Use pathlib for files. await work.create(title, notes=...) returns a dict with id and revision; await work.update(id, revision=..., status=...) uses optimistic revisions. These methods are coroutines: always await them, including when assigning their result. run(program, *args, timeout=seconds) starts a job without a shell; await its handle and print(job.tail()) to see results. Use cells.read/cells.run if a cell fails. Work only in your current workspace. Do not inspect credentials, environment variables, other directories, or network services. Do not modify any README. You must actually create files and run tests, not merely describe code. Use at least two python calls and retain a variable between calls. Once your tests pass, update the work item to done and finish with a brief account of what passed. Do not keep adding unrelated tests after success." 167 168const task = 169"Implement interval_set.py using only the Python standard library. Public functions normalize(intervals), union(left, right), intersection(left, right), and difference(left, right) return canonical lists of (start, end) integer tuples for half-open intervals [start,end). Inputs may be unsorted, overlap, contain duplicates or empty intervals, and may be one-shot generators. Drop empty intervals; reject any reversed interval (start > end) with ValueError. Canonical output is sorted, nonempty, disjoint, and merges touching intervals. Do not mutate caller input. difference means left minus right. Do not enumerate integer points: endpoints can be arbitrarily large. Aim for sorting plus linear sweeps. Create a work item and mark it done only after tests pass. Write test_interval_set.py with edge cases and deterministic randomized checks, then execute it through bash from the python tool. Do not install dependencies. Keep the implementation readable."