Skip to content

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 passed

Scores 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:

Terminal window
pnpm add @effect-uai/retrieval
  • Chunking: 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.layer serves one through the Chunker tag, 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: the Tokenizer tag over any Hub repo with a tokenizer.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.

Terminal window
pnpm add @effect-uai/fal

On 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:

OldNew
/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.