@ai-sdk/openai-compatible 3.0.0-beta.2 → 3.0.0-beta.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  APICallError,
3
3
  InvalidResponseDataError,
4
- LanguageModelV3,
5
- LanguageModelV3CallOptions,
6
- LanguageModelV3Content,
7
- LanguageModelV3FinishReason,
8
- LanguageModelV3GenerateResult,
9
- LanguageModelV3StreamPart,
10
- LanguageModelV3StreamResult,
11
- SharedV3ProviderMetadata,
12
- SharedV3Warning,
4
+ LanguageModelV4,
5
+ LanguageModelV4CallOptions,
6
+ LanguageModelV4Content,
7
+ LanguageModelV4FinishReason,
8
+ LanguageModelV4GenerateResult,
9
+ LanguageModelV4StreamPart,
10
+ LanguageModelV4StreamResult,
11
+ SharedV4ProviderMetadata,
12
+ SharedV4Warning,
13
13
  } from '@ai-sdk/provider';
14
14
  import {
15
15
  combineHeaders,
@@ -18,6 +18,7 @@ import {
18
18
  createJsonResponseHandler,
19
19
  FetchFunction,
20
20
  generateId,
21
+ isCustomReasoning,
21
22
  isParsableJson,
22
23
  parseProviderOptions,
23
24
  ParseResult,
@@ -25,6 +26,11 @@ import {
25
26
  ResponseHandler,
26
27
  } from '@ai-sdk/provider-utils';
27
28
  import { z } from 'zod/v4';
29
+ import {
30
+ resolveProviderOptionsKey,
31
+ toCamelCase,
32
+ warnIfDeprecatedProviderOptionsKey,
33
+ } from '../utils/to-camel-case';
28
34
  import {
29
35
  defaultOpenAICompatibleErrorStructure,
30
36
  ProviderErrorStructure,
@@ -57,7 +63,7 @@ export type OpenAICompatibleChatConfig = {
57
63
  /**
58
64
  * The supported URLs for the model.
59
65
  */
60
- supportedUrls?: () => LanguageModelV3['supportedUrls'];
66
+ supportedUrls?: () => LanguageModelV4['supportedUrls'];
61
67
 
62
68
  /**
63
69
  * Optional function to transform the request body before sending it to the API.
@@ -67,8 +73,8 @@ export type OpenAICompatibleChatConfig = {
67
73
  transformRequestBody?: (args: Record<string, any>) => Record<string, any>;
68
74
  };
69
75
 
70
- export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
71
- readonly specificationVersion = 'v3';
76
+ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
77
+ readonly specificationVersion = 'v4';
72
78
 
73
79
  readonly supportsStructuredOutputs: boolean;
74
80
 
@@ -119,14 +125,15 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
119
125
  topK,
120
126
  frequencyPenalty,
121
127
  presencePenalty,
128
+ reasoning,
122
129
  providerOptions,
123
130
  stopSequences,
124
131
  responseFormat,
125
132
  seed,
126
133
  toolChoice,
127
134
  tools,
128
- }: LanguageModelV3CallOptions) {
129
- const warnings: SharedV3Warning[] = [];
135
+ }: LanguageModelV4CallOptions) {
136
+ const warnings: SharedV4Warning[] = [];
130
137
 
131
138
  // Parse provider options - check for deprecated 'openai-compatible' key
132
139
  const deprecatedOptions = await parseProviderOptions({
@@ -137,11 +144,19 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
137
144
 
138
145
  if (deprecatedOptions != null) {
139
146
  warnings.push({
140
- type: 'other',
141
- message: `The 'openai-compatible' key in providerOptions is deprecated. Use 'openaiCompatible' instead.`,
147
+ type: 'deprecated',
148
+ setting: "providerOptions key 'openai-compatible'",
149
+ message: "Use 'openaiCompatible' instead.",
142
150
  });
143
151
  }
144
152
 
153
+ // Warn when the raw (non-camelCase) provider name is used
154
+ warnIfDeprecatedProviderOptionsKey({
155
+ rawName: this.providerOptionsName,
156
+ providerOptions,
157
+ warnings,
158
+ });
159
+
145
160
  const compatibleOptions = Object.assign(
146
161
  deprecatedOptions ?? {},
147
162
  (await parseProviderOptions({
@@ -154,6 +169,11 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
154
169
  providerOptions,
155
170
  schema: openaiCompatibleLanguageModelChatOptions,
156
171
  })) ?? {},
172
+ (await parseProviderOptions({
173
+ provider: toCamelCase(this.providerOptionsName),
174
+ providerOptions,
175
+ schema: openaiCompatibleLanguageModelChatOptions,
176
+ })) ?? {},
157
177
  );
158
178
 
159
179
  const strictJsonSchema = compatibleOptions?.strictJsonSchema ?? true;
@@ -184,7 +204,13 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
184
204
  toolChoice,
185
205
  });
186
206
 
207
+ const metadataKey = resolveProviderOptionsKey(
208
+ this.providerOptionsName,
209
+ providerOptions,
210
+ );
211
+
187
212
  return {
213
+ metadataKey,
188
214
  args: {
189
215
  // model id:
190
216
  model: this.modelId,
@@ -217,9 +243,10 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
217
243
  stop: stopSequences,
218
244
  seed,
219
245
  ...Object.fromEntries(
220
- Object.entries(
221
- providerOptions?.[this.providerOptionsName] ?? {},
222
- ).filter(
246
+ Object.entries({
247
+ ...providerOptions?.[this.providerOptionsName],
248
+ ...providerOptions?.[toCamelCase(this.providerOptionsName)],
249
+ }).filter(
223
250
  ([key]) =>
224
251
  !Object.keys(
225
252
  openaiCompatibleLanguageModelChatOptions.shape,
@@ -227,7 +254,11 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
227
254
  ),
228
255
  ),
229
256
 
230
- reasoning_effort: compatibleOptions.reasoningEffort,
257
+ reasoning_effort:
258
+ compatibleOptions.reasoningEffort ??
259
+ (isCustomReasoning(reasoning) && reasoning !== 'none'
260
+ ? reasoning
261
+ : undefined),
231
262
  verbosity: compatibleOptions.textVerbosity,
232
263
 
233
264
  // messages:
@@ -242,9 +273,9 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
242
273
  }
243
274
 
244
275
  async doGenerate(
245
- options: LanguageModelV3CallOptions,
246
- ): Promise<LanguageModelV3GenerateResult> {
247
- const { args, warnings } = await this.getArgs({ ...options });
276
+ options: LanguageModelV4CallOptions,
277
+ ): Promise<LanguageModelV4GenerateResult> {
278
+ const { args, warnings, metadataKey } = await this.getArgs({ ...options });
248
279
 
249
280
  const transformedBody = this.transformRequestBody(args);
250
281
  const body = JSON.stringify(transformedBody);
@@ -269,7 +300,7 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
269
300
  });
270
301
 
271
302
  const choice = responseBody.choices[0];
272
- const content: Array<LanguageModelV3Content> = [];
303
+ const content: Array<LanguageModelV4Content> = [];
273
304
 
274
305
  // text content:
275
306
  const text = choice.message.content;
@@ -300,7 +331,7 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
300
331
  ...(thoughtSignature
301
332
  ? {
302
333
  providerMetadata: {
303
- [this.providerOptionsName]: { thoughtSignature },
334
+ [metadataKey]: { thoughtSignature },
304
335
  },
305
336
  }
306
337
  : {}),
@@ -309,8 +340,8 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
309
340
  }
310
341
 
311
342
  // provider metadata:
312
- const providerMetadata: SharedV3ProviderMetadata = {
313
- [this.providerOptionsName]: {},
343
+ const providerMetadata: SharedV4ProviderMetadata = {
344
+ [metadataKey]: {},
314
345
  ...(await this.config.metadataExtractor?.extractMetadata?.({
315
346
  parsedBody: rawResponse,
316
347
  })),
@@ -318,11 +349,11 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
318
349
  const completionTokenDetails =
319
350
  responseBody.usage?.completion_tokens_details;
320
351
  if (completionTokenDetails?.accepted_prediction_tokens != null) {
321
- providerMetadata[this.providerOptionsName].acceptedPredictionTokens =
352
+ providerMetadata[metadataKey].acceptedPredictionTokens =
322
353
  completionTokenDetails?.accepted_prediction_tokens;
323
354
  }
324
355
  if (completionTokenDetails?.rejected_prediction_tokens != null) {
325
- providerMetadata[this.providerOptionsName].rejectedPredictionTokens =
356
+ providerMetadata[metadataKey].rejectedPredictionTokens =
326
357
  completionTokenDetails?.rejected_prediction_tokens;
327
358
  }
328
359
 
@@ -345,9 +376,11 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
345
376
  }
346
377
 
347
378
  async doStream(
348
- options: LanguageModelV3CallOptions,
349
- ): Promise<LanguageModelV3StreamResult> {
350
- const { args, warnings } = await this.getArgs({ ...options });
379
+ options: LanguageModelV4CallOptions,
380
+ ): Promise<LanguageModelV4StreamResult> {
381
+ const { args, warnings, metadataKey } = await this.getArgs({
382
+ ...options,
383
+ });
351
384
 
352
385
  const body = this.transformRequestBody({
353
386
  ...args,
@@ -388,14 +421,14 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
388
421
  thoughtSignature?: string;
389
422
  }> = [];
390
423
 
391
- let finishReason: LanguageModelV3FinishReason = {
424
+ let finishReason: LanguageModelV4FinishReason = {
392
425
  unified: 'other',
393
426
  raw: undefined,
394
427
  };
395
428
  let usage: z.infer<typeof openaiCompatibleTokenUsageSchema> | undefined =
396
429
  undefined;
397
430
  let isFirstChunk = true;
398
- const providerOptionsName = this.providerOptionsName;
431
+ const providerOptionsName = metadataKey;
399
432
  let isActiveReasoning = false;
400
433
  let isActiveText = false;
401
434
 
@@ -403,7 +436,7 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
403
436
  stream: response.pipeThrough(
404
437
  new TransformStream<
405
438
  ParseResult<z.infer<typeof this.chunkSchema>>,
406
- LanguageModelV3StreamPart
439
+ LanguageModelV4StreamPart
407
440
  >({
408
441
  start(controller) {
409
442
  controller.enqueue({ type: 'stream-start', warnings });
@@ -684,7 +717,7 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
684
717
  });
685
718
  }
686
719
 
687
- const providerMetadata: SharedV3ProviderMetadata = {
720
+ const providerMetadata: SharedV4ProviderMetadata = {
688
721
  [providerOptionsName]: {},
689
722
  ...metadataExtractor?.buildMetadata(),
690
723
  };
@@ -1,4 +1,4 @@
1
- import { SharedV3ProviderMetadata } from '@ai-sdk/provider';
1
+ import { SharedV4ProviderMetadata } from '@ai-sdk/provider';
2
2
 
3
3
  /**
4
4
  * Extracts provider-specific metadata from API responses.
@@ -18,7 +18,7 @@ export type MetadataExtractor = {
18
18
  parsedBody,
19
19
  }: {
20
20
  parsedBody: unknown;
21
- }) => Promise<SharedV3ProviderMetadata | undefined>;
21
+ }) => Promise<SharedV4ProviderMetadata | undefined>;
22
22
 
23
23
  /**
24
24
  * Creates an extractor for handling streaming responses. The returned object provides
@@ -43,6 +43,6 @@ export type MetadataExtractor = {
43
43
  * @returns Provider-specific metadata or undefined if no metadata is available.
44
44
  * The metadata should be under a key indicating the provider id.
45
45
  */
46
- buildMetadata(): SharedV3ProviderMetadata | undefined;
46
+ buildMetadata(): SharedV4ProviderMetadata | undefined;
47
47
  };
48
48
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
- LanguageModelV3CallOptions,
3
- SharedV3Warning,
2
+ LanguageModelV4CallOptions,
3
+ SharedV4Warning,
4
4
  UnsupportedFunctionalityError,
5
5
  } from '@ai-sdk/provider';
6
6
 
@@ -8,8 +8,8 @@ export function prepareTools({
8
8
  tools,
9
9
  toolChoice,
10
10
  }: {
11
- tools: LanguageModelV3CallOptions['tools'];
12
- toolChoice?: LanguageModelV3CallOptions['toolChoice'];
11
+ tools: LanguageModelV4CallOptions['tools'];
12
+ toolChoice?: LanguageModelV4CallOptions['toolChoice'];
13
13
  }): {
14
14
  tools:
15
15
  | undefined
@@ -28,12 +28,12 @@ export function prepareTools({
28
28
  | 'none'
29
29
  | 'required'
30
30
  | undefined;
31
- toolWarnings: SharedV3Warning[];
31
+ toolWarnings: SharedV4Warning[];
32
32
  } {
33
33
  // when the tools array is empty, change it to undefined to prevent errors:
34
34
  tools = tools?.length ? tools : undefined;
35
35
 
36
- const toolWarnings: SharedV3Warning[] = [];
36
+ const toolWarnings: SharedV4Warning[] = [];
37
37
 
38
38
  if (tools == null) {
39
39
  return { tools: undefined, toolChoice: undefined, toolWarnings };
@@ -1,4 +1,4 @@
1
- import { LanguageModelV3Usage } from '@ai-sdk/provider';
1
+ import { LanguageModelV4Usage } from '@ai-sdk/provider';
2
2
 
3
3
  export function convertOpenAICompatibleCompletionUsage(
4
4
  usage:
@@ -8,7 +8,7 @@ export function convertOpenAICompatibleCompletionUsage(
8
8
  }
9
9
  | undefined
10
10
  | null,
11
- ): LanguageModelV3Usage {
11
+ ): LanguageModelV4Usage {
12
12
  if (usage == null) {
13
13
  return {
14
14
  inputTokens: {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  InvalidPromptError,
3
- LanguageModelV3Prompt,
3
+ LanguageModelV4Prompt,
4
4
  UnsupportedFunctionalityError,
5
5
  } from '@ai-sdk/provider';
6
6
 
@@ -9,7 +9,7 @@ export function convertToOpenAICompatibleCompletionPrompt({
9
9
  user = 'user',
10
10
  assistant = 'assistant',
11
11
  }: {
12
- prompt: LanguageModelV3Prompt;
12
+ prompt: LanguageModelV4Prompt;
13
13
  user?: string;
14
14
  assistant?: string;
15
15
  }): {
@@ -1,8 +1,8 @@
1
- import { LanguageModelV3FinishReason } from '@ai-sdk/provider';
1
+ import { LanguageModelV4FinishReason } from '@ai-sdk/provider';
2
2
 
3
3
  export function mapOpenAICompatibleFinishReason(
4
4
  finishReason: string | null | undefined,
5
- ): LanguageModelV3FinishReason['unified'] {
5
+ ): LanguageModelV4FinishReason['unified'] {
6
6
  switch (finishReason) {
7
7
  case 'stop':
8
8
  return 'stop';
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  APICallError,
3
- LanguageModelV3,
4
- LanguageModelV3CallOptions,
5
- LanguageModelV3Content,
6
- LanguageModelV3FinishReason,
7
- LanguageModelV3GenerateResult,
8
- LanguageModelV3StreamPart,
9
- LanguageModelV3StreamResult,
10
- SharedV3Warning,
3
+ LanguageModelV4,
4
+ LanguageModelV4CallOptions,
5
+ LanguageModelV4Content,
6
+ LanguageModelV4FinishReason,
7
+ LanguageModelV4GenerateResult,
8
+ LanguageModelV4StreamPart,
9
+ LanguageModelV4StreamResult,
10
+ SharedV4Warning,
11
11
  } from '@ai-sdk/provider';
12
12
  import {
13
13
  combineHeaders,
@@ -21,6 +21,10 @@ import {
21
21
  ResponseHandler,
22
22
  } from '@ai-sdk/provider-utils';
23
23
  import { z } from 'zod/v4';
24
+ import {
25
+ toCamelCase,
26
+ warnIfDeprecatedProviderOptionsKey,
27
+ } from '../utils/to-camel-case';
24
28
  import {
25
29
  defaultOpenAICompatibleErrorStructure,
26
30
  ProviderErrorStructure,
@@ -45,13 +49,11 @@ type OpenAICompatibleCompletionConfig = {
45
49
  /**
46
50
  * The supported URLs for the model.
47
51
  */
48
- supportedUrls?: () => LanguageModelV3['supportedUrls'];
52
+ supportedUrls?: () => LanguageModelV4['supportedUrls'];
49
53
  };
50
54
 
51
- export class OpenAICompatibleCompletionLanguageModel
52
- implements LanguageModelV3
53
- {
54
- readonly specificationVersion = 'v3';
55
+ export class OpenAICompatibleCompletionLanguageModel implements LanguageModelV4 {
56
+ readonly specificationVersion = 'v4';
55
57
 
56
58
  readonly modelId: OpenAICompatibleCompletionModelId;
57
59
  private readonly config: OpenAICompatibleCompletionConfig;
@@ -100,16 +102,29 @@ export class OpenAICompatibleCompletionLanguageModel
100
102
  providerOptions,
101
103
  tools,
102
104
  toolChoice,
103
- }: LanguageModelV3CallOptions) {
104
- const warnings: SharedV3Warning[] = [];
105
+ }: LanguageModelV4CallOptions) {
106
+ const warnings: SharedV4Warning[] = [];
107
+
108
+ // Warn when the raw (non-camelCase) provider name is used
109
+ warnIfDeprecatedProviderOptionsKey({
110
+ rawName: this.providerOptionsName,
111
+ providerOptions,
112
+ warnings,
113
+ });
105
114
 
106
- // Parse provider options
107
- const completionOptions =
115
+ // Parse provider options (support both raw and camelCase keys)
116
+ const completionOptions = Object.assign(
108
117
  (await parseProviderOptions({
109
118
  provider: this.providerOptionsName,
110
119
  providerOptions,
111
120
  schema: openaiCompatibleLanguageModelCompletionOptions,
112
- })) ?? {};
121
+ })) ?? {},
122
+ (await parseProviderOptions({
123
+ provider: toCamelCase(this.providerOptionsName),
124
+ providerOptions,
125
+ schema: openaiCompatibleLanguageModelCompletionOptions,
126
+ })) ?? {},
127
+ );
113
128
 
114
129
  if (topK != null) {
115
130
  warnings.push({ type: 'unsupported', feature: 'topK' });
@@ -155,6 +170,7 @@ export class OpenAICompatibleCompletionLanguageModel
155
170
  presence_penalty: presencePenalty,
156
171
  seed,
157
172
  ...providerOptions?.[this.providerOptionsName],
173
+ ...providerOptions?.[toCamelCase(this.providerOptionsName)],
158
174
 
159
175
  // prompt:
160
176
  prompt: completionPrompt,
@@ -167,8 +183,8 @@ export class OpenAICompatibleCompletionLanguageModel
167
183
  }
168
184
 
169
185
  async doGenerate(
170
- options: LanguageModelV3CallOptions,
171
- ): Promise<LanguageModelV3GenerateResult> {
186
+ options: LanguageModelV4CallOptions,
187
+ ): Promise<LanguageModelV4GenerateResult> {
172
188
  const { args, warnings } = await this.getArgs(options);
173
189
 
174
190
  const {
@@ -191,7 +207,7 @@ export class OpenAICompatibleCompletionLanguageModel
191
207
  });
192
208
 
193
209
  const choice = response.choices[0];
194
- const content: Array<LanguageModelV3Content> = [];
210
+ const content: Array<LanguageModelV4Content> = [];
195
211
 
196
212
  // text content:
197
213
  if (choice.text != null && choice.text.length > 0) {
@@ -216,8 +232,8 @@ export class OpenAICompatibleCompletionLanguageModel
216
232
  }
217
233
 
218
234
  async doStream(
219
- options: LanguageModelV3CallOptions,
220
- ): Promise<LanguageModelV3StreamResult> {
235
+ options: LanguageModelV4CallOptions,
236
+ ): Promise<LanguageModelV4StreamResult> {
221
237
  const { args, warnings } = await this.getArgs(options);
222
238
 
223
239
  const body = {
@@ -245,7 +261,7 @@ export class OpenAICompatibleCompletionLanguageModel
245
261
  fetch: this.config.fetch,
246
262
  });
247
263
 
248
- let finishReason: LanguageModelV3FinishReason = {
264
+ let finishReason: LanguageModelV4FinishReason = {
249
265
  unified: 'other',
250
266
  raw: undefined,
251
267
  };
@@ -262,7 +278,7 @@ export class OpenAICompatibleCompletionLanguageModel
262
278
  stream: response.pipeThrough(
263
279
  new TransformStream<
264
280
  ParseResult<z.infer<typeof this.chunkSchema>>,
265
- LanguageModelV3StreamPart
281
+ LanguageModelV4StreamPart
266
282
  >({
267
283
  start(controller) {
268
284
  controller.enqueue({ type: 'stream-start', warnings });
@@ -1,6 +1,6 @@
1
1
  import {
2
- EmbeddingModelV3,
3
- SharedV3Warning,
2
+ EmbeddingModelV4,
3
+ SharedV4Warning,
4
4
  TooManyEmbeddingValuesForCallError,
5
5
  } from '@ai-sdk/provider';
6
6
  import {
@@ -20,6 +20,7 @@ import {
20
20
  defaultOpenAICompatibleErrorStructure,
21
21
  ProviderErrorStructure,
22
22
  } from '../openai-compatible-error';
23
+ import { warnIfDeprecatedProviderOptionsKey } from '../utils/to-camel-case';
23
24
 
24
25
  type OpenAICompatibleEmbeddingConfig = {
25
26
  /**
@@ -39,8 +40,8 @@ type OpenAICompatibleEmbeddingConfig = {
39
40
  errorStructure?: ProviderErrorStructure<any>;
40
41
  };
41
42
 
42
- export class OpenAICompatibleEmbeddingModel implements EmbeddingModelV3 {
43
- readonly specificationVersion = 'v3';
43
+ export class OpenAICompatibleEmbeddingModel implements EmbeddingModelV4 {
44
+ readonly specificationVersion = 'v4';
44
45
  readonly modelId: OpenAICompatibleEmbeddingModelId;
45
46
 
46
47
  private readonly config: OpenAICompatibleEmbeddingConfig;
@@ -74,10 +75,10 @@ export class OpenAICompatibleEmbeddingModel implements EmbeddingModelV3 {
74
75
  headers,
75
76
  abortSignal,
76
77
  providerOptions,
77
- }: Parameters<EmbeddingModelV3['doEmbed']>[0]): Promise<
78
- Awaited<ReturnType<EmbeddingModelV3['doEmbed']>>
78
+ }: Parameters<EmbeddingModelV4['doEmbed']>[0]): Promise<
79
+ Awaited<ReturnType<EmbeddingModelV4['doEmbed']>>
79
80
  > {
80
- const warnings: SharedV3Warning[] = [];
81
+ const warnings: SharedV4Warning[] = [];
81
82
 
82
83
  // Parse provider options - check for deprecated 'openai-compatible' key
83
84
  const deprecatedOptions = await parseProviderOptions({
@@ -88,11 +89,19 @@ export class OpenAICompatibleEmbeddingModel implements EmbeddingModelV3 {
88
89
 
89
90
  if (deprecatedOptions != null) {
90
91
  warnings.push({
91
- type: 'other',
92
- message: `The 'openai-compatible' key in providerOptions is deprecated. Use 'openaiCompatible' instead.`,
92
+ type: 'deprecated',
93
+ setting: "providerOptions key 'openai-compatible'",
94
+ message: "Use 'openaiCompatible' instead.",
93
95
  });
94
96
  }
95
97
 
98
+ // Warn when the raw (non-camelCase) provider name is used
99
+ warnIfDeprecatedProviderOptionsKey({
100
+ rawName: this.providerOptionsName,
101
+ providerOptions,
102
+ warnings,
103
+ });
104
+
96
105
  const compatibleOptions = Object.assign(
97
106
  deprecatedOptions ?? {},
98
107
  (await parseProviderOptions({
@@ -1,9 +1,13 @@
1
1
  import {
2
- ImageModelV3,
3
- ImageModelV3File,
4
- SharedV3ProviderOptions,
5
- SharedV3Warning,
2
+ ImageModelV4,
3
+ ImageModelV4File,
4
+ SharedV4ProviderOptions,
5
+ SharedV4Warning,
6
6
  } from '@ai-sdk/provider';
7
+ import {
8
+ toCamelCase,
9
+ warnIfDeprecatedProviderOptionsKey,
10
+ } from '../utils/to-camel-case';
7
11
  import {
8
12
  combineHeaders,
9
13
  convertBase64ToUint8Array,
@@ -33,8 +37,8 @@ export type OpenAICompatibleImageModelConfig = {
33
37
  };
34
38
  };
35
39
 
36
- export class OpenAICompatibleImageModel implements ImageModelV3 {
37
- readonly specificationVersion = 'v3';
40
+ export class OpenAICompatibleImageModel implements ImageModelV4 {
41
+ readonly specificationVersion = 'v4';
38
42
  readonly maxImagesPerCall = 10;
39
43
 
40
44
  get provider(): string {
@@ -53,10 +57,15 @@ export class OpenAICompatibleImageModel implements ImageModelV3 {
53
57
  private readonly config: OpenAICompatibleImageModelConfig,
54
58
  ) {}
55
59
 
56
- // TODO: deprecate non-camelCase keys and remove in future major version
57
60
  private getArgs(
58
- providerOptions: SharedV3ProviderOptions,
61
+ providerOptions: SharedV4ProviderOptions,
62
+ warnings: SharedV4Warning[],
59
63
  ): Record<string, unknown> {
64
+ warnIfDeprecatedProviderOptionsKey({
65
+ rawName: this.providerOptionsKey,
66
+ providerOptions,
67
+ warnings,
68
+ });
60
69
  return {
61
70
  ...providerOptions[this.providerOptionsKey],
62
71
  ...providerOptions[toCamelCase(this.providerOptionsKey)],
@@ -74,10 +83,10 @@ export class OpenAICompatibleImageModel implements ImageModelV3 {
74
83
  abortSignal,
75
84
  files,
76
85
  mask,
77
- }: Parameters<ImageModelV3['doGenerate']>[0]): Promise<
78
- Awaited<ReturnType<ImageModelV3['doGenerate']>>
86
+ }: Parameters<ImageModelV4['doGenerate']>[0]): Promise<
87
+ Awaited<ReturnType<ImageModelV4['doGenerate']>>
79
88
  > {
80
- const warnings: Array<SharedV3Warning> = [];
89
+ const warnings: Array<SharedV4Warning> = [];
81
90
 
82
91
  if (aspectRatio != null) {
83
92
  warnings.push({
@@ -94,7 +103,7 @@ export class OpenAICompatibleImageModel implements ImageModelV3 {
94
103
 
95
104
  const currentDate = this.config._internal?.currentDate?.() ?? new Date();
96
105
 
97
- const args = this.getArgs(providerOptions);
106
+ const args = this.getArgs(providerOptions, warnings);
98
107
 
99
108
  // Image editing mode - use form data and /images/edits endpoint
100
109
  if (files != null && files.length > 0) {
@@ -187,7 +196,7 @@ type OpenAICompatibleFormDataInput = {
187
196
  [key: string]: unknown;
188
197
  };
189
198
 
190
- async function fileToBlob(file: ImageModelV3File): Promise<Blob> {
199
+ async function fileToBlob(file: ImageModelV4File): Promise<Blob> {
191
200
  if (file.type === 'url') {
192
201
  return downloadBlob(file.url);
193
202
  }
@@ -199,7 +208,3 @@ async function fileToBlob(file: ImageModelV3File): Promise<Blob> {
199
208
 
200
209
  return new Blob([data as BlobPart], { type: file.mediaType });
201
210
  }
202
-
203
- function toCamelCase(str: string): string {
204
- return str.replace(/[_-]([a-z])/g, g => g[1].toUpperCase());
205
- }