baikai-effectful 0.2.0.0 → 0.3.0.0
raw patch · 5 files changed
+24/−34 lines, 5 filesdep ~baikaiPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: baikai
API changes (from Hackage documentation)
Files
- README.md +5/−5
- baikai-effectful.cabal +2/−2
- src/Baikai/Effectful.hs +6/−5
- test/LiveSpec.hs +2/−3
- test/StubProvider.hs +9/−19
README.md view
@@ -6,10 +6,10 @@ operations against a real or isolated provider registry. It is the *seam*, not the framework: it adds **no** retries, backoff, rate limiting,-budgets, caching, or error remapping. Failures propagate exactly as baikai produces them —-the blocking path throws `Baikai.Error.BaikaiError`; the streaming paths surface baikai's-terminal `EventError` event in-band. Policy belongs one layer up, written *in terms of* this-effect.+budgets, caching, or error remapping. Failures propagate exactly as baikai produces them:+blocking completions return the same error-shaped `Response` as `completeRequest`, and+streaming paths surface baikai's terminal `EventError` event in-band. Policy belongs one+layer up, written *in terms of* this effect. ## The effect @@ -24,7 +24,7 @@ | Operation | Meaning | | --------------------------------- | ------------------------------------------------------------- |-| `complete m c o` | Blocking completion → `Response`. Throws `BaikaiError`. |+| `complete m c o` | Blocking completion → `Response`, including error-shaped responses. | | `streamCollect m c o` | Drain the stream into the full `[AssistantMessageEvent]`. | | `streamEach m c o k` | Run callback `k` once per event, in order, inside `Eff`. |
baikai-effectful.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.4 name: baikai-effectful-version: 0.2.0.0+version: 0.3.0.0 synopsis: effectful binding for the baikai AI-provider transport description: A thin, policy-free effectful binding over baikai's transport. Provides the dynamic@@ -35,7 +35,7 @@ hs-source-dirs: src exposed-modules: Baikai.Effectful build-depends:- , baikai ^>=0.2.0+ , baikai ^>=0.3.0 , base >=4.20 && <5 , effectful-core , streamly >=0.11 && <0.13
src/Baikai/Effectful.hs view
@@ -9,7 +9,8 @@ -- baikai's transport functions, plus two interpreters that run those operations -- against a real (global) or isolated provider registry. It carries NO policy: -- failures propagate exactly as baikai produces them — the blocking path--- ('complete') throws 'Baikai.Error.BaikaiError'; the streaming paths+-- ('complete') returns an error-shaped 'Response' whose+-- 'Baikai.responseError' is populated; the streaming paths -- ('streamCollect', 'streamEach') surface baikai's terminal @EventError@ in-band. -- Retries, caching, budgets, rate limiting, and error remapping belong one layer -- up, in terms of this effect.@@ -46,8 +47,8 @@ -- | The provider-neutral baikai transport effect. Operations mirror baikai's -- transport functions; the interpreter selects the provider registry. data Baikai :: Effect where- -- | A blocking completion. Mirrors 'Baikai.completeRequest' — throws- -- 'Baikai.Error.BaikaiError' on failure.+ -- | A blocking completion. Mirrors 'Baikai.completeRequest' — failures+ -- are returned in-band as an error-shaped 'Response'. Complete :: Model -> Context -> Options -> Baikai m Response -- | A streaming completion materialized into the full event list. Mirrors -- 'Baikai.streamRequest' drained to a list; a terminal @EventError@ appears@@ -59,8 +60,8 @@ type instance DispatchOf Baikai = 'Dynamic --- | Issue a blocking completion. Throws baikai's 'Baikai.Error.BaikaiError' on--- failure, just like 'Baikai.completeRequest' — this binding does not catch it.+-- | Issue a blocking completion. Provider failures are returned in-band+-- as an error-shaped 'Response', just like 'Baikai.completeRequest'. complete :: (Baikai :> es) => Model -> Context -> Options -> Eff es Response complete m c o = send (Complete m c o)
test/LiveSpec.hs view
@@ -15,7 +15,6 @@ import Data.Text qualified as T import Data.Vector qualified as V import Effectful (runEff)-import StubProvider (flattenAssistantText) import System.Environment (lookupEnv) import Test.Tasty (TestTree, testGroup) import Test.Tasty.HUnit (assertBool, testCase)@@ -29,8 +28,8 @@ case live of Just "1" -> do OpenAI.register- let ctx = _Context & #messages .~ V.singleton (user "Reply with a single word.")- opts = _Options & #maxTokens .~ Just 16+ let ctx = emptyContext & #messages .~ V.singleton (user "Reply with a single word.")+ opts = emptyOptions & #maxTokens .~ Just 16 out <- runEff . runBaikai $ do r <- complete openai_gpt_4o_mini ctx opts
test/StubProvider.hs view
@@ -19,20 +19,10 @@ import Baikai import Baikai.Prelude-import Data.Text qualified as T import Data.Vector qualified as V import Streamly.Data.Stream (Stream) import Streamly.Data.Stream qualified as Stream --- | Concatenate the text of every 'AssistantText' block, ignoring thinking and--- tool-call blocks. baikai has no library equivalent — its own smoke tests define--- this same helper locally (see @baikai-smoke/test/Smoke.hs@).-flattenAssistantText :: V.Vector AssistantContent -> Text-flattenAssistantText = T.concat . V.toList . V.mapMaybe textOf- where- textOf (AssistantText (TextContent t)) = Just t- textOf _ = Nothing- -- | The 'Api' tag the stub provider serves. A 'Custom' tag needs no catalog entry. stubApi :: Api stubApi = Custom "stub"@@ -40,7 +30,7 @@ -- | A hand-built 'Model' whose 'api' routes to the stub provider. stubModel :: Model stubModel =- _Model+ emptyModel & #api .~ stubApi & #modelId@@ -52,27 +42,27 @@ -- | A minimal request context (one user turn). stubContext :: Context-stubContext = _Context & #messages .~ V.singleton (user "ping")+stubContext = emptyContext & #messages .~ V.singleton (user "ping") -- | Default request options. stubOptions :: Options-stubOptions = _Options+stubOptions = emptyOptions -- | A blank assistant payload with a fixed (epoch) timestamp, reused as the base -- for both the streaming skeleton and the terminal message so nothing varies run--- to run. Borrowed from baikai's '_Response' fixture base.+-- to run. Borrowed from baikai's 'emptyResponse' fixture base. stubPayload :: AssistantPayload-stubPayload = _Response ^. #message+stubPayload = emptyResponse ^. #message -- | An assistant payload carrying the given text as its single text block. stubPayloadWith :: Text -> AssistantPayload stubPayloadWith t =- stubPayload & #content .~ V.singleton (AssistantText (_TextContent & #text .~ t))+ stubPayload & #content .~ V.singleton (AssistantText (emptyTextContent & #text .~ t)) -- | A fixed assistant response carrying the given text as its single text block. stubResponse :: Text -> Response stubResponse t =- _Response+ emptyResponse & #message .~ stubPayloadWith t & #model@@ -90,11 +80,11 @@ -- 'EventStart' first, one text block, and one 'EventDone' last. stubEvents :: Text -> [AssistantMessageEvent] stubEvents t =- [ EventStart StartPayload {partial = AssistantMessage stubPayload},+ [ EventStart StartPayload {partial = AssistantMessage stubPayload, responseId = Nothing}, TextStart IndexPayload {contentIndex = 0}, TextDelta DeltaPayload {contentIndex = 0, delta = t}, TextEnd BlockEndPayload {contentIndex = 0, content = t},- EventDone (doneTerminal Stop (AssistantMessage (stubPayloadWith t)))+ EventDone (doneTerminal Nothing Stop (AssistantMessage (stubPayloadWith t))) ] -- | The provider's streaming completion: ignore the request, emit fixed events.