packages feed

baikai-effectful-0.3.0.0: README.md

# baikai-effectful

A thin, **policy-free** [`effectful`](https://hackage.haskell.org/package/effectful)
binding over [baikai](../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

```haskell
data Baikai :: Effect where
  Complete      :: Model -> Context -> Options -> Baikai m Response
  StreamCollect :: 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]`.     |
| `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.

## Interpreters

```haskell
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

```haskell
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:

```bash
# 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`.