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

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,7 @@ import {
26
26
  ResponseHandler,
27
27
  } from '@ai-sdk/provider-utils';
28
28
  import { z } from 'zod/v4';
29
+ import { resolveProviderOptionsKey, toCamelCase } from '../utils/to-camel-case';
29
30
  import {
30
31
  defaultOpenAICompatibleErrorStructure,
31
32
  ProviderErrorStructure,
@@ -156,6 +157,11 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
156
157
  providerOptions,
157
158
  schema: openaiCompatibleLanguageModelChatOptions,
158
159
  })) ?? {},
160
+ (await parseProviderOptions({
161
+ provider: toCamelCase(this.providerOptionsName),
162
+ providerOptions,
163
+ schema: openaiCompatibleLanguageModelChatOptions,
164
+ })) ?? {},
159
165
  );
160
166
 
161
167
  const strictJsonSchema = compatibleOptions?.strictJsonSchema ?? true;
@@ -186,7 +192,13 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
186
192
  toolChoice,
187
193
  });
188
194
 
195
+ const metadataKey = resolveProviderOptionsKey(
196
+ this.providerOptionsName,
197
+ providerOptions,
198
+ );
199
+
189
200
  return {
201
+ metadataKey,
190
202
  args: {
191
203
  // model id:
192
204
  model: this.modelId,
@@ -219,9 +231,10 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
219
231
  stop: stopSequences,
220
232
  seed,
221
233
  ...Object.fromEntries(
222
- Object.entries(
223
- providerOptions?.[this.providerOptionsName] ?? {},
224
- ).filter(
234
+ Object.entries({
235
+ ...providerOptions?.[this.providerOptionsName],
236
+ ...providerOptions?.[toCamelCase(this.providerOptionsName)],
237
+ }).filter(
225
238
  ([key]) =>
226
239
  !Object.keys(
227
240
  openaiCompatibleLanguageModelChatOptions.shape,
@@ -250,7 +263,7 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
250
263
  async doGenerate(
251
264
  options: LanguageModelV4CallOptions,
252
265
  ): Promise<LanguageModelV4GenerateResult> {
253
- const { args, warnings } = await this.getArgs({ ...options });
266
+ const { args, warnings, metadataKey } = await this.getArgs({ ...options });
254
267
 
255
268
  const transformedBody = this.transformRequestBody(args);
256
269
  const body = JSON.stringify(transformedBody);
@@ -306,7 +319,7 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
306
319
  ...(thoughtSignature
307
320
  ? {
308
321
  providerMetadata: {
309
- [this.providerOptionsName]: { thoughtSignature },
322
+ [metadataKey]: { thoughtSignature },
310
323
  },
311
324
  }
312
325
  : {}),
@@ -316,7 +329,7 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
316
329
 
317
330
  // provider metadata:
318
331
  const providerMetadata: SharedV4ProviderMetadata = {
319
- [this.providerOptionsName]: {},
332
+ [metadataKey]: {},
320
333
  ...(await this.config.metadataExtractor?.extractMetadata?.({
321
334
  parsedBody: rawResponse,
322
335
  })),
@@ -324,11 +337,11 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
324
337
  const completionTokenDetails =
325
338
  responseBody.usage?.completion_tokens_details;
326
339
  if (completionTokenDetails?.accepted_prediction_tokens != null) {
327
- providerMetadata[this.providerOptionsName].acceptedPredictionTokens =
340
+ providerMetadata[metadataKey].acceptedPredictionTokens =
328
341
  completionTokenDetails?.accepted_prediction_tokens;
329
342
  }
330
343
  if (completionTokenDetails?.rejected_prediction_tokens != null) {
331
- providerMetadata[this.providerOptionsName].rejectedPredictionTokens =
344
+ providerMetadata[metadataKey].rejectedPredictionTokens =
332
345
  completionTokenDetails?.rejected_prediction_tokens;
333
346
  }
334
347
 
@@ -353,7 +366,9 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
353
366
  async doStream(
354
367
  options: LanguageModelV4CallOptions,
355
368
  ): Promise<LanguageModelV4StreamResult> {
356
- const { args, warnings } = await this.getArgs({ ...options });
369
+ const { args, warnings, metadataKey } = await this.getArgs({
370
+ ...options,
371
+ });
357
372
 
358
373
  const body = this.transformRequestBody({
359
374
  ...args,
@@ -401,7 +416,7 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
401
416
  let usage: z.infer<typeof openaiCompatibleTokenUsageSchema> | undefined =
402
417
  undefined;
403
418
  let isFirstChunk = true;
404
- const providerOptionsName = this.providerOptionsName;
419
+ const providerOptionsName = metadataKey;
405
420
  let isActiveReasoning = false;
406
421
  let isActiveText = false;
407
422
 
@@ -21,6 +21,7 @@ import {
21
21
  ResponseHandler,
22
22
  } from '@ai-sdk/provider-utils';
23
23
  import { z } from 'zod/v4';
24
+ import { toCamelCase } from '../utils/to-camel-case';
24
25
  import {
25
26
  defaultOpenAICompatibleErrorStructure,
26
27
  ProviderErrorStructure,
@@ -101,13 +102,19 @@ export class OpenAICompatibleCompletionLanguageModel implements LanguageModelV4
101
102
  }: LanguageModelV4CallOptions) {
102
103
  const warnings: SharedV4Warning[] = [];
103
104
 
104
- // Parse provider options
105
- const completionOptions =
105
+ // Parse provider options (support both raw and camelCase keys)
106
+ const completionOptions = Object.assign(
106
107
  (await parseProviderOptions({
107
108
  provider: this.providerOptionsName,
108
109
  providerOptions,
109
110
  schema: openaiCompatibleLanguageModelCompletionOptions,
110
- })) ?? {};
111
+ })) ?? {},
112
+ (await parseProviderOptions({
113
+ provider: toCamelCase(this.providerOptionsName),
114
+ providerOptions,
115
+ schema: openaiCompatibleLanguageModelCompletionOptions,
116
+ })) ?? {},
117
+ );
111
118
 
112
119
  if (topK != null) {
113
120
  warnings.push({ type: 'unsupported', feature: 'topK' });
@@ -153,6 +160,7 @@ export class OpenAICompatibleCompletionLanguageModel implements LanguageModelV4
153
160
  presence_penalty: presencePenalty,
154
161
  seed,
155
162
  ...providerOptions?.[this.providerOptionsName],
163
+ ...providerOptions?.[toCamelCase(this.providerOptionsName)],
156
164
 
157
165
  // prompt:
158
166
  prompt: completionPrompt,
@@ -4,6 +4,7 @@ import {
4
4
  SharedV4ProviderOptions,
5
5
  SharedV4Warning,
6
6
  } from '@ai-sdk/provider';
7
+ import { toCamelCase } from '../utils/to-camel-case';
7
8
  import {
8
9
  combineHeaders,
9
10
  convertBase64ToUint8Array,
@@ -199,7 +200,3 @@ async function fileToBlob(file: ImageModelV4File): Promise<Blob> {
199
200
 
200
201
  return new Blob([data as BlobPart], { type: file.mediaType });
201
202
  }
202
-
203
- function toCamelCase(str: string): string {
204
- return str.replace(/[_-]([a-z])/g, g => g[1].toUpperCase());
205
- }
@@ -0,0 +1,19 @@
1
+ export function toCamelCase(str: string): string {
2
+ return str.replace(/[_-]([a-z])/g, g => g[1].toUpperCase());
3
+ }
4
+
5
+ /**
6
+ Resolves which key to use for providerMetadata based on what the caller
7
+ passed in providerOptions. Returns the camelCase variant when the caller
8
+ supplied it, otherwise falls back to the raw name.
9
+ */
10
+ export function resolveProviderOptionsKey(
11
+ rawName: string,
12
+ providerOptions: Record<string, unknown> | undefined,
13
+ ): string {
14
+ const camelName = toCamelCase(rawName);
15
+ if (camelName !== rawName && providerOptions?.[camelName] != null) {
16
+ return camelName;
17
+ }
18
+ return rawName;
19
+ }