Migrating to 0.13
No rewrites needed. 0.13 is additive: four new capability tags in
@effect-uai/core, three new packages (@effect-uai/retrieval,
@effect-uai/mcp, @effect-uai/fal), a Jina reranker, and image
generation across OpenAI, Google and fal. Bump your dependencies, run
typecheck, done.
One caveat: ContentBlock and TurnEvent each gained a member for
images in a turn, so if you match exhaustively on either in your own
code, you will need a new arm. Details below.
No effect bump this release; the peer range stays >=4.0.0-rc.111 <5.0.0.
If you hit a compile error that looks like a rename, you are crossing an earlier breaking release. Apply the 0.12 page and the ones before it first.
What’s new (additive, no migration needed)
Retrieval
Three tags in core, in the usual shape: a portable service, a free helper, and implementations that register the tag at the layer.
Reranker scores a candidate set against a query and hands back positions,
best first. It is a per-hop filter for agent loops: anywhere a search or a tool
produces more candidates than the model can afford to read.
import { rerank } from "@effect-uai/core/Reranker"
const { results } = yield * rerank({ model, query, documents: candidates, topN: 5 })// results[].index points back into the documents you passedScores are sorted descending and higher is better, but they are not calibrated
and not comparable across calls, so cut by rank rather than by a fixed
threshold. @effect-uai/jina/JinaReranker is the first provider, registering
both the typed tag and the generic one; its typed request widens documents to
{ text } / { image } for jina-reranker-m0.
Chunker splits a document into passages, and Tokenizer encodes and decodes
text. Both are implemented by the new @effect-uai/retrieval:
pnpm add @effect-uai/retrievalChunking: four chunkers (recursive,sentences,markdown,fixed), each reporting the offsets every passage came from, so a hit can be traced back to its source.Chunking.layerserves one through theChunkertag, so ingest code never names a strategy.Rank.rrf: reciprocal rank fusion, for merging a keyword leg and a vector leg whose scores mean nothing to each other.HuggingFaceTokenizer: theTokenizertag over any Hub repo with atokenizer.json, behind an optional peer on@huggingface/tokenizers. Downloading and building are separate, so you cache the vocabulary rather than refetching on every boot.
See retrieval, chunking, and reranking.
@effect-uai/mcp
A Model Context Protocol client. Point it at a server and its tools become a
Toolkit the loop runs like any other, so an MCP tool and a local one are the
same thing to your agent.
import * as Client from "@effect-uai/mcp/Client"import { mcpToolkit } from "@effect-uai/mcp/Toolkit"
const client = yield * Client.connect({ transport: "http", url })const toolkit = yield * mcpToolkit(client, { prefix: "deepwiki" })Both transports (http, stdio), all three protocol revisions with the era
detected per connection rather than configured, and auth as a seam
(Auth.Static, Auth.TokenSource, Auth.OAuth) with tokens Redacted until
the wire. Connection lifetime is scope lifetime.
See MCP and the MCP tools recipe.
Image generation
ImageGenerator is the fourth new tag: a prompt goes in, images come out,
and swapping the Layer moves you between providers.
import { edit, generate } from "@effect-uai/core/ImageGenerator"
const { images } = yield * generate({ prompt, model, aspectRatio: "16:9", resolution: "2K" })const changed = yield * edit({ prompt, model, images: [images[0].image] })Shape and size are a ratio plus a tier rather than pixels, because a
hardcoded "1536x1024" becomes the wrong crop the moment you switch
providers. Adapters derive the dimensions; set exact pixels on the
provider-typed request if you need them. streamGeneration and
streamEdit give preview frames while the image resolves and are gated
behind the ImageStreaming marker, so a provider that cannot preview is
a compile error rather than a stream that never fires.
Three providers: @effect-uai/openai (gpt-image-2, the only one with
partial-image streaming), @effect-uai/google (Nano Banana 2, faster and
cheaper), and the new @effect-uai/fal, which reaches FLUX, Seedream,
Qwen Image and the open-weights field behind one key.
pnpm add @effect-uai/falOn fal the model id is an endpoint path, and generating and editing are separate endpoints, so copy the id from the model’s page verbatim.
See image generation, and the storyboard and conversational image edit recipes.
Images inside a turn
Google’s image models also work as LanguageModel models, answering an
ordinary turn with pictures among the content blocks. That adds
output_image to ContentBlock and ImageOutput to TurnEvent.
const turn = yield * gemini.turn({ model: "gemini-3.1-flash-image", history })Turn.assistantImages(turn) // ImageSource[]Replaying the turn edits the same picture. Providers without an
assistant-image wire drop the block on replay and log a capability
warning; Turn.imagesAsInput is the explicit conversion when you want
another model to look at it.
This is additive, but note one thing if you exhaustively match on
ContentBlock or TurnEvent in your own code: both unions gained a
member, so an exhaustive match will stop compiling until you add an arm.
That is the only place 0.13 can break a build.
See images in a turn.
Docs moved
Two pages changed URL. Both redirect, so existing links keep working:
| Old | New |
|---|---|
/concepts/items-and-turns | /language-models/items-and-turns |
/concepts/metrics | /language-models/metrics |
/recipes/hybrid-rag also now redirects to
/recipes/agentic-search, which is the same recipe
under a name that says what it does.