@ai-sdk/openai-compatible 2.0.39 → 2.0.41

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.
@@ -25,6 +25,7 @@ import {
25
25
  ResponseHandler,
26
26
  } from '@ai-sdk/provider-utils';
27
27
  import { z } from 'zod/v4';
28
+ import { resolveProviderOptionsKey, toCamelCase } from '../utils/to-camel-case';
28
29
  import {
29
30
  defaultOpenAICompatibleErrorStructure,
30
31
  ProviderErrorStructure,
@@ -154,6 +155,11 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
154
155
  providerOptions,
155
156
  schema: openaiCompatibleLanguageModelChatOptions,
156
157
  })) ?? {},
158
+ (await parseProviderOptions({
159
+ provider: toCamelCase(this.providerOptionsName),
160
+ providerOptions,
161
+ schema: openaiCompatibleLanguageModelChatOptions,
162
+ })) ?? {},
157
163
  );
158
164
 
159
165
  const strictJsonSchema = compatibleOptions?.strictJsonSchema ?? true;
@@ -184,7 +190,13 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
184
190
  toolChoice,
185
191
  });
186
192
 
193
+ const metadataKey = resolveProviderOptionsKey(
194
+ this.providerOptionsName,
195
+ providerOptions,
196
+ );
197
+
187
198
  return {
199
+ metadataKey,
188
200
  args: {
189
201
  // model id:
190
202
  model: this.modelId,
@@ -217,9 +229,10 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
217
229
  stop: stopSequences,
218
230
  seed,
219
231
  ...Object.fromEntries(
220
- Object.entries(
221
- providerOptions?.[this.providerOptionsName] ?? {},
222
- ).filter(
232
+ Object.entries({
233
+ ...providerOptions?.[this.providerOptionsName],
234
+ ...providerOptions?.[toCamelCase(this.providerOptionsName)],
235
+ }).filter(
223
236
  ([key]) =>
224
237
  !Object.keys(
225
238
  openaiCompatibleLanguageModelChatOptions.shape,
@@ -244,7 +257,7 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
244
257
  async doGenerate(
245
258
  options: LanguageModelV3CallOptions,
246
259
  ): Promise<LanguageModelV3GenerateResult> {
247
- const { args, warnings } = await this.getArgs({ ...options });
260
+ const { args, warnings, metadataKey } = await this.getArgs({ ...options });
248
261
 
249
262
  const transformedBody = this.transformRequestBody(args);
250
263
  const body = JSON.stringify(transformedBody);
@@ -300,7 +313,7 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
300
313
  ...(thoughtSignature
301
314
  ? {
302
315
  providerMetadata: {
303
- [this.providerOptionsName]: { thoughtSignature },
316
+ [metadataKey]: { thoughtSignature },
304
317
  },
305
318
  }
306
319
  : {}),
@@ -310,7 +323,7 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
310
323
 
311
324
  // provider metadata:
312
325
  const providerMetadata: SharedV3ProviderMetadata = {
313
- [this.providerOptionsName]: {},
326
+ [metadataKey]: {},
314
327
  ...(await this.config.metadataExtractor?.extractMetadata?.({
315
328
  parsedBody: rawResponse,
316
329
  })),
@@ -318,11 +331,11 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
318
331
  const completionTokenDetails =
319
332
  responseBody.usage?.completion_tokens_details;
320
333
  if (completionTokenDetails?.accepted_prediction_tokens != null) {
321
- providerMetadata[this.providerOptionsName].acceptedPredictionTokens =
334
+ providerMetadata[metadataKey].acceptedPredictionTokens =
322
335
  completionTokenDetails?.accepted_prediction_tokens;
323
336
  }
324
337
  if (completionTokenDetails?.rejected_prediction_tokens != null) {
325
- providerMetadata[this.providerOptionsName].rejectedPredictionTokens =
338
+ providerMetadata[metadataKey].rejectedPredictionTokens =
326
339
  completionTokenDetails?.rejected_prediction_tokens;
327
340
  }
328
341
 
@@ -347,7 +360,7 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
347
360
  async doStream(
348
361
  options: LanguageModelV3CallOptions,
349
362
  ): Promise<LanguageModelV3StreamResult> {
350
- const { args, warnings } = await this.getArgs({ ...options });
363
+ const { args, warnings, metadataKey } = await this.getArgs({ ...options });
351
364
 
352
365
  const body = this.transformRequestBody({
353
366
  ...args,
@@ -395,7 +408,7 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
395
408
  let usage: z.infer<typeof openaiCompatibleTokenUsageSchema> | undefined =
396
409
  undefined;
397
410
  let isFirstChunk = true;
398
- const providerOptionsName = this.providerOptionsName;
411
+ const providerOptionsName = metadataKey;
399
412
  let isActiveReasoning = false;
400
413
  let isActiveText = false;
401
414
 
@@ -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 LanguageModelV3
101
102
  }: LanguageModelV3CallOptions) {
102
103
  const warnings: SharedV3Warning[] = [];
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 LanguageModelV3
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
  SharedV3ProviderOptions,
5
5
  SharedV3Warning,
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: ImageModelV3File): 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
+ }