@ai-sdk/openai 4.0.0 moved the whole package to the v4 provider specification. At 3.0.74 OpenAIProvider extended ProviderV3 and its language, embedding, image, speech and transcription models were the matching v3 types; at 4.0.0 the same interface extends ProviderV4 and every one of those models is the v4 type, with no v3 name left in the published declarations. Nothing was removed and nothing was renamed, so the code that breaks is your own: a value you declare against one of the v3 types and assign one of this package’s models to.
What changed
- The provider and all five model kinds are the v4 types.
OpenAIProviderextendsProviderV4where it extendedProviderV3, andLanguageModelV3,EmbeddingModelV3,ImageModelV3,SpeechModelV3andTranscriptionModelV3are each replaced by their v4 counterpart in the same positions. The 3.0.74 export list survives intact at 4.0.0, which only adds names to it, soopenai,createOpenAIand every option type keep working.@ai-sdk/provider4.0.0 still exports the v3 names beside the v4 ones, so the old annotations resolve and the assignment is what fails, with TS2322, becausespecificationVersiongoes from'v3'to'v4'. aiis not where this surfaces.ai7.0.0 still admitsLanguageModelV3in itsLanguageModelunion, so passing one of these models intogenerateText,streamTextorwrapLanguageModelcompiles before and after. Emendant reports the annotation, never the call.
Before and after
A module that pins this package’s values to the v3 types:
import type { ImageModelV3, LanguageModelV3, SpeechModelV3 } from '@ai-sdk/provider';import { openai } from '@ai-sdk/openai';
export const chat: LanguageModelV3 = openai('gpt-5');
export function images(): ImageModelV3 { return openai.imageModel('gpt-image-1');}
export class Voices { readonly speech: SpeechModelV3 = openai.speech('tts-1');}The 4.0.0 declarations say what these values now are, so the annotations follow them:
import type { ImageModelV4, LanguageModelV4, SpeechModelV4 } from '@ai-sdk/provider';import { openai } from '@ai-sdk/openai';
export const chat: LanguageModelV4 = openai('gpt-5');
export function images(): ImageModelV4 { return openai.imageModel('gpt-image-1');}
export class Voices { readonly speech: SpeechModelV4 = openai.speech('tts-1');}The same applies to TranscriptionModelV3 on openai.transcription, to EmbeddingModelV3 written as openai.textEmbeddingModel('text-embedding-3-small') satisfies EmbeddingModelV3, and to a ProviderV3 field holding createOpenAI(...).
Migration guidance and its limits
Emendant writes this rewrite for you only when you name a model provider, on the command line or in emendant.json. The edit is narrow: the annotation and the specifier it was imported under both move to the v4 name, and no import is added or removed, because @ai-sdk/provider 4.0.0 exports all twelve names. Without a provider the sites are reported with the same guidance and nothing is written, and the compiler finds every one of them once you upgrade.
Read the diff rather than taking it. Confidence is medium here, and the reason is the same one that keeps this off a transform: the matcher sees that a value is built against one of the v3 types and never sees where that value came from. A repository that writes its own v3 model by hand and annotates it is reported and is not broken, because nothing in this package is being assigned to it. The guidance says to confirm the annotated value really comes from @ai-sdk/openai before editing, and to make no edit and say so where it does not, since retyping your own v3 implementation, or a wrapper that has to keep speaking v3 to an older provider, is a migration that compiles and is wrong.
Naming a v3 type is not enough to be reported, and it is not enough to be broken. A parameter annotated LanguageModelV3, a type argument such as Array<LanguageModelV3>, a cast, and an alias body are all left alone: each receives a model rather than building one, and each keeps compiling at 4.0.0. The matcher also follows the module a name came from, so LanguageModelV3 imported from a local provider module of your own is not this finding.
A clean scan is narrower than the release. This coverage is about the v3 to v4 move and nothing else in 4.0.0, and it says nothing about @ai-sdk/provider itself, which removed no v3 name at its own 4.0.0 and so breaks nothing on its own.
What Emendant detects, fixes and verifies
1 change in @ai-sdk/openai 4.0.0. 0 patched by a transform, 1 patched only with a model provider you name, 0 report only. Every patch is proved in a copy of your repository before it is offered, and its header names the checks that passed. This table is generated from the feed entry, so it cannot claim more than the entry does.
| Change | Severity | Detects | Fix | Verified by |
|---|---|---|---|---|
@ai-sdk/openai models are the v4 provider types, so a v3 annotation stops type checkingai-sdk-openai-npm-4.0.0-provider-types-v3-to-v4 | Breaking medium confidence |
| Patch, only when you name a model provider | Your typecheck and tests |
emendant explain <change-id> prints any row's entry, guidance and sources at the terminal.
Coverage
- Snapshot
2026-09-19.4, sequence 26, signed 19 September 2026- Minimum CLI
emendant@0.3.0- Feed entry
feed/npm/@ai-sdk/openai/4.0.0.json
emendant feed status shows the snapshot your machine holds, and emendant feed update fetches the latest.
Primary sources
Every claim above was checked against these, each pinned to the release rather than to a default branch.
- package.jsonvercel/ai at @ai-sdk/openai@4.0.0
- package.jsonvercel/ai at @ai-sdk/provider@4.0.0
- @ai-sdk/openai@4.0.0npm registry, the published package
- @ai-sdk/openai@3.0.74npm registry, the published package
- @ai-sdk/provider@4.0.0npm registry, the published package
- @ai-sdk/provider@3.0.10npm registry, the published package
Scan your repository
Runs locally, reads your lockfile and source, sends nothing anywhere.
npx emendant scanThen npx emendant fix writes the patches the table says exist. The getting started guide covers the flags and the patch grades.