@ai-sdk/openai-compatible 3.0.0-beta.16 → 3.0.0-beta.19

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.
@@ -26,6 +26,11 @@ import {
26
26
  ResponseHandler,
27
27
  } from '@ai-sdk/provider-utils';
28
28
  import { z } from 'zod/v4';
29
+ import {
30
+ resolveProviderOptionsKey,
31
+ toCamelCase,
32
+ warnIfDeprecatedProviderOptionsKey,
33
+ } from '../utils/to-camel-case';
29
34
  import {
30
35
  defaultOpenAICompatibleErrorStructure,
31
36
  ProviderErrorStructure,
@@ -139,11 +144,19 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
139
144
 
140
145
  if (deprecatedOptions != null) {
141
146
  warnings.push({
142
- type: 'other',
143
- 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.",
144
150
  });
145
151
  }
146
152
 
153
+ // Warn when the raw (non-camelCase) provider name is used
154
+ warnIfDeprecatedProviderOptionsKey({
155
+ rawName: this.providerOptionsName,
156
+ providerOptions,
157
+ warnings,
158
+ });
159
+
147
160
  const compatibleOptions = Object.assign(
148
161
  deprecatedOptions ?? {},
149
162
  (await parseProviderOptions({
@@ -156,6 +169,11 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
156
169
  providerOptions,
157
170
  schema: openaiCompatibleLanguageModelChatOptions,
158
171
  })) ?? {},
172
+ (await parseProviderOptions({
173
+ provider: toCamelCase(this.providerOptionsName),
174
+ providerOptions,
175
+ schema: openaiCompatibleLanguageModelChatOptions,
176
+ })) ?? {},
159
177
  );
160
178
 
161
179
  const strictJsonSchema = compatibleOptions?.strictJsonSchema ?? true;
@@ -186,7 +204,13 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
186
204
  toolChoice,
187
205
  });
188
206
 
