@bernierllc/ai-provider-openai 1.0.0 → 1.0.1

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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ # @bernierllc/ai-provider-openai
2
+
3
+ ## 1.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [[`24899fa`](https://github.com/bernierllc/tools/commit/24899fa4eaf80ae329f7bd74483cf97a132f5d4c)]:
8
+ - @bernierllc/logger@1.0.4
9
+ - @bernierllc/ai-provider-core@1.0.2
@@ -27,24 +27,30 @@ describe('OpenAIProvider', () => {
27
27
  beforeEach(() => {
28
28
  jest.clearAllMocks();
29
29
 
30
- // Setup mock OpenAI client
30
+ // Setup mock OpenAI client with properly typed Jest mocks
31
+ const mockChatCompletionsCreate = jest.fn() as jest.Mock;
32
+ const mockEmbeddingsCreate = jest.fn() as jest.Mock;
33
+ const mockModerationsCreate = jest.fn() as jest.Mock;
34
+ const mockModelsList = jest.fn() as jest.Mock;
35
+ const mockModelsRetrieve = jest.fn() as jest.Mock;
36
+
31
37
  mockClient = {
32
38
  chat: {
33
39
  completions: {
34
- create: jest.fn() as any
40
+ create: mockChatCompletionsCreate as any
35
41
  }
36
42
  },
37
43
  embeddings: {
38
- create: jest.fn() as any
44
+ create: mockEmbeddingsCreate as any
39
45
  },
40
46
  moderations: {
41
- create: jest.fn() as any
47
+ create: mockModerationsCreate as any
42
48
  },
43
49
  models: {
44
- list: jest.fn() as any,
45
- retrieve: jest.fn() as any
50
+ list: mockModelsList as any,
51
+ retrieve: mockModelsRetrieve as any
46
52
  }
47
- } as any;
53
+ } as unknown as jest.Mocked<OpenAI>;
48
54
 
49
55
  (OpenAI as jest.MockedClass<typeof OpenAI>).mockImplementation(() => mockClient);
50
56
 
@@ -110,7 +116,7 @@ describe('OpenAIProvider', () => {
110
116
  }
111
117
  };
112
118
 
113
- mockClient.chat.completions.create.mockResolvedValue(mockResponse as any);
119
+ (mockClient.chat.completions.create as jest.Mock).mockResolvedValue(mockResponse as any);
114
120
 
115
121
  const result = await provider.complete({
116
122
  messages: [
@@ -145,7 +151,7 @@ describe('OpenAIProvider', () => {
145
151
  ]
146
152
  };
147
153
 
148
- mockClient.chat.completions.create.mockResolvedValue(mockResponse as any);
154
+ (mockClient.chat.completions.create as jest.Mock).mockResolvedValue(mockResponse as any);
149
155
 
150
156
  const result = await provider.complete({
151
157
  messages: [{ role: 'user', content: 'Hello' }]
@@ -156,7 +162,7 @@ describe('OpenAIProvider', () => {
156
162
  });
157
163
 
158
164
  it('should pass all request parameters to OpenAI', async () => {
159
- mockClient.chat.completions.create.mockResolvedValue({
165
+ (mockClient.chat.completions.create as jest.Mock).mockResolvedValue({
160
166
  choices: [{ message: { content: 'test' }, finish_reason: 'stop' }]
161
167
  } as any);
162
168
 
@@ -186,7 +192,7 @@ describe('OpenAIProvider', () => {
186
192
  });
187
193
 
188
194
  it('should use default model if not specified', async () => {
189
- mockClient.chat.completions.create.mockResolvedValue({
195
+ (mockClient.chat.completions.create as jest.Mock).mockResolvedValue({
190
196
  choices: [{ message: { content: 'test' }, finish_reason: 'stop' }]
191
197
  } as any);
192
198
 
@@ -225,7 +231,7 @@ describe('OpenAIProvider', () => {
225
231
  {}
226
232
  );
227
233
 
228
- mockClient.chat.completions.create.mockRejectedValue(apiError);
234
+ (mockClient.chat.completions.create as jest.Mock).mockRejectedValue(apiError);
229
235
 
230
236
  const result = await provider.complete({
231
237
  messages: [{ role: 'user', content: 'Test' }]
@@ -264,7 +270,7 @@ describe('OpenAIProvider', () => {
264
270
  }
265
271
  ];
266
272
 
267
- mockClient.chat.completions.create.mockResolvedValue({
273
+ (mockClient.chat.completions.create as jest.Mock).mockResolvedValue({
268
274
  async *[Symbol.asyncIterator]() {
269
275
  for (const chunk of mockChunks) {
270
276
  yield chunk;
@@ -297,7 +303,7 @@ describe('OpenAIProvider', () => {
297
303
  { choices: [{ delta: { content: 'test' }, finish_reason: 'stop' }] }
298
304
  ];
299
305
 
300
- mockClient.chat.completions.create.mockResolvedValue({
306
+ (mockClient.chat.completions.create as jest.Mock).mockResolvedValue({
301
307
  async *[Symbol.asyncIterator]() {
302
308
  for (const chunk of mockChunks) {
303
309
  yield chunk;
@@ -339,7 +345,7 @@ describe('OpenAIProvider', () => {
339
345
  }
340
346
  };
341
347
 
342
- mockClient.embeddings.create.mockResolvedValue(mockResponse as any);
348
+ (mockClient.embeddings.create as jest.Mock).mockResolvedValue(mockResponse as any);
343
349
 
344
350
  const result = await provider.generateEmbeddings({
345
351
  input: ['text 1', 'text 2']
@@ -358,7 +364,7 @@ describe('OpenAIProvider', () => {
358
364
  });
359
365
 
360
366
  it('should use default embedding model if not specified', async () => {
361
- mockClient.embeddings.create.mockResolvedValue({
367
+ (mockClient.embeddings.create as jest.Mock).mockResolvedValue({
362
368
  data: [{ embedding: [1, 2, 3] }],
363
369
  usage: { prompt_tokens: 5, total_tokens: 5 }
364
370
  } as any);
@@ -375,7 +381,7 @@ describe('OpenAIProvider', () => {
375
381
  });
376
382
 
377
383
  it('should handle API errors', async () => {
378
- mockClient.embeddings.create.mockRejectedValue(new Error('API Error'));
384
+ (mockClient.embeddings.create as jest.Mock).mockRejectedValue(new Error('API Error'));
379
385
 
380
386
  const result = await provider.generateEmbeddings({
381
387
  input: 'test'
@@ -422,7 +428,7 @@ describe('OpenAIProvider', () => {
422
428
  ]
423
429
  };
424
430
 
425
- mockClient.moderations.create.mockResolvedValue(mockResponse as any);
431
+ (mockClient.moderations.create as jest.Mock).mockResolvedValue(mockResponse as any);
426
432
 
427
433
  const result = await provider.moderate('test content');
428
434
 
@@ -443,7 +449,7 @@ describe('OpenAIProvider', () => {
443
449
  ]
444
450
  };
445
451
 
446
- mockClient.moderations.create.mockResolvedValue(mockResponse as any);
452
+ (mockClient.moderations.create as jest.Mock).mockResolvedValue(mockResponse as any);
447
453
 
448
454
  const result = await provider.moderate('clean content');
449
455
 
@@ -452,7 +458,7 @@ describe('OpenAIProvider', () => {
452
458
  });
453
459
 
454
460
  it('should handle API errors', async () => {
455
- mockClient.moderations.create.mockRejectedValue(new Error('API Error'));
461
+ (mockClient.moderations.create as jest.Mock).mockRejectedValue(new Error('API Error'));
456
462
 
457
463
  const result = await provider.moderate('test');
458
464
 
@@ -474,7 +480,7 @@ describe('OpenAIProvider', () => {
474
480
  ]
475
481
  };
476
482
 
477
- mockClient.models.list.mockResolvedValue(mockResponse as any);
483
+ (mockClient.models.list as jest.Mock).mockResolvedValue(mockResponse as any);
478
484
 
479
485
  const models = await provider.getAvailableModels();
480
486
 
@@ -486,7 +492,7 @@ describe('OpenAIProvider', () => {
486
492
  });
487
493
 
488
494
  it('should return cached models if API fails', async () => {
489
- mockClient.models.list.mockRejectedValue(new Error('API Error'));
495
+ (mockClient.models.list as jest.Mock).mockRejectedValue(new Error('API Error'));
490
496
 
491
497
  const models = await provider.getAvailableModels();
492
498
 
@@ -497,7 +503,7 @@ describe('OpenAIProvider', () => {
497
503
 
498
504
  describe('checkHealth()', () => {
499
505
  it('should return healthy status when API is accessible', async () => {
500
- mockClient.models.retrieve.mockResolvedValue({ id: 'gpt-3.5-turbo' } as any);
506
+ (mockClient.models.retrieve as jest.Mock).mockResolvedValue({ id: 'gpt-3.5-turbo' } as any);
501
507
 
502
508
  const health = await provider.checkHealth();
503
509
 
@@ -507,7 +513,7 @@ describe('OpenAIProvider', () => {
507
513
  });
508
514
 
509
515
  it('should return unavailable status when API fails', async () => {
510
- mockClient.models.retrieve.mockRejectedValue(new Error('Connection failed'));
516
+ (mockClient.models.retrieve as jest.Mock).mockRejectedValue(new Error('Connection failed'));
511
517
 
512
518
  const health = await provider.checkHealth();
513
519
 
@@ -519,7 +525,7 @@ describe('OpenAIProvider', () => {
519
525
 
520
526
  describe('isAvailable()', () => {
521
527
  it('should return true when provider is healthy', async () => {
522
- mockClient.models.retrieve.mockResolvedValue({ id: 'gpt-3.5-turbo' } as any);
528
+ (mockClient.models.retrieve as jest.Mock).mockResolvedValue({ id: 'gpt-3.5-turbo' } as any);
523
529
 
524
530
  const available = await provider.isAvailable();
525
531
 
@@ -527,7 +533,7 @@ describe('OpenAIProvider', () => {
527
533
  });
528
534
 
529
535
  it('should return false when provider is unavailable', async () => {
530
- mockClient.models.retrieve.mockRejectedValue(new Error('API Error'));
536
+ (mockClient.models.retrieve as jest.Mock).mockRejectedValue(new Error('API Error'));
531
537
 
532
538
  const available = await provider.isAvailable();
533
539
 
@@ -25,13 +25,16 @@ describe('OpenAI-Specific Features', () => {
25
25
  beforeEach(() => {
26
26
  jest.clearAllMocks();
27
27
 
28
+ // Setup mock OpenAI client with properly typed Jest mocks
29
+ const mockChatCompletionsCreate = jest.fn() as jest.Mock;
30
+
28
31
  mockClient = {
29
32
  chat: {
30
33
  completions: {
31
- create: jest.fn() as any
34
+ create: mockChatCompletionsCreate as any
32
35
  }
33
36
  }
34
- } as any;
37
+ } as unknown as jest.Mocked<OpenAI>;
35
38
 
36
39
  (OpenAI as jest.MockedClass<typeof OpenAI>).mockImplementation(() => mockClient);
37
40
 
@@ -79,7 +82,7 @@ describe('OpenAI-Specific Features', () => {
79
82
  }
80
83
  };
81
84
 
82
- mockClient.chat.completions.create.mockResolvedValue(mockResponse as any);
85
+ (mockClient.chat.completions.create as jest.Mock).mockResolvedValue(mockResponse as any);
83
86
 
84
87
  const result = await provider.completionWithFunctions({
85
88
  messages: [{ role: 'user', content: 'What is the weather in San Francisco?' }],
@@ -132,7 +135,7 @@ describe('OpenAI-Specific Features', () => {
132
135
  }
133
136
  };
134
137
 
135
- mockClient.chat.completions.create.mockResolvedValue(mockResponse as any);
138
+ (mockClient.chat.completions.create as jest.Mock).mockResolvedValue(mockResponse as any);
136
139
 
137
140
  const result = await provider.completionWithFunctions({
138
141
  messages: [{ role: 'user', content: 'Hello' }],
@@ -185,7 +188,8 @@ describe('OpenAI-Specific Features', () => {
185
188
  });
186
189
 
187
190
  expect(result.success).toBe(true);
188
- expect(result.metadata?.functionCall?.name).toBe('get_time');
191
+ expect(result.metadata?.functionCall).toBeDefined();
192
+ expect((result.metadata?.functionCall as any)?.name).toBe('get_time');
189
193
  });
190
194
 
191
195
  it('should handle API errors', async () => {
@@ -200,7 +204,7 @@ describe('OpenAI-Specific Features', () => {
200
204
  }
201
205
  ];
202
206
 
203
- mockClient.chat.completions.create.mockRejectedValue(new Error('API Error'));
207
+ (mockClient.chat.completions.create as jest.Mock).mockRejectedValue(new Error('API Error'));
204
208
 
205
209
  const result = await provider.completionWithFunctions({
206
210
  messages: [{ role: 'user', content: 'Test' }],
@@ -232,7 +236,7 @@ describe('OpenAI-Specific Features', () => {
232
236
  }
233
237
  };
234
238
 
235
- mockClient.chat.completions.create.mockResolvedValue(mockResponse as any);
239
+ (mockClient.chat.completions.create as jest.Mock).mockResolvedValue(mockResponse as any);
236
240
 
237
241
  const result = await provider.analyzeImage(
238
242
  'https://example.com/sunset.jpg',
@@ -300,7 +304,7 @@ describe('OpenAI-Specific Features', () => {
300
304
  {}
301
305
  );
302
306
 
303
- mockClient.chat.completions.create.mockRejectedValue(apiError);
307
+ (mockClient.chat.completions.create as jest.Mock).mockRejectedValue(apiError);
304
308
 
305
309
  const result = await provider.analyzeImage(
306
310
  'invalid-url',
@@ -319,7 +323,7 @@ describe('OpenAI-Specific Features', () => {
319
323
  {}
320
324
  );
321
325
 
322
- mockClient.chat.completions.create.mockRejectedValue(rateLimitError);
326
+ (mockClient.chat.completions.create as jest.Mock).mockRejectedValue(rateLimitError);
323
327
 
324
328
  const result = await provider.analyzeImage(
325
329
  'https://example.com/image.jpg',
package/jest.config.cjs CHANGED
@@ -8,7 +8,8 @@ Redistribution or use in other products or commercial offerings is not permitted
8
8
 
9
9
  module.exports = {
10
10
  preset: 'ts-jest',
11
- testEnvironment: 'node',
11
+ // Use fixed environment to handle Node.js v25+ localStorage issue
12
+ testEnvironment: '<rootDir>/../../../jest-environment-node-fixed.cjs',
12
13
  roots: ['<rootDir>/__tests__'],
13
14
  testMatch: ['**/__tests__/**/*.test.ts'],
14
15
  collectCoverageFrom: [
package/package.json CHANGED
@@ -1,17 +1,9 @@
1
1
  {
2
2
  "name": "@bernierllc/ai-provider-openai",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "OpenAI API adapter implementing the unified AI provider interface",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
- "scripts": {
8
- "build": "tsc",
9
- "test": "jest --watch",
10
- "test:run": "jest",
11
- "test:coverage": "jest --coverage",
12
- "lint": "eslint src --ext .ts",
13
- "clean": "rm -rf dist"
14
- },
15
7
  "keywords": [
16
8
  "ai",
17
9
  "openai",
@@ -27,10 +19,10 @@
27
19
  "author": "Bernier LLC",
28
20
  "license": "SEE LICENSE IN LICENSE",
29
21
  "dependencies": {
30
- "@bernierllc/ai-provider-core": "^1.0.1",
31
- "@bernierllc/retry-policy": "^0.1.3",
32
- "@bernierllc/logger": "^1.0.1",
33
- "openai": "^4.20.0"
22
+ "openai": "^4.20.0",
23
+ "@bernierllc/ai-provider-core": "1.0.2",
24
+ "@bernierllc/retry-policy": "0.1.6",
25
+ "@bernierllc/logger": "1.0.4"
34
26
  },
35
27
  "devDependencies": {
36
28
  "@types/jest": "^29.5.0",
@@ -59,5 +51,17 @@
59
51
  "logger": "required",
60
52
  "docs-suite": "ready"
61
53
  }
54
+ },
55
+ "publishConfig": {
56
+ "access": "public",
57
+ "registry": "https://registry.npmjs.org/"
58
+ },
59
+ "scripts": {
60
+ "build": "tsc",
61
+ "test": "jest --watch",
62
+ "test:run": "jest",
63
+ "test:coverage": "jest --coverage",
64
+ "lint": "eslint src --ext .ts",
65
+ "clean": "rm -rf dist"
62
66
  }
63
- }
67
+ }