@ai-sdk/openai-compatible 2.0.86 → 2.0.87

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.
@@ -192,9 +192,9 @@ type OpenAICompatibleChatConfig = {
192
192
  * Optional usage converter for OpenAI-compatible providers with different
193
193
  * token accounting semantics.
194
194
  */
195
- convertUsage?: (usage: z.infer<typeof openaiCompatibleTokenUsageSchema>) => LanguageModelV3Usage;
195
+ convertUsage?: (usage: z.infer<ReturnType<typeof openaiCompatibleTokenUsageSchema>>) => LanguageModelV3Usage;
196
196
  };
197
- declare const openaiCompatibleTokenUsageSchema: z.ZodOptional<z.ZodNullable<z.ZodObject<{
197
+ declare const openaiCompatibleTokenUsageSchema: () => z.ZodOptional<z.ZodNullable<z.ZodObject<{
198
198
  prompt_tokens: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
199
199
  completion_tokens: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
200
200
  total_tokens: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/openai-compatible",
3
- "version": "2.0.86",
3
+ "version": "2.0.87",
4
4
  "license": "Apache-2.0",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -37,7 +37,7 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "@ai-sdk/provider": "3.0.19",
40
- "@ai-sdk/provider-utils": "4.0.61"
40
+ "@ai-sdk/provider-utils": "4.0.62"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "20.17.24",
@@ -13,6 +13,8 @@ import {
13
13
  type SharedV3Warning,
14
14
  } from '@ai-sdk/provider';
15
15
  import {
16
+ lazySchema,
17
+ zodSchema,
16
18
  StreamingToolCallTracker,
17
19
  combineHeaders,
18
20
  createEventSourceResponseHandler,
@@ -88,7 +90,7 @@ export type OpenAICompatibleChatConfig = {
88
90
  * token accounting semantics.
89
91
  */
90
92
  convertUsage?: (
91
- usage: z.infer<typeof openaiCompatibleTokenUsageSchema>,
93
+ usage: z.infer<ReturnType<typeof openaiCompatibleTokenUsageSchema>>,
92
94
  ) => LanguageModelV3Usage;
93
95
  };
94
96
 
@@ -137,7 +139,7 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
137
139
  }
138
140
 
139
141
  private convertUsage(
140
- usage: z.infer<typeof openaiCompatibleTokenUsageSchema>,
142
+ usage: z.infer<ReturnType<typeof openaiCompatibleTokenUsageSchema>>,
141
143
  ): LanguageModelV3Usage {
142
144
  return (
143
145
  this.config.convertUsage?.(usage) ??
@@ -487,13 +489,14 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
487
489
  };
488
490
 
489
491
  let finishReason: LanguageModelV3FinishReason | undefined;
490
- let usage: z.infer<typeof openaiCompatibleTokenUsageSchema> | undefined =
491
- undefined;
492
+ let usage:
493
+ | z.infer<ReturnType<typeof openaiCompatibleTokenUsageSchema>>
494
+ | undefined = undefined;
492
495
  let isFirstChunk = true;
493
496
  let isActiveReasoning = false;
494
497
  let isActiveText = false;
495
498
  const convertUsage = (
496
- usage: z.infer<typeof openaiCompatibleTokenUsageSchema>,
499
+ usage: z.infer<ReturnType<typeof openaiCompatibleTokenUsageSchema>>,
497
500
  ) => this.convertUsage(usage);
498
501
 
499
502
  return {
@@ -549,7 +552,9 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
549
552
 
550
553
  // TODO we lost type safety on Chunk, most likely due to the error schema. MUST FIX
551
554
  // remove this workaround when the issue is fixed
552
- const value = chunk.value as z.infer<typeof chunkBaseSchema>;
555
+ const value = chunk.value as z.infer<
556
+ ReturnType<typeof chunkBaseSchema>
557
+ >;
553
558
 
554
559
  if (isFirstChunk) {
555
560
  isFirstChunk = false;
@@ -721,39 +726,41 @@ export class OpenAICompatibleChatLanguageModel implements LanguageModelV3 {
721
726
  }
722
727
 
723
728
  // Loose, nested objects included: the parsed value is returned as `usage.raw`.
724
- const openaiCompatibleTokenUsageSchema = z
725
- .looseObject({
726
- prompt_tokens: z.number().nullish(),
727
- completion_tokens: z.number().nullish(),
728
- total_tokens: z.number().nullish(),
729
- prompt_tokens_details: z
730
- .looseObject({
731
- cached_tokens: z.number().nullish(),
732
- })
733
- .nullish(),
734
- completion_tokens_details: z
735
- .looseObject({
736
- reasoning_tokens: z.number().nullish(),
737
- accepted_prediction_tokens: z.number().nullish(),
738
- rejected_prediction_tokens: z.number().nullish(),
739
- })
740
- .nullish(),
741
- })
742
- .nullish();
743
-
744
- const openAICompatibleContentSchema = z
745
- .union([
746
- z.string(),
747
- z.array(
748
- z.looseObject({
749
- type: z.string(),
750
- }),
751
- ),
752
- ])
753
- .nullish();
729
+ const openaiCompatibleTokenUsageSchema = () =>
730
+ z
731
+ .looseObject({
732
+ prompt_tokens: z.number().nullish(),
733
+ completion_tokens: z.number().nullish(),
734
+ total_tokens: z.number().nullish(),
735
+ prompt_tokens_details: z
736
+ .looseObject({
737
+ cached_tokens: z.number().nullish(),
738
+ })
739
+ .nullish(),
740
+ completion_tokens_details: z
741
+ .looseObject({
742
+ reasoning_tokens: z.number().nullish(),
743
+ accepted_prediction_tokens: z.number().nullish(),
744
+ rejected_prediction_tokens: z.number().nullish(),
745
+ })
746
+ .nullish(),
747
+ })
748
+ .nullish();
749
+
750
+ const openAICompatibleContentSchema = () =>
751
+ z
752
+ .union([
753
+ z.string(),
754
+ z.array(
755
+ z.looseObject({
756
+ type: z.string(),
757
+ }),
758
+ ),
759
+ ])
760
+ .nullish();
754
761
 
755
762
  function convertOpenAICompatibleContent(
756
- content: z.infer<typeof openAICompatibleContentSchema>,
763
+ content: z.infer<ReturnType<typeof openAICompatibleContentSchema>>,
757
764
  ): Array<Extract<LanguageModelV3Content, { type: 'text' | 'reasoning' }>> {
758
765
  if (content == null) {
759
766
  return [];
@@ -797,88 +804,93 @@ function convertOpenAICompatibleContent(
797
804
 
798
805
  // limited version of the schema, focussed on what is needed for the implementation
799
806
  // this approach limits breakages when the API changes and increases efficiency
800
- const OpenAICompatibleChatResponseSchema = z.looseObject({
801
- id: z.string().nullish(),
802
- created: z.number().nullish(),
803
- model: z.string().nullish(),
804
- choices: z.array(
805
- z.object({
806
- message: z.object({
807
- role: z.literal('assistant').nullish(),
808
- content: openAICompatibleContentSchema,
809
- reasoning_content: z.string().nullish(),
810
- reasoning: z.string().nullish(),
811
- tool_calls: z
812
- .array(
813
- z.object({
814
- id: z.string().nullish(),
815
- function: z.object({
816
- name: z.string(),
817
- arguments: z.string(),
818
- }),
819
- // Support for Google Gemini thought signatures via OpenAI compatibility
820
- extra_content: z
821
- .object({
822
- google: z
807
+ const OpenAICompatibleChatResponseSchema = lazySchema(() =>
808
+ zodSchema(
809
+ z.looseObject({
810
+ id: z.string().nullish(),
811
+ created: z.number().nullish(),
812
+ model: z.string().nullish(),
813
+ choices: z.array(
814
+ z.object({
815
+ message: z.object({
816
+ role: z.literal('assistant').nullish(),
817
+ content: openAICompatibleContentSchema(),
818
+ reasoning_content: z.string().nullish(),
819
+ reasoning: z.string().nullish(),
820
+ tool_calls: z
821
+ .array(
822
+ z.object({
823
+ id: z.string().nullish(),
824
+ function: z.object({
825
+ name: z.string(),
826
+ arguments: z.string(),
827
+ }),
828
+ // Support for Google Gemini thought signatures via OpenAI compatibility
829
+ extra_content: z
823
830
  .object({
824
- thought_signature: z.string().nullish(),
831
+ google: z
832
+ .object({
833
+ thought_signature: z.string().nullish(),
834
+ })
835
+ .nullish(),
825
836
  })
826
837
  .nullish(),
827
- })
828
- .nullish(),
829
- }),
830
- )
831
- .nullish(),
832
- }),
833
- finish_reason: z.string().nullish(),
834
- }),
835
- ),
836
- usage: openaiCompatibleTokenUsageSchema,
837
- });
838
-
839
- const chunkBaseSchema = z.looseObject({
840
- id: z.string().nullish(),
841
- created: z.number().nullish(),
842
- model: z.string().nullish(),
843
- choices: z.array(
844
- z.object({
845
- delta: z
846
- .object({
847
- role: z.enum(['assistant', '']).nullish(),
848
- content: openAICompatibleContentSchema,
849
- // Most openai-compatible models set `reasoning_content`, but some
850
- // providers serving `gpt-oss` set `reasoning`. See #7866
851
- reasoning_content: z.string().nullish(),
852
- reasoning: z.string().nullish(),
853
- tool_calls: z
854
- .array(
855
- z.object({
856
- index: z.number().nullish(), //google does not send index
857
- id: z.string().nullish(),
858
- function: z.object({
859
- name: z.string().nullish(),
860
- arguments: z.string().nullish(),
861
838
  }),
862
- // Support for Google Gemini thought signatures via OpenAI compatibility
863
- extra_content: z
864
- .object({
865
- google: z
866
- .object({
867
- thought_signature: z.string().nullish(),
868
- })
869
- .nullish(),
870
- })
871
- .nullish(),
872
- }),
873
- )
874
- .nullish(),
875
- })
876
- .nullish(),
877
- finish_reason: z.string().nullish(),
839
+ )
840
+ .nullish(),
841
+ }),
842
+ finish_reason: z.string().nullish(),
843
+ }),
844
+ ),
845
+ usage: openaiCompatibleTokenUsageSchema(),
878
846
  }),
879
847
  ),
880
- usage: openaiCompatibleTokenUsageSchema,
881
- });
848
+ );
849
+
850
+ const chunkBaseSchema = () =>
851
+ z.looseObject({
852
+ id: z.string().nullish(),
853
+ created: z.number().nullish(),
854
+ model: z.string().nullish(),
855
+ choices: z.array(
856
+ z.object({
857
+ delta: z
858
+ .object({
859
+ role: z.enum(['assistant', '']).nullish(),
860
+ content: openAICompatibleContentSchema(),
861
+ // Most openai-compatible models set `reasoning_content`, but some
862
+ // providers serving `gpt-oss` set `reasoning`. See #7866
863
+ reasoning_content: z.string().nullish(),
864
+ reasoning: z.string().nullish(),
865
+ tool_calls: z
866
+ .array(
867
+ z.object({
868
+ index: z.number().nullish(), //google does not send index
869
+ id: z.string().nullish(),
870
+ function: z.object({
871
+ name: z.string().nullish(),
872
+ arguments: z.string().nullish(),
873
+ }),
874
+ // Support for Google Gemini thought signatures via OpenAI compatibility
875
+ extra_content: z
876
+ .object({
877
+ google: z
878
+ .object({
879
+ thought_signature: z.string().nullish(),
880
+ })
881
+ .nullish(),
882
+ })
883
+ .nullish(),
884
+ }),
885
+ )
886
+ .nullish(),
887
+ })
888
+ .nullish(),
889
+ finish_reason: z.string().nullish(),
890
+ }),
891
+ ),
892
+ usage: openaiCompatibleTokenUsageSchema(),
893
+ });
882
894
 
883
895
  // limited version of the schema, focussed on what is needed for the implementation
884
896
  // this approach limits breakages when the API changes and increases efficiency
@@ -886,4 +898,4 @@ const createOpenAICompatibleChatChunkSchema = <
886
898
  ERROR_SCHEMA extends z.core.$ZodType,
887
899
  >(
888
900
  errorSchema: ERROR_SCHEMA,
889
- ) => z.union([chunkBaseSchema, errorSchema]);
901
+ ) => z.union([chunkBaseSchema(), errorSchema]);
@@ -10,6 +10,8 @@ import type {
10
10
  SharedV3Warning,
11
11
  } from '@ai-sdk/provider';
12
12
  import {
13
+ lazySchema,
14
+ zodSchema,
13
15
  combineHeaders,
14
16
  createEventSourceResponseHandler,
15
17
  createJsonErrorResponseHandler,
@@ -351,26 +353,31 @@ export class OpenAICompatibleCompletionLanguageModel implements LanguageModelV3
351
353
  }
352
354
 
353
355
  // Loose: the parsed value is returned as `usage.raw`.
354
- const usageSchema = z.looseObject({
355
- prompt_tokens: z.number(),
356
- completion_tokens: z.number(),
357
- total_tokens: z.number(),
358
- });
356
+ const usageSchema = () =>
357
+ z.looseObject({
358
+ prompt_tokens: z.number(),
359
+ completion_tokens: z.number(),
360
+ total_tokens: z.number(),
361
+ });
359
362
 
360
363
  // limited version of the schema, focussed on what is needed for the implementation
361
364
  // this approach limits breakages when the API changes and increases efficiency
362
- const openaiCompatibleCompletionResponseSchema = z.object({
363
- id: z.string().nullish(),
364
- created: z.number().nullish(),
365
- model: z.string().nullish(),
366
- choices: z.array(
365
+ const openaiCompatibleCompletionResponseSchema = lazySchema(() =>
366
+ zodSchema(
367
367
  z.object({
368
- text: z.string(),
369
- finish_reason: z.string(),
368
+ id: z.string().nullish(),
369
+ created: z.number().nullish(),
370
+ model: z.string().nullish(),
371
+ choices: z.array(
372
+ z.object({
373
+ text: z.string(),
374
+ finish_reason: z.string(),
375
+ }),
376
+ ),
377
+ usage: usageSchema().nullish(),
370
378
  }),
371
379
  ),
372
- usage: usageSchema.nullish(),
373
- });
380
+ );
374
381
 
375
382
  // limited version of the schema, focussed on what is needed for the implementation
376
383
  // this approach limits breakages when the API changes and increases efficiency
@@ -391,7 +398,7 @@ const createOpenAICompatibleCompletionChunkSchema = <
391
398
  index: z.number(),
392
399
  }),
393
400
  ),
394
- usage: usageSchema.nullish(),
401
+ usage: usageSchema().nullish(),
395
402
  }),
396
403
  errorSchema,
397
404
  ]);
@@ -1,33 +1,42 @@
1
+ import {
2
+ lazySchema,
3
+ zodSchema,
4
+ type InferSchema,
5
+ } from '@ai-sdk/provider-utils';
1
6
  import { z } from 'zod/v4';
2
7
 
3
8
  export type OpenAICompatibleCompletionModelId = string;
4
9
 
5
- export const openaiCompatibleLanguageModelCompletionOptions = z.object({
6
- /**
7
- * Echo back the prompt in addition to the completion.
8
- */
9
- echo: z.boolean().optional(),
10
+ export const openaiCompatibleLanguageModelCompletionOptions = lazySchema(() =>
11
+ zodSchema(
12
+ z.object({
13
+ /**
14
+ * Echo back the prompt in addition to the completion.
15
+ */
16
+ echo: z.boolean().optional(),
10
17
 
11
- /**
12
- * Modify the likelihood of specified tokens appearing in the completion.
13
- *
14
- * Accepts a JSON object that maps tokens (specified by their token ID in
15
- * the GPT tokenizer) to an associated bias value from -100 to 100.
16
- */
17
- logitBias: z.record(z.string(), z.number()).optional(),
18
+ /**
19
+ * Modify the likelihood of specified tokens appearing in the completion.
20
+ *
21
+ * Accepts a JSON object that maps tokens (specified by their token ID in
22
+ * the GPT tokenizer) to an associated bias value from -100 to 100.
23
+ */
24
+ logitBias: z.record(z.string(), z.number()).optional(),
18
25
 
19
- /**
20
- * The suffix that comes after a completion of inserted text.
21
- */
22
- suffix: z.string().optional(),
26
+ /**
27
+ * The suffix that comes after a completion of inserted text.
28
+ */
29
+ suffix: z.string().optional(),
23
30
 
24
- /**
25
- * A unique identifier representing your end-user, which can help providers to
26
- * monitor and detect abuse.
27
- */
28
- user: z.string().optional(),
29
- });
31
+ /**
32
+ * A unique identifier representing your end-user, which can help providers to
33
+ * monitor and detect abuse.
34
+ */
35
+ user: z.string().optional(),
36
+ }),
37
+ ),
38
+ );
30
39
 
31
- export type OpenAICompatibleLanguageModelCompletionOptions = z.infer<
40
+ export type OpenAICompatibleLanguageModelCompletionOptions = InferSchema<
32
41
  typeof openaiCompatibleLanguageModelCompletionOptions
33
42
  >;
@@ -4,6 +4,8 @@ import {
4
4
  type SharedV3Warning,
5
5
  } from '@ai-sdk/provider';
6
6
  import {
7
+ lazySchema,
8
+ zodSchema,
7
9
  combineHeaders,
8
10
  createJsonErrorResponseHandler,
9
11
  createJsonResponseHandler,
@@ -157,10 +159,14 @@ export class OpenAICompatibleEmbeddingModel implements EmbeddingModelV3 {
157
159
 
158
160
  // minimal version of the schema, focussed on what is needed for the implementation
159
161
  // this approach limits breakages when the API changes and increases efficiency
160
- const openaiTextEmbeddingResponseSchema = z.object({
161
- data: z.array(z.object({ embedding: z.array(z.number()) })),
162
- usage: z.object({ prompt_tokens: z.number() }).nullish(),
163
- providerMetadata: z
164
- .record(z.string(), z.record(z.string(), z.any()))
165
- .optional(),
166
- });
162
+ const openaiTextEmbeddingResponseSchema = lazySchema(() =>
163
+ zodSchema(
164
+ z.object({
165
+ data: z.array(z.object({ embedding: z.array(z.number()) })),
166
+ usage: z.object({ prompt_tokens: z.number() }).nullish(),
167
+ providerMetadata: z
168
+ .record(z.string(), z.record(z.string(), z.any()))
169
+ .optional(),
170
+ }),
171
+ ),
172
+ );
@@ -1,21 +1,30 @@
1
+ import {
2
+ lazySchema,
3
+ zodSchema,
4
+ type InferSchema,
5
+ } from '@ai-sdk/provider-utils';
1
6
  import { z } from 'zod/v4';
2
7
 
3
8
  export type OpenAICompatibleEmbeddingModelId = string;
4
9
 
5
- export const openaiCompatibleEmbeddingModelOptions = z.object({
6
- /**
7
- * The number of dimensions the resulting output embeddings should have.
8
- * Only supported in text-embedding-3 and later models.
9
- */
10
- dimensions: z.number().optional(),
10
+ export const openaiCompatibleEmbeddingModelOptions = lazySchema(() =>
11
+ zodSchema(
12
+ z.object({
13
+ /**
14
+ * The number of dimensions the resulting output embeddings should have.
15
+ * Only supported in text-embedding-3 and later models.
16
+ */
17
+ dimensions: z.number().optional(),
11
18
 
12
- /**
13
- * A unique identifier representing your end-user, which can help providers to
14
- * monitor and detect abuse.
15
- */
16
- user: z.string().optional(),
17
- });
19
+ /**
20
+ * A unique identifier representing your end-user, which can help providers to
21
+ * monitor and detect abuse.
22
+ */
23
+ user: z.string().optional(),
24
+ }),
25
+ ),
26
+ );
18
27
 
19
- export type OpenAICompatibleEmbeddingModelOptions = z.infer<
28
+ export type OpenAICompatibleEmbeddingModelOptions = InferSchema<
20
29
  typeof openaiCompatibleEmbeddingModelOptions
21
30
  >;
@@ -7,6 +7,9 @@ import {
7
7
  } from '@ai-sdk/provider';
8
8
  import { toCamelCase } from '../utils/to-camel-case';
9
9
  import {
10
+ lazySchema,
11
+ zodSchema,
12
+ type InferSchema,
10
13
  combineHeaders,
11
14
  convertBase64ToUint8Array,
12
15
  convertToFormData,
@@ -184,7 +187,7 @@ export class OpenAICompatibleImageModel implements ImageModelV3 {
184
187
  }
185
188
 
186
189
  private async getImages(
187
- data: z.infer<typeof openaiCompatibleImageResponseSchema>['data'],
190
+ data: InferSchema<typeof openaiCompatibleImageResponseSchema>['data'],
188
191
  options: {
189
192
  url: string;
190
193
  headers: Record<string, string | undefined>;
@@ -228,18 +231,22 @@ export class OpenAICompatibleImageModel implements ImageModelV3 {
228
231
 
229
232
  // minimal version of the schema, focussed on what is needed for the implementation
230
233
  // this approach limits breakages when the API changes and increases efficiency
231
- const openaiCompatibleImageResponseSchema = z.object({
232
- data: z.array(
233
- z
234
- .object({
235
- b64_json: z.string().nullish(),
236
- url: z.string().nullish(),
237
- })
238
- .refine(image => image.b64_json != null || image.url != null, {
239
- message: 'Expected b64_json or url in the image response',
240
- }),
234
+ const openaiCompatibleImageResponseSchema = lazySchema(() =>
235
+ zodSchema(
236
+ z.object({
237
+ data: z.array(
238
+ z
239
+ .object({
240
+ b64_json: z.string().nullish(),
241
+ url: z.string().nullish(),
242
+ })
243
+ .refine(image => image.b64_json != null || image.url != null, {
244
+ message: 'Expected b64_json or url in the image response',
245
+ }),
246
+ ),
247
+ }),
241
248
  ),
242
- });
249
+ );
243
250
 
244
251
  type OpenAICompatibleFormDataInput = {
245
252
  model: string;