207
+ const metadataKey = resolveProviderOptionsKey(
208
+ this.providerOptionsName,
209
+ providerOptions,
210
+ );
211
+
189
212
  return {
213
+ metadataKey,
190
214
  args: {
191
215
  // model id:
192
216
  model: this.modelId,
@@ -219,9 +243,10 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
219
243
  stop: stopSequences,
220
244
  seed,
221
245
  ...Object.fromEntries(
222
- Object.entries(
223
- providerOptions?.[this.providerOptionsName] ?? {},
224
- ).filter(
246
+ Object.entries({
247
+ ...providerOptions?.[this.providerOptionsName],
248
+ ...providerOptions?.[toCamelCase(this.providerOptionsName)],
249
+ }).filter(
225
250
  ([key]) =>
226
251
  !Object.keys(
227
252
  openaiCompatibleLanguageModelChatOptions.shape,
@@ -250,7 +275,7 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
250
275
  async doGenerate(
251
276
  options: LanguageModelV4CallOptions,
252
277
  ): Promise<LanguageModelV4GenerateResult> {
253
- const { args, warnings } = await this.getArgs({ ...options });
278
+ const { args, warnings, metadataKey } = await this.getArgs({ ...options });
254
279
 
255
280
  const transformedBody = this.transformRequestBody(args);
256
281
  const body = JSON.stringify(transformedBody);
@@ -306,7 +331,7 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
306
331
  ...(thoughtSignature
307
332
  ? {
308
333
  providerMetadata: {
309
- [this.providerOptionsName]: { thoughtSignature },
334
+ [metadataKey]: { thoughtSignature },
310
335
  },
311
336
  }
312
337
  : {}),
@@ -316,7 +341,7 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
316
341
 
317
342
  // provider metadata:
318
343
  const providerMetadata: SharedV4ProviderMetadata = {
319
- [this.providerOptionsName]: {},
344
+ [metadataKey]: {},
320
345
  ...(await this.config.metadataExtractor?.extractMetadata?.({
321
346
  parsedBody: rawResponse,
322
347
  })),
@@ -324,11 +349,11 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
324
349
  const completionTokenDetails =
325
350
  responseBody.usage?.completion_tokens_details;
326
351
  if (completionTokenDetails?.accepted_prediction_tokens != null) {
327
- providerMetadata[this.providerOptionsName].acceptedPredictionTokens =
352
+ providerMetadata[metadataKey].acceptedPredictionTokens =
328
353
  completionTokenDetails?.accepted_prediction_tokens;
329
354
  }
330
355
  if (completionTokenDetails?.rejected_prediction_tokens != null) {
331
- providerMetadata[this.providerOptionsName].rejectedPredictionTokens =
356
+ providerMetadata[metadataKey].rejectedPredictionTokens =
332
357
  completionTokenDetails?.rejected_prediction_tokens;
333
358
  }
334
359
 
@@ -353,7 +378,9 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
353
378
  async doStream(
354
379
  options: LanguageModelV4CallOptions,
355
380
  ): Promise<LanguageModelV4StreamResult> {
356
- const { args, warnings } = await this.getArgs({ ...options });
381
+ const { args, warnings, metadataKey } = await this.getArgs({
382
+ ...options,
383
+ });
357
384
 
358
385
  const body = this.transformRequestBody({
359
386
  ...args,
@@ -401,7 +428,7 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
401
428
  let usage: z.infer<typeof openaiCompatibleTokenUsageSchema> | undefined =
402
429
  undefined;
403
430
  let isFirstChunk = true;
404
- const providerOptionsName = this.providerOptionsName;
431
+ const providerOptionsName = metadataKey;
405
432
  let isActiveReasoning = false;
406
433
  let isActiveText = false;
407
434
 
@@ -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,
@@ -101,13 +105,26 @@ export class OpenAICompatibleCompletionLanguageModel implements LanguageModelV4
101
105
  }: LanguageModelV4CallOptions) {
102
106
  const warnings: SharedV4Warning[] = [];
103
107
 
104
- // Parse provider options
105
- const completionOptions =
108
+ // Warn when the raw (non-camelCase) provider name is used
109
+ warnIfDeprecatedProviderOptionsKey({
110
+ rawName: this.providerOptionsName,
111
+ providerOptions,
112
+ warnings,
113
+ });
114
+
115
+ // Parse provider options (support both raw and camelCase keys)
116
+ const completionOptions = Object.assign(
106
117
  (await parseProviderOptions({
107
118
  provider: this.providerOptionsName,
108
119
  providerOptions,
109
120
  schema: openaiCompatibleLanguageModelCompletionOptions,
110
- })) ?? {};
121
+ })) ?? {},
122
+ (await parseProviderOptions({
123
+ provider: toCamelCase(this.providerOptionsName),
124
+ providerOptions,
125
+ schema: openaiCompatibleLanguageModelCompletionOptions,
126
+ })) ?? {},
127
+ );
111
128
 
112
129
  if (topK != null) {
113
130
  warnings.push({ type: 'unsupported', feature: 'topK' });
@@ -153,6 +170,7 @@ export class OpenAICompatibleCompletionLanguageModel implements LanguageModelV4
153
170
  presence_penalty: presencePenalty,
154
171
  seed,
155
172
  ...providerOptions?.[this.providerOptionsName],
173
+ ...providerOptions?.[toCamelCase(this.providerOptionsName)],
156
174
 
157
175
  // prompt:
158
176
  prompt: completionPrompt,
@@ -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
  /**
@@ -88,11 +89,19 @@ export class OpenAICompatibleEmbeddingModel implements EmbeddingModelV4 {
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({
@@ -4,6 +4,10 @@ import {
4
4
  SharedV4ProviderOptions,
5
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,
@@ -53,10 +57,15 @@ export class OpenAICompatibleImageModel implements ImageModelV4 {
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
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)],
@@ -94,7 +103,7 @@ export class OpenAICompatibleImageModel implements ImageModelV4 {
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) {
@@ -199,7 +208,3 @@ async function fileToBlob(file: ImageModelV4File): 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
- }
@@ -0,0 +1,43 @@
1
+ import { SharedV4Warning } from '@ai-sdk/provider';
2
+
3
+ export function toCamelCase(str: string): string {
4
+ return str.replace(/[_-]([a-z])/g, g => g[1].toUpperCase());
5
+ }
6
+
7
+ /**
8
+ Resolves which key to use for providerMetadata based on what the caller
9
+ passed in providerOptions. Returns the camelCase variant when the caller
10
+ supplied it, otherwise falls back to the raw name.
11
+ */
12
+ export function resolveProviderOptionsKey(
13
+ rawName: string,
14
+ providerOptions: Record<string, unknown> | undefined,
15
+ ): string {
16
+ const camelName = toCamelCase(rawName);
17
+ if (camelName !== rawName && providerOptions?.[camelName] != null) {
18
+ return camelName;
19
+ }
20
+ return rawName;
21
+ }
22
+
23
+ /**
24
+ Pushes a deprecation warning when the user supplies providerOptions under a non-camelCase key
25
+ */
26
+ export function warnIfDeprecatedProviderOptionsKey({
27
+ rawName,
28
+ providerOptions,
29
+ warnings,
30
+ }: {
31
+ rawName: string;
32
+ providerOptions: Record<string, unknown> | undefined;
33
+ warnings: SharedV4Warning[];
34
+ }): void {
35
+ const camelName = toCamelCase(rawName);
36
+ if (camelName !== rawName && providerOptions?.[rawName] != null) {
37
+ warnings.push({
38
+ type: 'deprecated',
39
+ setting: `providerOptions key '${rawName}'`,
40
+ message: `Use '${camelName}' instead.`,
41
+ });
42
+ }
43
+ }