char-slop/berry
multipurpose self-modifiable agentic harness
git clone https://git.t4t.associates/char-slop/berry
1c0a335
main
1import { assert , assertEquals , assertStringIncludes } from "@std/assert" ; 2import { BACKGROUND_CONTEXT as context } from "@earendil-works/chord/context" ; 3import { createAssistantMessageEventStream } from "@earendil-works/pi-ai" ; 4import { fauxAssistantMessage , fauxProvider } from "@earendil-works/pi-ai/providers/faux" ; 5import { createModels } from "@earendil-works/pi-ai/models" ; 6import { MemoryStorage } from "@earendil-works/pi-durable" ; 7import { openBerry } from "./berry.ts" ; 8import { setup , turn } from "./fixture.ts" ; 9 10Deno . test ( "a connection body read failure retries generation" , async () => { 11await usingharness = await setup (); 12const { berry, faux, model} = harness ; 13const tid = await berry . create ({ extensions :{}, model}); 14faux . setResponses ([ 15fauxAssistantMessage ( "" , { 16stopReason :"error" , 17errorMessage :"error reading a body from connection" , 18}), 19fauxAssistantMessage ( "recovered" ), 20]); 21const conversation = ( await berry . conversation ( tid )) ! ; 22const settled = await ( await conversation . submit ({ type :"input" , content :"go" }, context )) 23. wait ( context ); 24await conversation . waitForIdle ( context ); 25assertEquals ( settled . status , "done" ); 26assertEquals ( faux . state . callCount , 2 ); 27const stored = await conversation . context ( context ); 28assertEquals ( stored . messages . at ( - 1 )?. content , [{ type :"text" , text :"recovered" }]); 29}); 30 31for ( const delivery of [ "a setup exception" , "a result-only stream" ] as const ) { 32Deno . test ( `generation retries EOF delivered through ${ delivery } ` , async () => { 33const faux = fauxProvider (); 34const models = createModels (); 35models . setProvider ( faux . provider ); 36const original = models . streamSimple . bind ( models ); 37let attempts = 0 ; 38models . streamSimple = ( ...args ) => { 39if ( ++ attempts === 1 ) { 40if ( delivery === "a setup exception" ) throw new Error ( "Unexpected EOF" ); 41const stream = createAssistantMessageEventStream (); 42stream . end ( 43fauxAssistantMessage ( "" , { stopReason :"error" , errorMessage :"Unexpected EOF" }), 44); 45return stream ; 46} 47return original ( ...args ); 48}; 49const berry = await openBerry ( new MemoryStorage (), models ); 50try { 51const model = { provider :"faux" , modelId :faux . getModel (). id }; 52await berry . enableModels ([ model ]); 53const tid = await berry . create ({ extensions :{}, model}); 54faux . setResponses ([ fauxAssistantMessage ( "recovered" )]); 55const conversation = ( await berry . conversation ( tid )) ! ; 56const settled = await ( await conversation . submit ({ type :"input" , content :"go" }, context )) 57. wait ( context ); 58assertEquals ( settled . status , "done" ); 59assertEquals ( attempts , 2 ); 60} finally { 61await berry . close (); 62} 63}); 64} 65 66Deno . test ( "compaction also retries EOF and places the recovered summary" , async () => { 67await usingharness = await setup (); 68const { berry, faux, model} = harness ; 69const tid = await berry . create ({ extensions :{}, model}); 70await turn ( harness , tid , []); 71const conversation = ( await berry . conversation ( tid )) ! ; 72faux . setResponses ([ fauxAssistantMessage ( "done" )]); 73await ( await conversation . submit ({ type :"input" , content :"x" . repeat ( 100_000 ) }, context )) 74. wait ( context ); 75await conversation . waitForIdle ( context ); 76faux . setResponses ([ 77fauxAssistantMessage ( "" , { stopReason :"error" , errorMessage :"Unexpected EOF" }), 78fauxAssistantMessage ( "Recovered summary" ), 79]); 80const task = await conversation . compact ( undefined , context ); 81const { outcome} = ( await berry . harness . waitForTask ( task , context )). state ; 82assert ( outcome . status === "completed" ); 83await conversation . waitForIdle ( context ); 84assertEquals ( faux . state . callCount , 4 ); 85const entries = 86( await conversation . entries ({ order :"ascending" }, 100 , undefined , context )). items ; 87const summary = entries . find (( entry ) => entry . kind === "pi.compaction" ); 88assert ( summary ); 89const text = JSON . stringify ( summary . model ); 90assertStringIncludes ( text , "Recovered summary" ); 91});