Skip to content

Migrating to 0.12

0.12 has two breaking changes, both narrow: providerData is namespaced per provider, and @effect-uai/exa/ExaDeepResearch is removed. Neither touches the core loop, tools, or streaming. Everything else is additive.

No effect bump this release; the peer range stays >=4.0.0-beta.94 <5.0.0.

If you hit a compile error that looks like an older rename, you are crossing an earlier breaking release. Apply the 0.11 page and the ones before it first.

Breaking: providerData is namespaced per provider

providerData is a shared slot on a HistoryItem. Providers used to write to its root and re-emit whatever they found, so an item that had passed through another provider first (dynamic fallback) went back on the wire as that provider’s data. Now each provider writes under its own key and reads only that key, so several can coexist and unknown data is left untouched. Where the slot is meant to be read, it now carries a domain value with an exported accessor, not the raw wire shape.

Google deep research. The step-by-step research trace moved to providerData.gemini as a typed GeminiResearchData. Read it with the accessor instead of reaching into providerData:

// before
const steps = (item.providerData as any)?.steps
// after
import * as GoogleDeepResearch from "@effect-uai/google/GoogleDeepResearch"
const data = GoogleDeepResearch.researchDataOf(item) // Option<GeminiResearchData>

The Turn still carries the final report and the deduped union of sources; only the per-step trace lives under the namespaced slot.

Perplexity. No longer writes providerData at all: its text is on the Turn, usage on Turn.usage, and search results are annotations with their [n] markers. Drop any code reading providerData off a Perplexity result.

Responses. Keeps its wire item internally under providerData.responses, purely to round-trip encrypted_content and item ids; it was never public. One upgrade caveat: items persisted by an older version keep their data at the root, so a conversation that spans the upgrade loses encrypted_content and item ids on those items (they fall back to a normal encode). Only turns straddling the upgrade are affected.

Breaking: ExaDeepResearch removed

@effect-uai/exa/ExaDeepResearch is gone. Exa retired the Research API (POST /research/v0/tasks now returns RESEARCH_RETIRED), so the capability could no longer succeed. @effect-uai/exa/ExaSearch and @effect-uai/exa/ExaContents are unaffected.

For provider-hosted deep research, swap the layer for another DeepResearch provider; the capability tag and call sites are identical:

  • @effect-uai/responses/OpenAIDeepResearch
  • @effect-uai/perplexity/PerplexityDeepResearch
  • @effect-uai/google/GoogleDeepResearch

Behavior change: throughput counts all output deltas

Metrics.throughput now measures every delta that carries generated output, not just TextDelta: ReasoningDelta, RefusalDelta, and ToolCallArgsDelta count too. A tool-heavy agent that used to chart a rate near zero (its output is mostly tool-call arguments) will now show a real rate. ThroughputOptions.tokenizer is called with the new OutputDelta type rather than TurnEvent; a tokenizer that accepts a full TurnEvent stays assignable. timeToFirstToken is unchanged.

What’s new (additive)

  • @effect-uai/chat-completions: a reusable OpenAI Chat Completions (POST /chat/completions) LanguageModel base for any compatible endpoint (OpenRouter, Requesty, Groq, Together, self-hosted). Prefer Responses where the endpoint supports it; this is the legacy dialect for endpoints that do not. See Chat Completions and gateways.
  • @effect-uai/openai is now the single OpenAI install. It re-exports the Responses language model, embeddings, and deep research from @effect-uai/responses alongside its speech surface, under new subpaths (@effect-uai/openai/Responses, /OpenAIEmbedding, /OpenAIDeepResearch, /ResponsesTools). @effect-uai/responses still installs standalone for protocol-only use (e.g. against a gateway). No code change required.
  • Anthropic prompt caching. anthropicLayer({ apiKey, promptCaching: true }) (or { ttl: "1h" }) pays for a long system prompt and toolkit once instead of per turn. Off by default. Cache-creation (write) tokens are now reported: Items.Usage.input_tokens_details gains cache_write_tokens, and Metrics emits an effect_uai_cache_write_tokens counter.

Internally, the Mistral language model was rebuilt on @effect-uai/chat-completions (it speaks that dialect); no public API change.

Migration order

  1. Bump dependencies to 0.12 and run pnpm typecheck.
  2. If you read providerData on a deep-research result, switch to GoogleDeepResearch.researchDataOf(item) (and drop any Perplexity providerData reads).
  3. If you used @effect-uai/exa/ExaDeepResearch, swap to another DeepResearch provider layer.