Skip to content

Commit 0c3b58b

Browse files
authored
fix(provider): add specificationVersion to ProviderV3 (#9820)
1 parent 4a25ff9 commit 0c3b58b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+232
-77
lines changed

.changeset/angry-cobras-care.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
'@ai-sdk/openai-compatible': patch
3+
'@ai-sdk/amazon-bedrock': patch
4+
'@ai-sdk/google-vertex': patch
5+
'@ai-sdk/huggingface': patch
6+
'@ai-sdk/assemblyai': patch
7+
'@ai-sdk/elevenlabs': patch
8+
'@ai-sdk/perplexity': patch
9+
'@ai-sdk/togetherai': patch
10+
'@ai-sdk/anthropic': patch
11+
'@ai-sdk/deepinfra': patch
12+
'@ai-sdk/fireworks': patch
13+
'@ai-sdk/replicate': patch
14+
'@ai-sdk/cerebras': patch
15+
'@ai-sdk/deepgram': patch
16+
'@ai-sdk/deepseek': patch
17+
'@ai-sdk/provider': patch
18+
'@ai-sdk/baseten': patch
19+
'@ai-sdk/gateway': patch
20+
'@ai-sdk/mistral': patch
21+
'@ai-sdk/cohere': patch
22+
'@ai-sdk/gladia': patch
23+
'@ai-sdk/google': patch
24+
'@ai-sdk/openai': patch
25+
'@ai-sdk/vercel': patch
26+
'@ai-sdk/azure': patch
27+
'@ai-sdk/revai': patch
28+
'@ai-sdk/groq': patch
29+
'@ai-sdk/luma': patch
30+
'@ai-sdk/fal': patch
31+
'@ai-sdk/xai': patch
32+
'ai': patch
33+
---
34+
35+
fix(provider): add specificationVersion to ProviderV3
Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import type { LanguageModelV3, ProviderV2, ProviderV3 } from '@ai-sdk/provider';
1+
import type { ProviderV2, ProviderV3 } from '@ai-sdk/provider';
22
import { LanguageModelMiddleware } from '../types/language-model-middleware';
33
import { wrapLanguageModel } from './wrap-language-model';
4+
import { asProviderV3 } from '../model/as-provider-v3';
45

56
/**
67
* Wraps a ProviderV3 instance with middleware functionality.
@@ -20,20 +21,17 @@ export function wrapProvider({
2021
provider: ProviderV3 | ProviderV2;
2122
languageModelMiddleware: LanguageModelMiddleware | LanguageModelMiddleware[];
2223
}): ProviderV3 {
23-
const wrappedProvider = {
24-
languageModel(modelId: string) {
25-
let model = provider.languageModel(modelId);
26-
model = wrapLanguageModel({
27-
model: model as LanguageModelV3,
24+
const providerV3 = asProviderV3(provider);
25+
return {
26+
specificationVersion: 'v3',
27+
languageModel: (modelId: string) =>
28+
wrapLanguageModel({
29+
model: providerV3.languageModel(modelId),
2830
middleware: languageModelMiddleware,
29-
});
30-
return model;
31-
},
32-
textEmbeddingModel: provider.textEmbeddingModel,
33-
imageModel: provider.imageModel,
34-
transcriptionModel: provider.transcriptionModel,
35-
speechModel: provider.speechModel,
31+
}),
32+
textEmbeddingModel: providerV3.textEmbeddingModel,
33+
imageModel: providerV3.imageModel,
34+
transcriptionModel: providerV3.transcriptionModel,
35+
speechModel: providerV3.speechModel,
3636
};
37-
38-
return wrappedProvider as ProviderV3;
3937
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { EmbeddingModelV2, EmbeddingModelV3 } from '@ai-sdk/provider';
2+
3+
export function asEmbeddingModelV3<VALUE>(
4+
model: EmbeddingModelV2<VALUE> | EmbeddingModelV3<VALUE>,
5+
): EmbeddingModelV3<VALUE> {
6+
if (model.specificationVersion === 'v3') {
7+
return model;
8+
}
9+
10+
// TODO this could break, we need to properly map v2 to v3
11+
// and support all relevant v3 properties:
12+
return new Proxy(model, {
13+
get(target, prop: keyof EmbeddingModelV2<VALUE>) {
14+
if (prop === 'specificationVersion') return 'v3';
15+
return target[prop];
16+
},
17+
}) as unknown as EmbeddingModelV3<VALUE>;
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { ImageModelV2, ImageModelV3 } from '@ai-sdk/provider';
2+
3+
export function asImageModelV3(
4+
model: ImageModelV2 | ImageModelV3,
5+
): ImageModelV3 {
6+
if (model.specificationVersion === 'v3') {
7+
return model;
8+
}
9+
10+
// TODO this could break, we need to properly map v2 to v3
11+
// and support all relevant v3 properties:
12+
return new Proxy(model, {
13+
get(target, prop: keyof ImageModelV2) {
14+
if (prop === 'specificationVersion') return 'v3';
15+
return target[prop];
16+
},
17+
}) as unknown as ImageModelV3;
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { LanguageModelV2, LanguageModelV3 } from '@ai-sdk/provider';
2+
3+
export function asLanguageModelV3(
4+
model: LanguageModelV2 | LanguageModelV3,
5+
): LanguageModelV3 {
6+
if (model.specificationVersion === 'v3') {
7+
return model;
8+
}
9+
10+
// TODO this could break, we need to properly map v2 to v3
11+
// and support all relevant v3 properties:
12+
return new Proxy(model, {
13+
get(target, prop: keyof LanguageModelV2) {
14+
if (prop === 'specificationVersion') return 'v3';
15+
return target[prop];
16+
},
17+
}) as unknown as LanguageModelV3;
18+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { ProviderV2, ProviderV3 } from '@ai-sdk/provider';
2+
import { asEmbeddingModelV3 } from './as-embedding-model-v3';
3+
import { asImageModelV3 } from './as-image-model-v3';
4+
import { asLanguageModelV3 } from './as-language-model-v3';
5+
import { asTranscriptionModelV3 } from './as-transcription-model-v3';
6+
import { asSpeechModelV3 } from './as-speech-model-v3';
7+
8+
export function asProviderV3(provider: ProviderV2 | ProviderV3): ProviderV3 {
9+
if (
10+
'specificationVersion' in provider &&
11+
provider.specificationVersion === 'v3'
12+
) {
13+
return provider;
14+
}
15+
16+
return {
17+
specificationVersion: 'v3',
18+
languageModel: (modelId: string) =>
19+
asLanguageModelV3(provider.languageModel(modelId)),
20+
textEmbeddingModel: (modelId: string) =>
21+
asEmbeddingModelV3(provider.textEmbeddingModel(modelId)),
22+
imageModel: (modelId: string) =>
23+
asImageModelV3(provider.imageModel(modelId)),
24+
transcriptionModel: provider.transcriptionModel
25+
? (modelId: string) =>
26+
asTranscriptionModelV3(provider.transcriptionModel!(modelId))
27+
: undefined,
28+
speechModel: provider.speechModel
29+
? (modelId: string) => asSpeechModelV3(provider.speechModel!(modelId))
30+
: undefined,
31+
};
32+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { SpeechModelV2, SpeechModelV3 } from '@ai-sdk/provider';
2+
3+
export function asSpeechModelV3(
4+
model: SpeechModelV3 | SpeechModelV2,
5+
): SpeechModelV3 {
6+
if (model.specificationVersion === 'v3') {
7+
return model;
8+
}
9+
10+
// TODO this could break, we need to properly map v2 to v3
11+
// and support all relevant v3 properties:
12+
return new Proxy(model, {
13+
get(target, prop: keyof SpeechModelV2) {
14+
if (prop === 'specificationVersion') return 'v3';
15+
return target[prop];
16+
},
17+
}) as unknown as SpeechModelV3;
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { TranscriptionModelV2, TranscriptionModelV3 } from '@ai-sdk/provider';
2+
3+
export function asTranscriptionModelV3(
4+
model: TranscriptionModelV3 | TranscriptionModelV2,
5+
): TranscriptionModelV3 {
6+
if (model.specificationVersion === 'v3') {
7+
return model;
8+
}
9+
10+
// TODO this could break, we need to properly map v2 to v3
11+
// and support all relevant v3 properties:
12+
return new Proxy(model, {
13+
get(target, prop: keyof TranscriptionModelV2) {
14+
if (prop === 'specificationVersion') return 'v3';
15+
return target[prop];
16+
},
17+
}) as unknown as TranscriptionModelV3;
18+
}

packages/ai/src/model/resolve-model.ts

Lines changed: 9 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,20 @@
11
import { gateway } from '@ai-sdk/gateway';
22
import {
3-
EmbeddingModelV2,
43
EmbeddingModelV3,
5-
LanguageModelV2,
64
LanguageModelV3,
75
ProviderV3,
8-
SpeechModelV2,
96
SpeechModelV3,
10-
TranscriptionModelV2,
117
TranscriptionModelV3,
128
} from '@ai-sdk/provider';
139
import { UnsupportedModelVersionError } from '../error';
1410
import { EmbeddingModel } from '../types/embedding-model';
1511
import { LanguageModel } from '../types/language-model';
1612
import { SpeechModel } from '../types/speech-model';
1713
import { TranscriptionModel } from '../types/transcription-model';
18-
19-
function transformToV3LanguageModel(model: LanguageModelV2): LanguageModelV3 {
20-
return new Proxy(model, {
21-
get(target, prop: keyof LanguageModelV2) {
22-
if (prop === 'specificationVersion') return 'v3';
23-
return target[prop];
24-
},
25-
}) as unknown as LanguageModelV3;
26-
}
27-
28-
function transformToV3EmbeddingModel<VALUE>(
29-
model: EmbeddingModelV2<VALUE>,
30-
): EmbeddingModelV3<VALUE> {
31-
return new Proxy(model, {
32-
get(target, prop: keyof EmbeddingModelV2<VALUE>) {
33-
if (prop === 'specificationVersion') return 'v3';
34-
return target[prop];
35-
},
36-
}) as unknown as EmbeddingModelV3<VALUE>;
37-
}
38-
39-
function transformToV3TranscriptionModel(
40-
model: TranscriptionModelV2,
41-
): TranscriptionModelV3 {
42-
return new Proxy(model, {
43-
get(target, prop: keyof TranscriptionModelV2) {
44-
if (prop === 'specificationVersion') return 'v3';
45-
return target[prop];
46-
},
47-
}) as unknown as TranscriptionModelV3;
48-
}
49-
50-
function transformToV3SpeechModel(model: SpeechModelV2): SpeechModelV3 {
51-
return new Proxy(model, {
52-
get(target, prop: keyof SpeechModelV2) {
53-
if (prop === 'specificationVersion') return 'v3';
54-
return target[prop];
55-
},
56-
}) as unknown as SpeechModelV3;
57-
}
14+
import { asEmbeddingModelV3 } from './as-embedding-model-v3';
15+
import { asLanguageModelV3 } from './as-language-model-v3';
16+
import { asSpeechModelV3 } from './as-speech-model-v3';
17+
import { asTranscriptionModelV3 } from './as-transcription-model-v3';
5818

5919
export function resolveLanguageModel(model: LanguageModel): LanguageModelV3 {
6020
if (typeof model !== 'string') {
@@ -69,10 +29,8 @@ export function resolveLanguageModel(model: LanguageModel): LanguageModelV3 {
6929
modelId: unsupportedModel.modelId,
7030
});
7131
}
72-
if (model.specificationVersion === 'v2') {
73-
return transformToV3LanguageModel(model);
74-
}
75-
return model;
32+
33+
return asLanguageModelV3(model);
7634
}
7735

7836
return getGlobalProvider().languageModel(model);
@@ -93,11 +51,8 @@ export function resolveEmbeddingModel<VALUE = string>(
9351
modelId: unsupportedModel.modelId,
9452
});
9553
}
96-
if (model.specificationVersion === 'v2') {
97-
return transformToV3EmbeddingModel(model);
98-
}
9954

100-
return model;
55+
return asEmbeddingModelV3(model);
10156
}
10257

10358
// TODO AI SDK 6: figure out how to cleanly support different generic types
@@ -121,10 +76,7 @@ export function resolveTranscriptionModel(
12176
modelId: unsupportedModel.modelId,
12277
});
12378
}
124-
if (model.specificationVersion === 'v2') {
125-
return transformToV3TranscriptionModel(model);
126-
}
127-
return model;
79+
return asTranscriptionModelV3(model);
12880
}
12981

13082
return getGlobalProvider().transcriptionModel?.(model);
@@ -145,10 +97,7 @@ export function resolveSpeechModel(
14597
modelId: unsupportedModel.modelId,
14698
});
14799
}
148-
if (model.specificationVersion === 'v2') {
149-
return transformToV3SpeechModel(model);
150-
}
151-
return model;
100+
return asSpeechModelV3(model);
152101
}
153102

154103
return getGlobalProvider().speechModel?.(model);

packages/ai/src/registry/custom-provider.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export function customProvider<
5555
speechModel(modelId: ExtractModelId<SPEECH_MODELS>): SpeechModelV3;
5656
} {
5757
return {
58+
specificationVersion: 'v3',
5859
languageModel(modelId: ExtractModelId<LANGUAGE_MODELS>): LanguageModelV3 {
5960
if (languageModels != null && modelId in languageModels) {
6061
return languageModels[modelId];

0 commit comments

Comments
 (0)