Skip to content

Migrating to 0.4

0.4 is purely additive on top of 0.3. No imports break, no signatures change, no behavior changes. The release introduces three new modality services (STT, TTS, music generation), the shared Audio / Transcript / Music domain that backs them, and three new provider packages (@effect-uai/openai for OpenAI speech, @effect-uai/elevenlabs, @effect-uai/inworld). Skip this page if you’re only using language models.

At a glance

What’s newWhere it lives
Transcriber service (STT)@effect-uai/core/Transcriber
SpeechSynthesizer service (TTS)@effect-uai/core/SpeechSynthesizer
MusicGenerator service@effect-uai/core/MusicGenerator
Audio / transcript / music domain@effect-uai/core/Audio, /Transcript, /Music
SttStreaming / TtsIncrementalTextprovider-fit markers
OpenAI speech provider@effect-uai/openai (new package)
ElevenLabs speech provider@effect-uai/elevenlabs (new package)
Inworld speech provider@effect-uai/inworld (new package)
Gemini speech + Lyria music@effect-uai/google/Gemini{Synthesizer,Transcriber} + LyriaGenerator

Speech services (STT + TTS)

Transcriber and SpeechSynthesizer are siblings of LanguageModel and EmbeddingModel. Both expose a sync shape (transcribe / synthesize) and a stream shape (streamTranscriptionFrom / streamSynthesisFrom).

  • Streaming STT takes Stream<Uint8Array> (mic frames) and returns a Stream<TranscriptEvent> (partial / final / speech-started / speech-stopped / usage).
  • Streaming TTS takes Stream<string> (incremental text) and returns a Stream<AudioChunk>.

Live audio composes with the rest of Effect (Stream.run*, Stream.merge, scoped resources) — no special audio runtime needed.

import { Transcriber } from "@effect-uai/core/Transcriber"
import { OpenAIRealtimeTranscriber } from "@effect-uai/openai/OpenAIRealtimeTranscriber"
const program = Effect.gen(function* () {
const transcripts = yield* Transcriber.streamTranscriptionFrom(micFrames)
yield* transcripts.pipe(Stream.runForEach((ev) => Effect.sync(() => console.log(ev))))
})
program.pipe(Effect.provide(OpenAIRealtimeTranscriber.layer({ apiKey })))

Provider-fit markers

SttStreaming and TtsIncrementalText are tags providers register alongside the base Transcriber / SpeechSynthesizer service. Recipes that demand live audio (e.g. Voice loop) require the marker at the type level — a sync-only provider fails to compile rather than blowing up at runtime.

// Demands a streaming STT provider; rejects sync-only at compile time.
const recipe: Effect.Effect<void, never, Transcriber | SttStreaming> = ...

Music generation

MusicGenerator is a third sibling for prompt-to-audio music models. Today: Google’s Lyria via @effect-uai/google/LyriaGenerator. Same sync generate shape as the speech services.

New provider packages

Three new npm packages ship in 0.4:

  • @effect-uai/openai — OpenAI speech only (TTS via OpenAISynthesizer, STT via OpenAITranscriber and OpenAIRealtimeTranscriber). The OpenAI Responses API stays in @effect-uai/responses; these are intentionally separate packages because the API surfaces are unrelated.
  • @effect-uai/elevenlabs — TTS (incremental text-in via WebSocket) and Scribe v2 Realtime STT. Registers TtsIncrementalText and SttStreaming.
  • @effect-uai/inworld — sync + realtime TTS / STT. Registers both markers on the realtime layers.

The existing @effect-uai/google package gains GeminiSynthesizer (TTS), GeminiTranscriber (STT), and LyriaGenerator (music) sub-paths.

If you’re not adopting speech / music in this upgrade:

  1. Bump every @effect-uai/* dependency in your package.json to ^0.4.0.
  2. Run typecheck. Nothing should break.
  3. Done.

If you are adopting speech / music:

  1. Bump dependencies as above.
  2. Install the new provider package(s) you need (@effect-uai/openai, @effect-uai/elevenlabs, @effect-uai/inworld, or use the new @effect-uai/google exports).
  3. See the speech overview and music generation overview for the service shape and the per-provider docs for setup.

What about renames / removals?

There aren’t any. The renames listed in some 0.4 changelog entries (Loop.streamUntilCompleteLoop.onTurnComplete, Toolkit.nextStateFromToolkit.continueWith, Match module removal, ToolResult / ToolEvent / Image*Data.TaggedEnum, Loop.loopWithState, embeddings) actually landed in 0.3 and are covered by Migrating to 0.3. If you’re upgrading straight from 0.2, apply the 0.3 guide first, then this one.

Using Claude to migrate

Nothing to migrate here, but the effect-uai-migrate skill flags 0.4 as a no-op release so it won’t try to invent rewrites:

/skill effect-uai-migrate