@ai-sdk/openai-compatible 2.0.15 → 2.0.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.
Files changed (39) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/index.d.mts +5 -0
  3. package/dist/index.d.ts +5 -0
  4. package/dist/index.js +23 -6
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +23 -6
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +3 -2
  9. package/src/chat/convert-openai-compatible-chat-usage.ts +55 -0
  10. package/src/chat/convert-to-openai-compatible-chat-messages.test.ts +1238 -0
  11. package/src/chat/convert-to-openai-compatible-chat-messages.ts +246 -0
  12. package/src/chat/get-response-metadata.ts +15 -0
  13. package/src/chat/map-openai-compatible-finish-reason.ts +19 -0
  14. package/src/chat/openai-compatible-api-types.ts +86 -0
  15. package/src/chat/openai-compatible-chat-language-model.test.ts +3292 -0
  16. package/src/chat/openai-compatible-chat-language-model.ts +830 -0
  17. package/src/chat/openai-compatible-chat-options.ts +34 -0
  18. package/src/chat/openai-compatible-metadata-extractor.ts +48 -0
  19. package/src/chat/openai-compatible-prepare-tools.test.ts +336 -0
  20. package/src/chat/openai-compatible-prepare-tools.ts +98 -0
  21. package/src/completion/convert-openai-compatible-completion-usage.ts +46 -0
  22. package/src/completion/convert-to-openai-compatible-completion-prompt.ts +93 -0
  23. package/src/completion/get-response-metadata.ts +15 -0
  24. package/src/completion/map-openai-compatible-finish-reason.ts +19 -0
  25. package/src/completion/openai-compatible-completion-language-model.test.ts +773 -0
  26. package/src/completion/openai-compatible-completion-language-model.ts +390 -0
  27. package/src/completion/openai-compatible-completion-options.ts +33 -0
  28. package/src/embedding/openai-compatible-embedding-model.test.ts +171 -0
  29. package/src/embedding/openai-compatible-embedding-model.ts +166 -0
  30. package/src/embedding/openai-compatible-embedding-options.ts +21 -0
  31. package/src/image/openai-compatible-image-model.test.ts +494 -0
  32. package/src/image/openai-compatible-image-model.ts +205 -0
  33. package/src/image/openai-compatible-image-settings.ts +1 -0
  34. package/src/index.ts +27 -0
  35. package/src/internal/index.ts +4 -0
  36. package/src/openai-compatible-error.ts +30 -0
  37. package/src/openai-compatible-provider.test.ts +329 -0
  38. package/src/openai-compatible-provider.ts +189 -0
  39. package/src/version.ts +5 -0
