baikai-effectful: effectful binding for the baikai AI-provider transport

[ ai, bsd3, library ] [ Propose Tags ] [ Report a vulnerability ]

A thin, policy-free effectful binding over baikai's transport. Provides the dynamic Baikai effect (Complete StreamCollect StreamCollectTraced / StreamEach) and interpreters over a real or isolated provider registry. Adds no retries, caching, budgets, or error remapping.


[Skip to Readme]

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

  • No Candidates
Versions [RSS] 0.1.0.0, 0.2.0.0, 0.3.0.0, 0.3.0.1, 0.3.0.2, 0.3.0.3, 0.4.0.0, 0.4.0.1, 0.4.0.2, 0.4.0.3, 0.4.0.4, 0.4.0.5, 0.5.0.0
Change log CHANGELOG.md
Dependencies baikai (>=0.10.1 && <0.11), base (>=4.20 && <5), effectful-core (>=2.7 && <2.8), streamly-core (>=0.3 && <0.5), text (>=2.1 && <2.2), vector (>=0.13 && <0.14) [details]
Tested with ghc ==9.12.4
License BSD-3-Clause
Copyright (c) 2026 Nadeem Bitar
Author Nadeem Bitar
Maintainer nadeem@gmail.com
Uploaded by shinzui at 2026-11-03T14:46:56Z
Category AI
Distributions
Reverse Dependencies 2 direct, 11 indirect [details]
Downloads 123 total (54 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs uploaded by user
Build status unknown [no reports yet]

Readme for baikai-effectful-0.5.0.0

[back to package description]

baikai-effectful

A thin, policy-free effectful binding over baikai's transport. It exposes one dynamic effect, Baikai, whose operations mirror baikai's transport functions, plus interpreters that run those 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: 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

data Baikai :: Effect where
  Complete      :: Model -> Context -> Options -> Baikai m Response
  StreamCollect :: Model -> Context -> Options -> Baikai m [AssistantMessageEvent]
  StreamCollectTraced :: TraceSink -> Model -> Context -> Options -> Baikai m [AssistantMessageEvent]
  StreamEach    :: Model -> Context -> Options -> (AssistantMessageEvent -> m ()) -> Baikai m ()

Operations

Operation Meaning
complete m c o Blocking completion → Response, including error-shaped responses.
streamCollect m c o Drain the stream into the full [AssistantMessageEvent].
streamCollectTraced sink m c o Collect events with tracing and bounded cleanup on cancellation.
streamEach m c o k Run callback k once per event, in order, inside Eff.

streamEach preserves incrementality (the callback sees events as they arrive); a terminal EventError appears in-band rather than as a thrown exception, matching baikai.

Evidence on cancellation

streamCollectTraced delegates to core's withTraceListWith, which owns the whole fold lifetime. Supply a TraceSink and opt in through Options.evidence. On success or provider failure the sink's single CallEvidence carries exactly the terminal's evidence record, including its callId. A strict pre-dispatch refusal retains core's failed evidence and never invokes the provider. Opt-out produces no evidence; providers that omit successful evidence retain core's strict missing-record failure.

For cancellation before terminal commitment, use a short, nonblocking sink that stores the complete ModelCallEvidence in a caller-owned cell before acknowledging it. Read that cell immediately in the caller's exception handler. With a healthy sink the CallAborted record is already present and the original exception is unchanged. The sink fold's end action also completes before propagation. Cancellation after terminal commitment retains that terminal's evidence and adds no abort.

The sink drain budget is one second in total. A blocked or throwing sink still allows cancellation to unwind and reports its failure on stderr; an empty cell means delivery was unconfirmed. A worker abandoned at the bound can deliver later. Exactly-once means Baikai submits one evidence event, not that a failing external destination durably stores it. Keep slow export behind a nonblocking queue.

Core's withTraceFold, withTraceList, withTrace, and their With variants own the same lifetime. Arbitrary abandonment of withTraceStream between stream steps still relies on garbage collection. The existing streamCollect and streamEach operations keep their behavior.

Interpreters

runBaikai     :: (IOE :> es) => Eff (Baikai : es) a -> Eff es a                       -- global registry
runBaikaiWith :: (IOE :> es) => ProviderRegistry -> Eff (Baikai : es) a -> Eff es a   -- explicit registry

The operations are registry-agnostic; the interpreter picks the registry, mirroring baikai's own completeRequest vs completeRequestWith split. Because Baikai is a dynamic effect, you can re-interpret the same operations — stub them in tests, record/replay, intercept, or (one layer up) wrap them with retries/caching/tracing — without touching any call site.

Example

import Baikai.Effectful
import Effectful (Eff, runEff)

describe :: (Baikai :> es) => Model -> Context -> Options -> Eff es Response
describe = complete

main :: IO ()
main = do
  -- against baikai's process-global registry (providers registered elsewhere):
  r <- runEff . runBaikai $ describe model ctx opts
  print r

For a hermetic test, register a stub provider in an isolated registry with Baikai.Provider.Registry.newProviderRegistry / registerApiProviderWith and drive the operations through runEff . runBaikaiWith reg. See test/ in this package.

Live demo

The test suite includes a live, network-touching demo gated on an environment variable, so the default run stays hermetic:

# hermetic (default): no network, no key — the live case prints a skip line and passes
cabal test baikai-effectful-test

# live: registers the OpenAI provider and makes one real call
BAIKAI_EFFECTFUL_LIVE=1 OPENAI_API_KEY=sk-... cabal test baikai-effectful-test

Why a separate package

The core baikai package deliberately carries no effectful dependency, and most baikai users do not use effectful. Shipping the binding as its own artifact keeps the core dependency-light while giving any effectful program a reusable baikai↔effectful seam — the same way baikai-claude / baikai-openai layer on baikai.