@ai-sdk/openai-compatible 3.0.0-beta.3 → 3.0.0-beta.31

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,6 +1,5 @@
1
1
  import {
2
2
  APICallError,
3
- InvalidResponseDataError,
4
3
  LanguageModelV4,
5
4
  LanguageModelV4CallOptions,
6
5
  LanguageModelV4Content,
@@ -18,13 +17,22 @@ import {
18
17
  createJsonResponseHandler,
19
18
  FetchFunction,
20
19
  generateId,
21
- isParsableJson,
20
+ isCustomReasoning,
22
21
  parseProviderOptions,
23
22
  ParseResult,
24
23
  postJsonToApi,
25
24
  ResponseHandler,
25
+ serializeModelOptions,
26
+ StreamingToolCallTracker,
27
+ WORKFLOW_SERIALIZE,
28
+ WORKFLOW_DESERIALIZE,
26
29
  } from '@ai-sdk/provider-utils';
27
30
  import { z } from 'zod/v4';
31
+ import {
32
+ resolveProviderOptionsKey,
33
+ toCamelCase,
34
+ warnIfDeprecatedProviderOptionsKey,
35
+ } from '../utils/to-camel-case';
28
36
  import {
29
37
  defaultOpenAICompatibleErrorStructure,
30
38
  ProviderErrorStructure,
@@ -42,7 +50,7 @@ import { prepareTools } from './openai-compatible-prepare-tools';
42
50
 
43
51
  export type OpenAICompatibleChatConfig = {
44
52
  provider: string;
45
- headers: () => Record<string, string | undefined>;
53
+ headers?: () => Record<string, string | undefined>;
46
54
  url: (options: { modelId: string; path: string }) => string;
47
55
  fetch?: FetchFunction;
48
56
  includeUsage?: boolean;
@@ -73,10 +81,27 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
73
81
  readonly supportsStructuredOutputs: boolean;
74
82
 
75
83
  readonly modelId: OpenAICompatibleChatModelId;
76
- private readonly config: OpenAICompatibleChatConfig;
84
+ protected readonly config: OpenAICompatibleChatConfig;
77
85
  private readonly failedResponseHandler: ResponseHandler<APICallError>;
78
86
  private readonly chunkSchema; // type inferred via constructor
79
87
 
88
+ static [WORKFLOW_SERIALIZE](model: OpenAICompatibleChatLanguageModel) {
89
+ return serializeModelOptions({
90
+ modelId: model.modelId,
91
+ config: model.config,
92
+ });
93
+ }
94
+
95
+ static [WORKFLOW_DESERIALIZE](options: {
96
+ modelId: string;
97
+ config: OpenAICompatibleChatConfig;
98
+ }) {
99
+ return new OpenAICompatibleChatLanguageModel(
100
+ options.modelId,
101
+ options.config,
102
+ );
103
+ }
104
+
80
105
  constructor(
81
106
  modelId: OpenAICompatibleChatModelId,
82
107
  config: OpenAICompatibleChatConfig,
@@ -119,6 +144,7 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
119
144
  topK,
120
145
  frequencyPenalty,
121
146
  presencePenalty,
147
+ reasoning,
122
148
  providerOptions,
123
149
  stopSequences,
124
150
  responseFormat,
@@ -137,11 +163,19 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
137
163
 
138
164
  if (deprecatedOptions != null) {
139
165
  warnings.push({
140
- type: 'other',
141
- message: `The 'openai-compatible' key in providerOptions is deprecated. Use 'openaiCompatible' instead.`,
166
+ type: 'deprecated',
167
+ setting: "providerOptions key 'openai-compatible'",
168
+ message: "Use 'openaiCompatible' instead.",
142
169
  });
143
170
  }
144
171
 
172
+ // Warn when the raw (non-camelCase) provider name is used
173
+ warnIfDeprecatedProviderOptionsKey({
174
+ rawName: this.providerOptionsName,
175
+ providerOptions,
176
+ warnings,
177
+ });
178
+
145
179
  const compatibleOptions = Object.assign(
146
180
  deprecatedOptions ?? {},
147
181
  (await parseProviderOptions({
@@ -154,6 +188,11 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
154
188
  providerOptions,
155
189
  schema: openaiCompatibleLanguageModelChatOptions,
156
190
  })) ?? {},
191
+ (await parseProviderOptions({
192
+ provider: toCamelCase(this.providerOptionsName),
193
+ providerOptions,
194
+ schema: openaiCompatibleLanguageModelChatOptions,
195
+ })) ?? {},
157
196
  );
158
197
 
159
198
  const strictJsonSchema = compatibleOptions?.strictJsonSchema ?? true;
@@ -184,7 +223,13 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
184
223
  toolChoice,
185
224
  });
186
225
 
226
+ const metadataKey = resolveProviderOptionsKey(
227
+ this.providerOptionsName,
228
+ providerOptions,
229
+ );
230
+
187
231
  return {
232
+ metadataKey,
188
233
  args: {
189
234
  // model id:
190
235
  model: this.modelId,
@@ -217,9 +262,10 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
217
262
  stop: stopSequences,
218
263
  seed,
219
264
  ...Object.fromEntries(
220
- Object.entries(
221
- providerOptions?.[this.providerOptionsName] ?? {},
222
- ).filter(
265
+ Object.entries({
266
+ ...providerOptions?.[this.providerOptionsName],
267
+ ...providerOptions?.[toCamelCase(this.providerOptionsName)],
268
+ }).filter(
223
269
  ([key]) =>
224
270
  !Object.keys(
225
271
  openaiCompatibleLanguageModelChatOptions.shape,
@@ -227,7 +273,11 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
227
273
  ),
228
274
  ),
229
275
 
230
- reasoning_effort: compatibleOptions.reasoningEffort,
276
+ reasoning_effort:
277
+ compatibleOptions.reasoningEffort ??
278
+ (isCustomReasoning(reasoning) && reasoning !== 'none'
279
+ ? reasoning
280
+ : undefined),
231
281
  verbosity: compatibleOptions.textVerbosity,
232
282
 
233
283
  // messages:
@@ -244,7 +294,7 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
244
294
  async doGenerate(
245
295
  options: LanguageModelV4CallOptions,
246
296
  ): Promise<LanguageModelV4GenerateResult> {
247
- const { args, warnings } = await this.getArgs({ ...options });
297
+ const { args, warnings, metadataKey } = await this.getArgs({ ...options });
248
298
 
249
299
  const transformedBody = this.transformRequestBody(args);
250
300
  const body = JSON.stringify(transformedBody);
@@ -258,7 +308,7 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
258
308
  path: '/chat/completions',
259
309
  modelId: this.modelId,
260
310
  }),
261
- headers: combineHeaders(this.config.headers(), options.headers),
311
+ headers: combineHeaders(this.config.headers?.(), options.headers),
262
312
  body: transformedBody,
263
313
  failedResponseHandler: this.failedResponseHandler,
264
314
  successfulResponseHandler: createJsonResponseHandler(
@@ -300,7 +350,7 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
300
350
  ...(thoughtSignature
301
351
  ? {
302
352
  providerMetadata: {
303
- [this.providerOptionsName]: { thoughtSignature },
353
+ [metadataKey]: { thoughtSignature },
304
354
  },
305
355
  }
306
356
  : {}),
@@ -310,7 +360,7 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
310
360
 
311
361
  // provider metadata:
312
362
  const providerMetadata: SharedV4ProviderMetadata = {
313
- [this.providerOptionsName]: {},
363
+ [metadataKey]: {},
314
364
  ...(await this.config.metadataExtractor?.extractMetadata?.({
315
365
  parsedBody: rawResponse,
316
366
  })),
@@ -318,11 +368,11 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
318
368
  const completionTokenDetails =
319
369
  responseBody.usage?.completion_tokens_details;
320
370
  if (completionTokenDetails?.accepted_prediction_tokens != null) {
321
- providerMetadata[this.providerOptionsName].acceptedPredictionTokens =
371
+ providerMetadata[metadataKey].acceptedPredictionTokens =
322
372
  completionTokenDetails?.accepted_prediction_tokens;
323
373
  }
324
374
  if (completionTokenDetails?.rejected_prediction_tokens != null) {
325
- providerMetadata[this.providerOptionsName].rejectedPredictionTokens =
375
+ providerMetadata[metadataKey].rejectedPredictionTokens =
326
376
  completionTokenDetails?.rejected_prediction_tokens;
327
377
  }
328
378
 
@@ -347,7 +397,9 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
347
397
  async doStream(
348
398
  options: LanguageModelV4CallOptions,
349
399
  ): Promise<LanguageModelV4StreamResult> {
350
- const { args, warnings } = await this.getArgs({ ...options });
400
+ const { args, warnings, metadataKey } = await this.getArgs({
401
+ ...options,
402
+ });
351
403
 
352
404
  const body = this.transformRequestBody({
353
405
  ...args,
@@ -367,7 +419,7 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
367
419
  path: '/chat/completions',
368
420
  modelId: this.modelId,
369
421
  }),
370
- headers: combineHeaders(this.config.headers(), options.headers),
422
+ headers: combineHeaders(this.config.headers?.(), options.headers),
371
423
  body,
372
424
  failedResponseHandler: this.failedResponseHandler,
373
425
  successfulResponseHandler: createEventSourceResponseHandler(
@@ -377,16 +429,17 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
377
429
  fetch: this.config.fetch,
378
430
  });
379
431
 
380
- const toolCalls: Array<{
381
- id: string;
382
- type: 'function';
383
- function: {
384
- name: string;
385
- arguments: string;
386
- };
387
- hasFinished: boolean;
388
- thoughtSignature?: string;
389
- }> = [];
432
+ const providerOptionsName = metadataKey;
433
+ const toolCallTracker = new StreamingToolCallTracker({
434
+ generateId,
435
+ extractMetadata: delta => {
436
+ const sig = (delta as any).extra_content?.google?.thought_signature;
437
+ return sig
438
+ ? { [providerOptionsName]: { thoughtSignature: sig } }
439
+ : undefined;
440
+ },
441
+ buildToolCallProviderMetadata: metadata => metadata,
442
+ });
390
443
 
391
444
  let finishReason: LanguageModelV4FinishReason = {
392
445
  unified: 'other',
@@ -395,7 +448,6 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
395
448
  let usage: z.infer<typeof openaiCompatibleTokenUsageSchema> | undefined =
396
449
  undefined;
397
450
  let isFirstChunk = true;
398
- const providerOptionsName = this.providerOptionsName;
399
451
  let isActiveReasoning = false;
400
452
  let isActiveText = false;
401
453
 
@@ -517,134 +569,10 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
517
569
  }
518
570
 
519
571
  for (const toolCallDelta of delta.tool_calls) {
520
- const index = toolCallDelta.index ?? toolCalls.length;
521
-
522
- if (toolCalls[index] == null) {
523
- if (toolCallDelta.id == null) {
524
- throw new InvalidResponseDataError({
525
- data: toolCallDelta,
526
- message: `Expected 'id' to be a string.`,
527
- });
528
- }
529
-
530
- if (toolCallDelta.function?.name == null) {
531
- throw new InvalidResponseDataError({
532
- data: toolCallDelta,
533
- message: `Expected 'function.name' to be a string.`,
534
- });
535
- }
536
-
537
- controller.enqueue({
538
- type: 'tool-input-start',
539
- id: toolCallDelta.id,
540
- toolName: toolCallDelta.function.name,
541
- });
542
-
543
- toolCalls[index] = {
544
- id: toolCallDelta.id,
545
- type: 'function',
546
- function: {
547
- name: toolCallDelta.function.name,
548
- arguments: toolCallDelta.function.arguments ?? '',
549
- },
550
- hasFinished: false,
551
- thoughtSignature:
552
- toolCallDelta.extra_content?.google?.thought_signature ??
553
- undefined,
554
- };
555
-
556
- const toolCall = toolCalls[index];
557
-
558
- if (
559
- toolCall.function?.name != null &&
560
- toolCall.function?.arguments != null
561
- ) {
562
- // send delta if the argument text has already started:
563
- if (toolCall.function.arguments.length > 0) {
564
- controller.enqueue({
565
- type: 'tool-input-delta',
566
- id: toolCall.id,
567
- delta: toolCall.function.arguments,
568
- });
569
- }
570
-
571
- // check if tool call is complete
572
- // (some providers send the full tool call in one chunk):
573
- if (isParsableJson(toolCall.function.arguments)) {
574
- controller.enqueue({
575
- type: 'tool-input-end',
576
- id: toolCall.id,
577
- });
578
-
579
- controller.enqueue({
580
- type: 'tool-call',
581
- toolCallId: toolCall.id ?? generateId(),
582
- toolName: toolCall.function.name,
583
- input: toolCall.function.arguments,
584
- ...(toolCall.thoughtSignature
585
- ? {
586
- providerMetadata: {
587
- [providerOptionsName]: {
588
- thoughtSignature: toolCall.thoughtSignature,
589
- },
590
- },
591
- }
592
- : {}),
593
- });
594
- toolCall.hasFinished = true;
595
- }
596
- }
597
-
598
- continue;
599
- }
600
-
601
- // existing tool call, merge if not finished
602
- const toolCall = toolCalls[index];
603
-
604
- if (toolCall.hasFinished) {
605
- continue;
606
- }
607
-
608
- if (toolCallDelta.function?.arguments != null) {
609
- toolCall.function!.arguments +=
610
- toolCallDelta.function?.arguments ?? '';
611
- }
612
-
613
- // send delta
614
- controller.enqueue({
615
- type: 'tool-input-delta',
616
- id: toolCall.id,
617
- delta: toolCallDelta.function.arguments ?? '',
618
- });
619
-
620
- // check if tool call is complete
621
- if (
622
- toolCall.function?.name != null &&
623
- toolCall.function?.arguments != null &&
624
- isParsableJson(toolCall.function.arguments)
625
- ) {
626
- controller.enqueue({
627
- type: 'tool-input-end',
628
- id: toolCall.id,
629
- });
630
-
631
- controller.enqueue({
632
- type: 'tool-call',
633
- toolCallId: toolCall.id ?? generateId(),
634
- toolName: toolCall.function.name,
635
- input: toolCall.function.arguments,
636
- ...(toolCall.thoughtSignature
637
- ? {
638
- providerMetadata: {
639
- [providerOptionsName]: {
640
- thoughtSignature: toolCall.thoughtSignature,
641
- },
642
- },
643
- }
644
- : {}),
645
- });
646
- toolCall.hasFinished = true;
647
- }
572
+ toolCallTracker.processDelta(
573
+ toolCallDelta,
574
+ controller.enqueue.bind(controller),
575
+ );
648
576
  }
649
577
  }
650
578
  },
@@ -658,31 +586,7 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV4 {
658
586
  controller.enqueue({ type: 'text-end', id: 'txt-0' });
659
587
  }
660
588
 
661
- // go through all tool calls and send the ones that are not finished
662
- for (const toolCall of toolCalls.filter(
663
- toolCall => !toolCall.hasFinished,
664
- )) {
665
- controller.enqueue({
666
- type: 'tool-input-end',
667
- id: toolCall.id,
668
- });
669
-
670
- controller.enqueue({
671
- type: 'tool-call',
672
- toolCallId: toolCall.id ?? generateId(),
673
- toolName: toolCall.function.name,
674
- input: toolCall.function.arguments,
675
- ...(toolCall.thoughtSignature
676
- ? {
677
- providerMetadata: {
678
- [providerOptionsName]: {
679
- thoughtSignature: toolCall.thoughtSignature,
680
- },
681
- },
682
- }
683
- : {}),
684
- });
685
- }
589
+ toolCallTracker.flush(controller.enqueue.bind(controller));
686
590
 
687
591
  const providerMetadata: SharedV4ProviderMetadata = {
688
592
  [providerOptionsName]: {},
@@ -19,8 +19,15 @@ import {
19
19
  ParseResult,
20
20
  postJsonToApi,
21
21
  ResponseHandler,
22
+ serializeModelOptions,
23
+ WORKFLOW_SERIALIZE,
24
+ WORKFLOW_DESERIALIZE,
22
25
  } from '@ai-sdk/provider-utils';
23
26
  import { z } from 'zod/v4';
27
+ import {
28
+ toCamelCase,
29
+ warnIfDeprecatedProviderOptionsKey,
30
+ } from '../utils/to-camel-case';
24
31
  import {
25
32
  defaultOpenAICompatibleErrorStructure,
26
33
  ProviderErrorStructure,
@@ -37,7 +44,7 @@ import {
37
44
  type OpenAICompatibleCompletionConfig = {
38
45
  provider: string;
39
46
  includeUsage?: boolean;
40
- headers: () => Record<string, string | undefined>;
47
+ headers?: () => Record<string, string | undefined>;
41
48
  url: (options: { modelId: string; path: string }) => string;
42
49
  fetch?: FetchFunction;
43
50
  errorStructure?: ProviderErrorStructure<any>;
@@ -48,9 +55,7 @@ type OpenAICompatibleCompletionConfig = {
48
55
  supportedUrls?: () => LanguageModelV4['supportedUrls'];
49
56
  };
50
57
 
51
- export class OpenAICompatibleCompletionLanguageModel
52
- implements LanguageModelV4
53
- {
58
+ export class OpenAICompatibleCompletionLanguageModel implements LanguageModelV4 {
54
59
  readonly specificationVersion = 'v4';
55
60
 
56
61
  readonly modelId: OpenAICompatibleCompletionModelId;
@@ -58,6 +63,23 @@ export class OpenAICompatibleCompletionLanguageModel
58
63
  private readonly failedResponseHandler: ResponseHandler<APICallError>;
59
64
  private readonly chunkSchema; // type inferred via constructor
60
65
 
66
+ static [WORKFLOW_SERIALIZE](model: OpenAICompatibleCompletionLanguageModel) {
67
+ return serializeModelOptions({
68
+ modelId: model.modelId,
69
+ config: model.config,
70
+ });
71
+ }
72
+
73
+ static [WORKFLOW_DESERIALIZE](options: {
74
+ modelId: string;
75
+ config: OpenAICompatibleCompletionConfig;
76
+ }) {
77
+ return new OpenAICompatibleCompletionLanguageModel(
78
+ options.modelId,
79
+ options.config,
80
+ );
81
+ }
82
+
61
83
  constructor(
62
84
  modelId: OpenAICompatibleCompletionModelId,
63
85
  config: OpenAICompatibleCompletionConfig,
@@ -103,13 +125,26 @@ export class OpenAICompatibleCompletionLanguageModel
103
125
  }: LanguageModelV4CallOptions) {
104
126
  const warnings: SharedV4Warning[] = [];
105
127
 
106
- // Parse provider options
107
- const completionOptions =
128
+ // Warn when the raw (non-camelCase) provider name is used
129
+ warnIfDeprecatedProviderOptionsKey({
130
+ rawName: this.providerOptionsName,
131
+ providerOptions,
132
+ warnings,
133
+ });
134
+
135
+ // Parse provider options (support both raw and camelCase keys)
136
+ const completionOptions = Object.assign(
108
137
  (await parseProviderOptions({
109
138
  provider: this.providerOptionsName,
110
139
  providerOptions,
111
140
  schema: openaiCompatibleLanguageModelCompletionOptions,
112
- })) ?? {};
141
+ })) ?? {},
142
+ (await parseProviderOptions({
143
+ provider: toCamelCase(this.providerOptionsName),
144
+ providerOptions,
145
+ schema: openaiCompatibleLanguageModelCompletionOptions,
146
+ })) ?? {},
147
+ );
113
148
 
114
149
  if (topK != null) {
115
150
  warnings.push({ type: 'unsupported', feature: 'topK' });
@@ -155,6 +190,7 @@ export class OpenAICompatibleCompletionLanguageModel
155
190
  presence_penalty: presencePenalty,
156
191
  seed,
157
192
  ...providerOptions?.[this.providerOptionsName],
193
+ ...providerOptions?.[toCamelCase(this.providerOptionsName)],
158
194
 
159
195
  // prompt:
160
196
  prompt: completionPrompt,
@@ -180,7 +216,7 @@ export class OpenAICompatibleCompletionLanguageModel
180
216
  path: '/completions',
181
217
  modelId: this.modelId,
182
218
  }),
183
- headers: combineHeaders(this.config.headers(), options.headers),
219
+ headers: combineHeaders(this.config.headers?.(), options.headers),
184
220
  body: args,
185
221
  failedResponseHandler: this.failedResponseHandler,
186
222
  successfulResponseHandler: createJsonResponseHandler(
@@ -235,7 +271,7 @@ export class OpenAICompatibleCompletionLanguageModel
235
271
  path: '/completions',
236
272
  modelId: this.modelId,
237
273
  }),
238
- headers: combineHeaders(this.config.headers(), options.headers),
274
+ headers: combineHeaders(this.config.headers?.(), options.headers),
239
275
  body,
240
276
  failedResponseHandler: this.failedResponseHandler,
241
277
  successfulResponseHandler: createEventSourceResponseHandler(
@@ -10,6 +10,9 @@ import {
10
10
  FetchFunction,
11
11
  parseProviderOptions,
12
12
  postJsonToApi,
13
+ serializeModelOptions,
14
+ WORKFLOW_SERIALIZE,
15
+ WORKFLOW_DESERIALIZE,
13
16
  } from '@ai-sdk/provider-utils';
14
17
  import { z } from 'zod/v4';
15
18
  import {
@@ -20,6 +23,7 @@ import {
20
23
  defaultOpenAICompatibleErrorStructure,
21
24
  ProviderErrorStructure,
22
25
  } from '../openai-compatible-error';
26
+ import { warnIfDeprecatedProviderOptionsKey } from '../utils/to-camel-case';
23
27
 
24
28
  type OpenAICompatibleEmbeddingConfig = {
25
29
  /**
@@ -34,7 +38,7 @@ type OpenAICompatibleEmbeddingConfig = {
34
38
 
35
39
  provider: string;
36
40
  url: (options: { modelId: string; path: string }) => string;
37
- headers: () => Record<string, string | undefined>;
41
+ headers?: () => Record<string, string | undefined>;
38
42
  fetch?: FetchFunction;
39
43
  errorStructure?: ProviderErrorStructure<any>;
40
44
  };
@@ -57,6 +61,20 @@ export class OpenAICompatibleEmbeddingModel implements EmbeddingModelV4 {
57
61
  return this.config.supportsParallelCalls ?? true;
58
62
  }
59
63
 
64
+ static [WORKFLOW_SERIALIZE](model: OpenAICompatibleEmbeddingModel) {
65
+ return serializeModelOptions({
66
+ modelId: model.modelId,
67
+ config: model.config,
68
+ });
69
+ }
70
+
71
+ static [WORKFLOW_DESERIALIZE](options: {
72
+ modelId: string;
73
+ config: OpenAICompatibleEmbeddingConfig;
74
+ }) {
75
+ return new OpenAICompatibleEmbeddingModel(options.modelId, options.config);
76
+ }
77
+
60
78
  constructor(
61
79
  modelId: OpenAICompatibleEmbeddingModelId,
62
80
  config: OpenAICompatibleEmbeddingConfig,
@@ -88,11 +106,19 @@ export class OpenAICompatibleEmbeddingModel implements EmbeddingModelV4 {
88
106
 
89
107
  if (deprecatedOptions != null) {
90
108
  warnings.push({
91
- type: 'other',
92
- message: `The 'openai-compatible' key in providerOptions is deprecated. Use 'openaiCompatible' instead.`,
109
+ type: 'deprecated',
110
+ setting: "providerOptions key 'openai-compatible'",
111
+ message: "Use 'openaiCompatible' instead.",
93
112
  });
94
113
  }
95
114
 
115
+ // Warn when the raw (non-camelCase) provider name is used
116
+ warnIfDeprecatedProviderOptionsKey({
117
+ rawName: this.providerOptionsName,
118
+ providerOptions,
119
+ warnings,
120
+ });
121
+
96
122
  const compatibleOptions = Object.assign(
97
123
  deprecatedOptions ?? {},
98
124
  (await parseProviderOptions({
@@ -125,7 +151,7 @@ export class OpenAICompatibleEmbeddingModel implements EmbeddingModelV4 {
125
151
  path: '/embeddings',
126
152
  modelId: this.modelId,
127
153
  }),
128
- headers: combineHeaders(this.config.headers(), headers),
154
+ headers: combineHeaders(this.config.headers?.(), headers),
129
155
  body: {
130
156
  model: this.modelId,
131
157
  input: values,