@@ -0,0 +1,205 @@
1
+ import {
2
+ ImageModelV3,
3
+ ImageModelV3File,
4
+ SharedV3ProviderOptions,
5
+ SharedV3Warning,
6
+ } from '@ai-sdk/provider';
7
+ import {
8
+ combineHeaders,
9
+ convertBase64ToUint8Array,
10
+ convertToFormData,
11
+ createJsonErrorResponseHandler,
12
+ createJsonResponseHandler,
13
+ downloadBlob,
14
+ FetchFunction,
15
+ postFormDataToApi,
16
+ postJsonToApi,
17
+ } from '@ai-sdk/provider-utils';
18
+ import { z } from 'zod/v4';
19
+ import {
20
+ defaultOpenAICompatibleErrorStructure,
21
+ ProviderErrorStructure,
22
+ } from '../openai-compatible-error';
23
+ import { OpenAICompatibleImageModelId } from './openai-compatible-image-settings';
24
+
25
+ export type OpenAICompatibleImageModelConfig = {
26
+ provider: string;
27
+ headers: () => Record<string, string | undefined>;
28
+ url: (options: { modelId: string; path: string }) => string;
29
+ fetch?: FetchFunction;
30
+ errorStructure?: ProviderErrorStructure<any>;
31
+ _internal?: {
32
+ currentDate?: () => Date;
33
+ };
34
+ };
35
+
36
+ export class OpenAICompatibleImageModel implements ImageModelV3 {
37
+ readonly specificationVersion = 'v3';
38
+ readonly maxImagesPerCall = 10;
39
+
40
+ get provider(): string {
41
+ return this.config.provider;
42
+ }
43
+
44
+ /**
45
+ * The provider options key used to extract provider-specific options.
46
+ */
47
+ private get providerOptionsKey(): string {
48
+ return this.config.provider.split('.')[0].trim();
49
+ }
50
+
51
+ constructor(
52
+ readonly modelId: OpenAICompatibleImageModelId,
53
+ private readonly config: OpenAICompatibleImageModelConfig,
54
+ ) {}
55
+
56
+ // TODO: deprecate non-camelCase keys and remove in future major version
57
+ private getArgs(
58
+ providerOptions: SharedV3ProviderOptions,
59
+ ): Record<string, unknown> {
60
+ return {
61
+ ...providerOptions[this.providerOptionsKey],
62
+ ...providerOptions[toCamelCase(this.providerOptionsKey)],
63
+ };
64
+ }
65
+
66
+ async doGenerate({
67
+ prompt,
68
+ n,
69
+ size,
70
+ aspectRatio,
71
+ seed,
72
+ providerOptions,
73
+ headers,
74
+ abortSignal,
75
+ files,
76
+ mask,
77
+ }: Parameters<ImageModelV3['doGenerate']>[0]): Promise<
78
+ Awaited<ReturnType<ImageModelV3['doGenerate']>>
79
+ > {
80
+ const warnings: Array<SharedV3Warning> = [];
81
+
82
+ if (aspectRatio != null) {
83
+ warnings.push({
84
+ type: 'unsupported',
85
+ feature: 'aspectRatio',
86
+ details:
87
+ 'This model does not support aspect ratio. Use `size` instead.',
88
+ });
89
+ }
90
+
91
+ if (seed != null) {
92
+ warnings.push({ type: 'unsupported', feature: 'seed' });
93
+ }
94
+
95
+ const currentDate = this.config._internal?.currentDate?.() ?? new Date();
96
+
97
+ const args = this.getArgs(providerOptions);
98
+
99
+ // Image editing mode - use form data and /images/edits endpoint
100
+ if (files != null && files.length > 0) {
101
+ const { value: response, responseHeaders } = await postFormDataToApi({
102
+ url: this.config.url({
103
+ path: '/images/edits',
104
+ modelId: this.modelId,
105
+ }),
106
+ headers: combineHeaders(this.config.headers(), headers),
107
+ formData: convertToFormData<OpenAICompatibleFormDataInput>({
108
+ model: this.modelId,
109
+ prompt,
110
+ image: await Promise.all(files.map(file => fileToBlob(file))),
111
+ mask: mask != null ? await fileToBlob(mask) : undefined,
112
+ n,
113
+ size,
114
+ ...args,
115
+ }),
116
+ failedResponseHandler: createJsonErrorResponseHandler(
117
+ this.config.errorStructure ?? defaultOpenAICompatibleErrorStructure,
118
+ ),
119
+ successfulResponseHandler: createJsonResponseHandler(
120
+ openaiCompatibleImageResponseSchema,
121
+ ),
122
+ abortSignal,
123
+ fetch: this.config.fetch,
124
+ });
125
+
126
+ return {
127
+ images: response.data.map(item => item.b64_json),
128
+ warnings,
129
+ response: {
130
+ timestamp: currentDate,
131
+ modelId: this.modelId,
132
+ headers: responseHeaders,
133
+ },
134
+ };
135
+ }
136
+
137
+ // Standard image generation mode - use JSON and /images/generations endpoint
138
+ const { value: response, responseHeaders } = await postJsonToApi({
139
+ url: this.config.url({
140
+ path: '/images/generations',
141
+ modelId: this.modelId,
142
+ }),
143
+ headers: combineHeaders(this.config.headers(), headers),
144
+ body: {
145
+ model: this.modelId,
146
+ prompt,
147
+ n,
148
+ size,
149
+ ...args,
150
+ response_format: 'b64_json',
151
+ },
152
+ failedResponseHandler: createJsonErrorResponseHandler(
153
+ this.config.errorStructure ?? defaultOpenAICompatibleErrorStructure,
154
+ ),
155
+ successfulResponseHandler: createJsonResponseHandler(
156
+ openaiCompatibleImageResponseSchema,
157
+ ),
158
+ abortSignal,
159
+ fetch: this.config.fetch,
160
+ });
161
+
162
+ return {
163
+ images: response.data.map(item => item.b64_json),
164
+ warnings,
165
+ response: {
166
+ timestamp: currentDate,
167
+ modelId: this.modelId,
168
+ headers: responseHeaders,
169
+ },
170
+ };
171
+ }
172
+ }
173
+
174
+ // minimal version of the schema, focussed on what is needed for the implementation
175
+ // this approach limits breakages when the API changes and increases efficiency
176
+ const openaiCompatibleImageResponseSchema = z.object({
177
+ data: z.array(z.object({ b64_json: z.string() })),
178
+ });
179
+
180
+ type OpenAICompatibleFormDataInput = {
181
+ model: string;
182
+ prompt: string | undefined;
183
+ image: Blob | Blob[];
184
+ mask?: Blob;
185
+ n: number;
186
+ size: `${number}x${number}` | undefined;
187
+ [key: string]: unknown;
188
+ };
189
+
190
+ async function fileToBlob(file: ImageModelV3File): Promise<Blob> {
191
+ if (file.type === 'url') {
192
+ return downloadBlob(file.url);
193
+ }
194
+
195
+ const data =
196
+ file.data instanceof Uint8Array
197
+ ? file.data
198
+ : convertBase64ToUint8Array(file.data);
199
+
200
+ return new Blob([data as BlobPart], { type: file.mediaType });
201
+ }
202
+
203
+ function toCamelCase(str: string): string {
204
+ return str.replace(/[_-]([a-z])/g, g => g[1].toUpperCase());
205
+ }
@@ -0,0 +1 @@
1
+ export type OpenAICompatibleImageModelId = string;
package/src/index.ts ADDED
@@ -0,0 +1,27 @@
1
+ export { OpenAICompatibleChatLanguageModel } from './chat/openai-compatible-chat-language-model';
2
+ export type {
3
+ OpenAICompatibleChatModelId,
4
+ OpenAICompatibleProviderOptions,
5
+ } from './chat/openai-compatible-chat-options';
6
+ export { OpenAICompatibleCompletionLanguageModel } from './completion/openai-compatible-completion-language-model';
7
+ export type {
8
+ OpenAICompatibleCompletionModelId,
9
+ OpenAICompatibleCompletionProviderOptions,
10
+ } from './completion/openai-compatible-completion-options';
11
+ export { OpenAICompatibleEmbeddingModel } from './embedding/openai-compatible-embedding-model';
12
+ export type {
13
+ OpenAICompatibleEmbeddingModelId,
14
+ OpenAICompatibleEmbeddingProviderOptions,
15
+ } from './embedding/openai-compatible-embedding-options';
16
+ export { OpenAICompatibleImageModel } from './image/openai-compatible-image-model';
17
+ export type {
18
+ OpenAICompatibleErrorData,
19
+ ProviderErrorStructure,
20
+ } from './openai-compatible-error';
21
+ export type { MetadataExtractor } from './chat/openai-compatible-metadata-extractor';
22
+ export { createOpenAICompatible } from './openai-compatible-provider';
23
+ export type {
24
+ OpenAICompatibleProvider,
25
+ OpenAICompatibleProviderSettings,
26
+ } from './openai-compatible-provider';
27
+ export { VERSION } from './version';
@@ -0,0 +1,4 @@
1
+ export { convertToOpenAICompatibleChatMessages } from '../chat/convert-to-openai-compatible-chat-messages';
2
+ export { mapOpenAICompatibleFinishReason } from '../chat/map-openai-compatible-finish-reason';
3
+ export { getResponseMetadata } from '../chat/get-response-metadata';
4
+ export type { OpenAICompatibleChatConfig } from '../chat/openai-compatible-chat-language-model';
@@ -0,0 +1,30 @@
1
+ import { z, ZodType } from 'zod/v4';
2
+
3
+ export const openaiCompatibleErrorDataSchema = z.object({
4
+ error: z.object({
5
+ message: z.string(),
6
+
7
+ // The additional information below is handled loosely to support
8
+ // OpenAI-compatible providers that have slightly different error
9
+ // responses:
10
+ type: z.string().nullish(),
11
+ param: z.any().nullish(),
12
+ code: z.union([z.string(), z.number()]).nullish(),
13
+ }),
14
+ });
15
+
16
+ export type OpenAICompatibleErrorData = z.infer<
17
+ typeof openaiCompatibleErrorDataSchema
18
+ >;
19
+
20
+ export type ProviderErrorStructure<T> = {
21
+ errorSchema: ZodType<T>;
22
+ errorToMessage: (error: T) => string;
23
+ isRetryable?: (response: Response, error?: T) => boolean;
24
+ };
25
+
26
+ export const defaultOpenAICompatibleErrorStructure: ProviderErrorStructure<OpenAICompatibleErrorData> =
27
+ {
28
+ errorSchema: openaiCompatibleErrorDataSchema,
29
+ errorToMessage: data => data.error.message,
30
+ };
@@ -0,0 +1,329 @@
1
+ import { createOpenAICompatible } from './openai-compatible-provider';
2
+ import { OpenAICompatibleChatLanguageModel } from './chat/openai-compatible-chat-language-model';
3
+ import { OpenAICompatibleCompletionLanguageModel } from './completion/openai-compatible-completion-language-model';
4
+ import { OpenAICompatibleEmbeddingModel } from './embedding/openai-compatible-embedding-model';
5
+ import { OpenAICompatibleImageModel } from './image/openai-compatible-image-model';
6
+ import { vi, describe, beforeEach, it, expect } from 'vitest';
7
+
8
+ // Mock version
9
+ vi.mock('./version', () => ({
10
+ VERSION: '0.0.0-test',
11
+ }));
12
+
13
+ const OpenAICompatibleChatLanguageModelMock = vi.mocked(
14
+ OpenAICompatibleChatLanguageModel,
15
+ );
16
+ const OpenAICompatibleCompletionLanguageModelMock = vi.mocked(
17
+ OpenAICompatibleCompletionLanguageModel,
18
+ );
19
+ const OpenAICompatibleEmbeddingModelMock = vi.mocked(
20
+ OpenAICompatibleEmbeddingModel,
21
+ );
22
+
23
+ const OpenAICompatibleImageModelMock = vi.mocked(OpenAICompatibleImageModel);
24
+
25
+ vi.mock('./chat/openai-compatible-chat-language-model', () => ({
26
+ OpenAICompatibleChatLanguageModel: vi.fn(),
27
+ }));
28
+
29
+ vi.mock('./image/openai-compatible-image-model', () => ({
30
+ OpenAICompatibleImageModel: vi.fn(),
31
+ }));
32
+
33
+ vi.mock('./completion/openai-compatible-completion-language-model', () => ({
34
+ OpenAICompatibleCompletionLanguageModel: vi.fn(),
35
+ }));
36
+
37
+ vi.mock('./embedding/openai-compatible-embedding-model', () => ({
38
+ OpenAICompatibleEmbeddingModel: vi.fn(),
39
+ }));
40
+
41
+ describe('OpenAICompatibleProvider', () => {
42
+ beforeEach(() => {
43
+ vi.clearAllMocks();
44
+ });
45
+
46
+ describe('createOpenAICompatible', () => {
47
+ it('should create provider with correct configuration', () => {
48
+ const options = {
49
+ baseURL: 'https://api.example.com',
50
+ name: 'test-provider',
51
+ apiKey: 'test-api-key',
52
+ headers: { 'custom-header': 'value' },
53
+ queryParams: { 'Custom-Param': 'value' },
54
+ };
55
+
56
+ const provider = createOpenAICompatible(options);
57
+ provider('model-id');
58
+
59
+ const constructorCall =
60
+ OpenAICompatibleChatLanguageModelMock.mock.calls[0];
61
+ const config = constructorCall[1];
62
+ const headers = config.headers();
63
+
64
+ expect(headers).toEqual({
65
+ authorization: 'Bearer test-api-key',
66
+ 'custom-header': 'value',
67
+ 'user-agent': 'ai-sdk/openai-compatible/0.0.0-test',
68
+ });
69
+ expect(config.provider).toBe('test-provider.chat');
70
+ expect(config.url({ modelId: 'model-id', path: '/v1/chat' })).toBe(
71
+ 'https://api.example.com/v1/chat?Custom-Param=value',
72
+ );
73
+ });
74
+
75
+ it('should create headers without authorization when no apiKey provided', () => {
76
+ const options = {
77
+ baseURL: 'https://api.example.com',
78
+ name: 'test-provider',
79
+ headers: { 'custom-header': 'value' },
80
+ };
81
+
82
+ const provider = createOpenAICompatible(options);
83
+ provider('model-id');
84
+
85
+ const constructorCall =
86
+ OpenAICompatibleChatLanguageModelMock.mock.calls[0];
87
+ const config = constructorCall[1];
88
+ const headers = config.headers();
89
+
90
+ expect(headers).toEqual({
91
+ 'custom-header': 'value',
92
+ 'user-agent': 'ai-sdk/openai-compatible/0.0.0-test',
93
+ });
94
+ });
95
+ });
96
+
97
+ describe('model creation methods', () => {
98
+ const defaultOptions = {
99
+ baseURL: 'https://api.example.com',
100
+ name: 'test-provider',
101
+ apiKey: 'test-api-key',
102
+ headers: { 'custom-header': 'value' },
103
+ queryParams: { 'Custom-Param': 'value' },
104
+ };
105
+
106
+ it('should create chat model with correct configuration', () => {
107
+ const provider = createOpenAICompatible(defaultOptions);
108
+
109
+ provider.chatModel('chat-model');
110
+
111
+ const constructorCall =
112
+ OpenAICompatibleChatLanguageModelMock.mock.calls[0];
113
+ const config = constructorCall[1];
114
+ const headers = config.headers();
115
+
116
+ expect(headers).toEqual({
117
+ authorization: 'Bearer test-api-key',
118
+ 'custom-header': 'value',
119
+ 'user-agent': 'ai-sdk/openai-compatible/0.0.0-test',
120
+ });
121
+ expect(config.provider).toBe('test-provider.chat');
122
+ expect(config.url({ modelId: 'model-id', path: '/v1/chat' })).toBe(
123
+ 'https://api.example.com/v1/chat?Custom-Param=value',
124
+ );
125
+ });
126
+
127
+ it('should create completion model with correct configuration', () => {
128
+ const provider = createOpenAICompatible(defaultOptions);
129
+
130
+ provider.completionModel('completion-model');
131
+
132
+ const constructorCall =
133
+ OpenAICompatibleCompletionLanguageModelMock.mock.calls[0];
134
+ const config = constructorCall[1];
135
+ const headers = config.headers();
136
+
137
+ expect(headers).toEqual({
138
+ authorization: 'Bearer test-api-key',
139
+ 'custom-header': 'value',
140
+ 'user-agent': 'ai-sdk/openai-compatible/0.0.0-test',
141
+ });
142
+ expect(config.provider).toBe('test-provider.completion');
143
+ expect(
144
+ config.url({ modelId: 'completion-model', path: '/v1/completions' }),
145
+ ).toBe('https://api.example.com/v1/completions?Custom-Param=value');
146
+ });
147
+
148
+ it('should create embedding model with correct configuration', () => {
149
+ const provider = createOpenAICompatible(defaultOptions);
150
+
151
+ provider.embeddingModel('embedding-model');
152
+
153
+ const constructorCall = OpenAICompatibleEmbeddingModelMock.mock.calls[0];
154
+ const config = constructorCall[1];
155
+ const headers = config.headers();
156
+
157
+ expect(headers).toEqual({
158
+ authorization: 'Bearer test-api-key',
159
+ 'custom-header': 'value',
160
+ 'user-agent': 'ai-sdk/openai-compatible/0.0.0-test',
161
+ });
162
+ expect(config.provider).toBe('test-provider.embedding');
163
+ expect(
164
+ config.url({ modelId: 'embedding-model', path: '/v1/embeddings' }),
165
+ ).toBe('https://api.example.com/v1/embeddings?Custom-Param=value');
166
+ });
167
+
168
+ it('should use languageModel as default when called as function', () => {
169
+ const provider = createOpenAICompatible(defaultOptions);
170
+
171
+ provider('model-id');
172
+
173
+ expect(OpenAICompatibleChatLanguageModel).toHaveBeenCalledWith(
174
+ 'model-id',
175
+ expect.objectContaining({
176
+ provider: 'test-provider.chat',
177
+ }),
178
+ );
179
+ });
180
+
181
+ it('should create URL without query parameters when queryParams is not specified', () => {
182
+ const options = {
183
+ baseURL: 'https://api.example.com',
184
+ name: 'test-provider',
185
+ apiKey: 'test-api-key',
186
+ };
187
+
188
+ const provider = createOpenAICompatible(options);
189
+ provider('model-id');
190
+
191
+ const constructorCall =
192
+ OpenAICompatibleChatLanguageModelMock.mock.calls[0];
193
+ const config = constructorCall[1];
194
+
195
+ expect(config.url({ modelId: 'model-id', path: '/v1/chat' })).toBe(
196
+ 'https://api.example.com/v1/chat',
197
+ );
198
+ });
199
+ });
200
+
201
+ describe('includeUsage setting', () => {
202
+ it('should pass includeUsage: true to all model types when specified in provider settings', () => {
203
+ const options = {
204
+ baseURL: 'https://api.example.com',
205
+ name: 'test-provider',
206
+ includeUsage: true,
207
+ };
208
+ const provider = createOpenAICompatible(options);
209
+
210
+ provider.chatModel('chat-model');
211
+ expect(
212
+ OpenAICompatibleChatLanguageModelMock.mock.calls[0][1].includeUsage,
213
+ ).toBe(true);
214
+
215
+ provider.completionModel('completion-model');
216
+ expect(
217
+ OpenAICompatibleCompletionLanguageModelMock.mock.calls[0][1]
218
+ .includeUsage,
219
+ ).toBe(true);
220
+
221
+ provider('model-id');
222
+ expect(
223
+ OpenAICompatibleChatLanguageModelMock.mock.calls[1][1].includeUsage,
224
+ ).toBe(true);
225
+ });
226
+
227
+ it('should pass includeUsage: false to all model types when specified in provider settings', () => {
228
+ const options = {
229
+ baseURL: 'https://api.example.com',
230
+ name: 'test-provider',
231
+ includeUsage: false,
232
+ };
233
+ const provider = createOpenAICompatible(options);
234
+
235
+ provider.chatModel('chat-model');
236
+ expect(
237
+ OpenAICompatibleChatLanguageModelMock.mock.calls[0][1].includeUsage,
238
+ ).toBe(false);
239
+
240
+ provider.completionModel('completion-model');
241
+ expect(
242
+ OpenAICompatibleCompletionLanguageModelMock.mock.calls[0][1]
243
+ .includeUsage,
244
+ ).toBe(false);
245
+
246
+ provider('model-id');
247
+ expect(
248
+ OpenAICompatibleChatLanguageModelMock.mock.calls[1][1].includeUsage,
249
+ ).toBe(false);
250
+ });
251
+
252
+ it('should pass includeUsage: undefined to all model types when not specified in provider settings', () => {
253
+ const options = {
254
+ baseURL: 'https://api.example.com',
255
+ name: 'test-provider',
256
+ };
257
+ const provider = createOpenAICompatible(options);
258
+
259
+ provider.chatModel('chat-model');
260
+ expect(
261
+ OpenAICompatibleChatLanguageModelMock.mock.calls[0][1].includeUsage,
262
+ ).toBeUndefined();
263
+
264
+ provider.completionModel('completion-model');
265
+ expect(
266
+ OpenAICompatibleCompletionLanguageModelMock.mock.calls[0][1]
267
+ .includeUsage,
268
+ ).toBeUndefined();
269
+
270
+ provider('model-id');
271
+ expect(
272
+ OpenAICompatibleChatLanguageModelMock.mock.calls[1][1].includeUsage,
273
+ ).toBeUndefined();
274
+ });
275
+ });
276
+
277
+ describe('supportsStructuredOutputs setting', () => {
278
+ it('should pass supportsStructuredOutputs to to .chatModel() and .languageModel() only', () => {
279
+ const options = {
280
+ baseURL: 'https://api.example.com',
281
+ name: 'test-provider',
282
+ supportsStructuredOutputs: true,
283
+ };
284
+ const provider = createOpenAICompatible(options);
285
+
286
+ provider('model-id');
287
+ expect(
288
+ OpenAICompatibleChatLanguageModelMock.mock.calls[0][1]
289
+ .supportsStructuredOutputs,
290
+ ).toBe(true);
291
+
292
+ provider.chatModel('chat-model');
293
+ expect(
294
+ OpenAICompatibleChatLanguageModelMock.mock.calls[1][1]
295
+ .supportsStructuredOutputs,
296
+ ).toBe(true);
297
+
298
+ provider.languageModel('completion-model');
299
+ expect(
300
+ OpenAICompatibleChatLanguageModelMock.mock.calls[2][1]
301
+ .supportsStructuredOutputs,
302
+ ).toBe(true);
303
+
304
+ provider.completionModel('completion-model');
305
+ const completionModelConfigArg =
306
+ OpenAICompatibleCompletionLanguageModelMock.mock.calls[0][1];
307
+ expect(
308
+ // @ts-expect-error - testing
309
+ completionModelConfigArg.supportsStructuredOutputs,
310
+ ).toBe(undefined);
311
+
312
+ provider.embeddingModel('embedding-model');
313
+ const embeddingModelConfigArg =
314
+ OpenAICompatibleEmbeddingModelMock.mock.calls[0][1];
315
+ expect(
316
+ // @ts-expect-error - testing
317
+ embeddingModelConfigArg.supportsStructuredOutputs,
318
+ ).toBe(undefined);
319
+
320
+ provider.imageModel('image-model');
321
+ const imageModelConfigArg =
322
+ OpenAICompatibleImageModelMock.mock.calls[0][1];
323
+ expect(
324
+ // @ts-expect-error - testing
325
+ imageModelConfigArg.supportsStructuredOutputs,
326
+ ).toBe(undefined);
327
+ });
328
+ });
329
+ });