@mate-academy/llm-gateway 1.3.0 → 1.3.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.
Files changed (2) hide show
  1. package/README.md +485 -22
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -16,9 +16,12 @@ npm install @mate-academy/llm-gateway
16
16
 
17
17
  - Support for multiple LLM providers (OpenAI, Google Generative AI)
18
18
  - Standardized completion service interface
19
- - Standardized assistance service interface
19
+ - Standardized assistance service interface with file handling
20
+ - Speech-to-text transcription capabilities
21
+ - Text-to-speech generation capabilities
20
22
  - Factory pattern for easy provider selection
21
23
  - Consistent logging across all providers
24
+ - Comprehensive testing suite with integration tests
22
25
 
23
26
  ## Usage
24
27
 
@@ -29,7 +32,9 @@ import {
29
32
  LLMProviders,
30
33
  LLMServiceFactory,
31
34
  LLMCompletionService,
32
- LLMAssistanceService
35
+ LLMAssistanceService,
36
+ LLMSpeechToTextService,
37
+ LLMTextToSpeechService,
33
38
  } from '@mate-academy/llm-gateway';
34
39
 
35
40
  // Define provider options
@@ -60,6 +65,20 @@ const assistanceService = LLMServiceFactory.getAssistanceService(
60
65
  logger, // your logger instance
61
66
  llmProviderOptions,
62
67
  );
68
+
69
+ // Get speech-to-text service
70
+ const speechToTextService = LLMServiceFactory.getSpeechToTextService(
71
+ LLMProviders.OpenAI,
72
+ logger, // your logger instance
73
+ llmProviderOptions,
74
+ );
75
+
76
+ // Get text-to-speech service
77
+ const textToSpeechService = LLMServiceFactory.getTextToSpeechService(
78
+ LLMProviders.OpenAI,
79
+ logger, // your logger instance
80
+ llmProviderOptions,
81
+ );
63
82
  ```
64
83
 
65
84
  ### Real-world Example
@@ -72,11 +91,15 @@ import {
72
91
  LLMServiceFactory,
73
92
  LLMCompletionService,
74
93
  LLMAssistanceService,
94
+ LLMSpeechToTextService,
95
+ LLMTextToSpeechService,
75
96
  } from '@mate-academy/llm-gateway';
76
97
 
77
98
  class MyUseCase {
78
99
  private llmCompletionService: LLMCompletionService<LLMProviders>;
79
100
  private llmAssistanceService: LLMAssistanceService<LLMProviders>;
101
+ private llmSpeechToTextService: LLMSpeechToTextService<LLMProviders>;
102
+ private llmTextToSpeechService: LLMTextToSpeechService<LLMProviders>;
80
103
 
81
104
  constructor(logger, config) {
82
105
  const llmProviderOptions = LLMServiceFactory.resolveProviderOptions(
@@ -104,18 +127,136 @@ class MyUseCase {
104
127
  logger,
105
128
  llmProviderOptions,
106
129
  );
130
+
131
+ this.llmSpeechToTextService = LLMServiceFactory.getSpeechToTextService(
132
+ config.llmProvider,
133
+ logger,
134
+ llmProviderOptions,
135
+ );
136
+
137
+ this.llmTextToSpeechService = LLMServiceFactory.getTextToSpeechService(
138
+ config.llmProvider,
139
+ logger,
140
+ llmProviderOptions,
141
+ );
107
142
  }
108
143
 
109
144
  async processRequest(prompt) {
110
145
  // Use completion service
111
- const completion = await this.llmCompletionService.complete({
112
- prompt,
113
- maxTokens: 500,
146
+ const completion = await this.llmCompletionService.sendMessage({
147
+ message: {
148
+ role: 'user',
149
+ content: { type: 'text', text: prompt }
150
+ },
151
+ model: this.getPreferredModel(),
114
152
  });
115
153
 
116
154
  return completion;
117
155
  }
156
+
157
+ async transcribeAudio(audioPath) {
158
+ // Use speech-to-text service
159
+ const transcription = await this.llmSpeechToTextService.transcribe({
160
+ pathToAudio: audioPath,
161
+ model: this.getPreferredModel(),
162
+ });
163
+
164
+ return transcription;
165
+ }
166
+
167
+ async generateSpeech(text) {
168
+ // Use text-to-speech service
169
+ const speech = await this.llmTextToSpeechService.createSpeech({
170
+ input: text,
171
+ model: this.getPreferredModel(),
172
+ voice: 'alloy', // OpenAI voice option
173
+ });
174
+
175
+ return speech;
176
+ }
177
+
178
+ private getPreferredModel() {
179
+ // Get the appropriate model from the service's available models
180
+ const models = Object.values(this.llmCompletionService.models);
181
+ return models[0]; // Use the first available model
182
+ }
118
183
  }
184
+ ```
185
+
186
+ this.llmAssistanceService = LLMServiceFactory.getAssistanceService(
187
+ config.llmProvider,
188
+ logger,
189
+ llmProviderOptions,
190
+ );
191
+ }
192
+
193
+ ### Usage Examples
194
+
195
+ #### Basic Text Completion
196
+
197
+ ```typescript
198
+ const result = await completionService.sendMessage({
199
+ message: {
200
+ role: 'user',
201
+ content: { type: 'text', text: 'Hello, how are you?' }
202
+ },
203
+ model: preferredModel,
204
+ });
205
+
206
+ console.log(result.text); // AI response
207
+ ```
208
+
209
+ #### File-based Assistance
210
+
211
+ ```typescript
212
+ // Upload files for context
213
+ const uploadedFile = await assistanceService.uploadFile({
214
+ filename: 'document.pdf',
215
+ content: fileBuffer,
216
+ mimeType: 'application/pdf',
217
+ });
218
+
219
+ // Create file storage
220
+ const storage = await assistanceService.createFileStorage({
221
+ uploadedFiles: [uploadedFile],
222
+ model: preferredModel,
223
+ instructions: 'Help me analyze this document',
224
+ });
225
+
226
+ // Create chat with file context
227
+ const chat = await assistanceService.createChat({
228
+ storageId: storage.storageId,
229
+ model: preferredModel,
230
+ history: [],
231
+ files: [uploadedFile],
232
+ instructions: 'Answer questions about the uploaded document',
233
+ });
234
+ ```
235
+
236
+ #### Speech-to-Text Transcription
237
+
238
+ ```typescript
239
+ const transcription = await speechToTextService.transcribe({
240
+ pathToAudio: '/path/to/audio.mp3',
241
+ model: preferredModel,
242
+ language: 'en', // optional
243
+ });
244
+
245
+ console.log(transcription.text); // Transcribed text
246
+ ```
247
+
248
+ #### Text-to-Speech Generation
249
+
250
+ ```typescript
251
+ const speech = await textToSpeechService.createSpeech({
252
+ input: 'Hello, this will be converted to speech',
253
+ model: preferredModel,
254
+ voice: 'alloy', // OpenAI voice option
255
+ response_format: 'mp3',
256
+ });
257
+
258
+ // Save audio buffer to file
259
+ fs.writeFileSync('output.mp3', speech.audioBuffer);
119
260
  ```
120
261
 
121
262
  ## API Reference
@@ -126,12 +267,25 @@ Enum of supported LLM providers:
126
267
 
127
268
  ```typescript
128
269
  enum LLMProviders {
129
- OpenAI = 'openai',
130
- GoogleGenerativeAI = 'google',
270
+ OpenAI = 'OpenAI',
271
+ GoogleGenerativeAI = 'GoogleGenerativeAI',
131
272
  // other providers may be added in the future
132
273
  }
133
274
  ```
134
275
 
276
+ ### LLMPurposes
277
+
278
+ Enum of supported service purposes:
279
+
280
+ ```typescript
281
+ enum LLMPurposes {
282
+ Completion = 'completion',
283
+ Assistance = 'assistance',
284
+ SpeechToText = 'speech_to_text',
285
+ TextToSpeech = 'text_to_speech',
286
+ }
287
+ ```
288
+
135
289
  ### LLMServiceFactory
136
290
 
137
291
  Factory class for creating LLM service instances.
@@ -141,6 +295,8 @@ Factory class for creating LLM service instances.
141
295
  - `resolveProviderOptions(provider, optionsMap)`: Resolves the options for the specified provider
142
296
  - `getCompletionService(provider, logger, options)`: Creates a completion service instance
143
297
  - `getAssistanceService(provider, logger, options)`: Creates an assistance service instance
298
+ - `getSpeechToTextService(provider, logger, options)`: Creates a speech-to-text service instance
299
+ - `getTextToSpeechService(provider, logger, options)`: Creates a text-to-speech service instance
144
300
 
145
301
  ### LLMCompletionService
146
302
 
@@ -148,30 +304,188 @@ Interface for text completion services.
148
304
 
149
305
  #### Methods
150
306
 
151
- - `complete(params)`: Generate a completion for the given prompt
307
+ - `sendMessage(options)`: Send a message to the LLM and get a completion response
152
308
 
153
309
  ### LLMAssistanceService
154
310
 
155
- Interface for chat/assistance services.
311
+ Interface for chat/assistance services with file handling capabilities.
156
312
 
157
313
  #### Methods
158
314
 
159
- - `createAssistant(params)`: Create a new assistant
160
- - `getAssistant(assistantId)`: Retrieve an existing assistant
161
- - `createThread(params)`: Create a new thread
162
- - `addMessage(threadId, params)`: Add a message to a thread
163
- - `runAssistant(params)`: Run an assistant on a thread
164
- - `getRunResult(params)`: Get the result of an assistant run
315
+ - `uploadFile(file)`: Upload a file to the LLM service
316
+ - `createFileStorage(options)`: Create a file storage for organizing files
317
+ - `createChat(options)`: Create a new chat/conversation
318
+ - `assistInChat(options)`: Send a message in an existing chat and get an assistant response
319
+
320
+ ### LLMSpeechToTextService
321
+
322
+ Interface for converting speech audio to text.
323
+
324
+ #### Methods
325
+
326
+ - `transcribe(options)`: Convert audio file to text transcription
327
+
328
+ ### LLMTextToSpeechService
329
+
330
+ Interface for converting text to speech audio.
331
+
332
+ #### Methods
333
+
334
+ - `createSpeech(options)`: Convert text to speech audio file
165
335
 
166
336
  ## Supported Providers
167
337
 
168
338
  ### OpenAI
169
339
 
170
- Supports both completion and assistance APIs. For more information, see [OpenAI API documentation](https://platform.openai.com/docs/api-reference).
340
+ Supports all service types: completion, assistance, speech-to-text, and text-to-speech APIs. For more information, see [OpenAI API documentation](https://platform.openai.com/docs/api-reference).
341
+
342
+ **Available Models:**
343
+ - GPT-4 models for completion and assistance
344
+ - TTS models for text-to-speech
171
345
 
172
346
  ### Google Generative AI
173
347
 
174
- Supports completion API through Google's Generative AI models. For more information, see [Google Generative AI documentation](https://ai.google.dev/docs).
348
+ Supports completion and assistance APIs through Google's Generative AI models. For more information, see [Google Generative AI documentation](https://ai.google.dev/docs).
349
+
350
+ **Available Models:**
351
+ - Gemini models for completion, assistance and speech-to-text
352
+
353
+ ## Testing
354
+
355
+ The LLM Gateway includes a comprehensive testing suite with both unit and integration tests.
356
+
357
+ ### Test Structure
358
+
359
+ The package includes:
360
+ - **Integration tests** for all service types (`src/tests/integration/`)
361
+ - **Test helpers** for common testing utilities (`src/tests/helpers.ts`)
362
+ - **Mock implementations** for testing environments
363
+ - **Audio test files** for speech-to-text testing
364
+
365
+ ### Running Tests
366
+
367
+ ```bash
368
+ # Run all tests with logging
369
+ npm test
370
+
371
+ # Run tests silently (without logs)
372
+ npm run test:silent
373
+
374
+ # Run with specific environment variables
375
+ ENABLE_LOGGING=true npm test
376
+ ```
377
+
378
+ ### Test Configuration
379
+
380
+ Integration tests require API keys for the respective providers:
381
+
382
+ ```bash
383
+ # Required environment variables for OpenAI tests
384
+ OPENAI_SECRET_API_KEY=your_openai_api_key
385
+ OPENAI_ORG_ID=your_organization_id # optional
386
+ OPENAI_BASE_URL=https://api.openai.com/v1 # optional
387
+
388
+ # Required environment variables for Google AI tests
389
+ GOOGLE_GENERATIVE_AI_API_KEY=your_google_ai_api_key
390
+ ```
391
+
392
+ ### Test Coverage
393
+
394
+ The integration tests cover:
395
+
396
+ 1. **Completion Service Tests:**
397
+ - Basic message sending and responses
398
+ - Message history handling
399
+ - Error handling for invalid inputs
400
+ - Model-specific functionality
401
+
402
+ 2. **Assistance Service Tests:**
403
+ - File upload and storage creation
404
+ - Chat creation and management
405
+ - Assistant interactions
406
+ - File-based conversations
407
+
408
+ 3. **Speech-to-Text Service Tests:**
409
+ - Audio file transcription
410
+ - Multiple audio format support
411
+ - Error handling for invalid files
412
+ - Model-specific transcription quality
413
+
414
+ 4. **Text-to-Speech Service Tests:**
415
+ - Text-to-audio conversion
416
+ - Voice selection options
417
+ - Audio format configuration
418
+ - Error handling
419
+
420
+ ### Test Helpers
421
+
422
+ The package provides several test utilities:
423
+
424
+ ```typescript
425
+ import {
426
+ mockLogger,
427
+ resolveTestConfig,
428
+ hasText,
429
+ hasError,
430
+ } from '@mate-academy/llm-gateway/tests/helpers';
431
+
432
+ // Mock logger for testing
433
+ const logger = mockLogger;
434
+
435
+ // Get test configuration for specific service purpose
436
+ const testConfig = resolveTestConfig(LLMPurposes.Completion);
437
+
438
+ // Type guards for test assertions
439
+ if (hasText(result)) {
440
+ expect(result.text).toContain('expected content');
441
+ }
442
+
443
+ if (hasError(result)) {
444
+ expect(result.error).toBeDefined();
445
+ }
446
+ ```
447
+
448
+ ### Writing Custom Tests
449
+
450
+ Example of writing a custom integration test:
451
+
452
+ ```typescript
453
+ import {
454
+ describe,
455
+ it,
456
+ expect,
457
+ beforeAll,
458
+ } from '@jest/globals';
459
+ import {
460
+ LLMServiceFactory,
461
+ LLMProviders,
462
+ LLMPurposes,
463
+ } from '@mate-academy/llm-gateway';
464
+ import { mockLogger, resolveTestConfig } from '@mate-academy/llm-gateway/tests/helpers';
465
+
466
+ describe('Custom LLM Integration Test', () => {
467
+ const testConfig = resolveTestConfig(LLMPurposes.Completion);
468
+
469
+ Object.values(testConfig).forEach(({ provider, clientOptions, requireCredentials }) => {
470
+ describe(`${provider} Provider`, () => {
471
+ let service;
472
+
473
+ beforeAll(() => {
474
+ requireCredentials();
475
+ service = LLMServiceFactory.getCompletionService(
476
+ provider,
477
+ mockLogger,
478
+ clientOptions
479
+ );
480
+ });
481
+
482
+ it('should process custom request', async () => {
483
+ // Your custom test logic here
484
+ });
485
+ });
486
+ });
487
+ });
488
+ ```
175
489
 
176
490
  ## Developer Guide: Adding a New Provider
177
491
 
@@ -325,6 +639,18 @@ export const YOUR_PROVIDER_MODELS: LLMProviderModelsByPurpose<
325
639
  YourProviderModelNames.MODEL_TWO, // Only MODEL_TWO supports assistance
326
640
  ],
327
641
  ),
642
+ [LLMPurposes.SpeechToText]: pick(
643
+ YOUR_PROVIDER_AVAILABLE_MODELS,
644
+ [
645
+ YourProviderModelNames.MODEL_ONE, // Speech-to-text capable model
646
+ ],
647
+ ),
648
+ [LLMPurposes.TextToSpeech]: pick(
649
+ YOUR_PROVIDER_AVAILABLE_MODELS,
650
+ [
651
+ YourProviderModelNames.MODEL_ONE, // Text-to-speech capable model
652
+ ],
653
+ ),
328
654
  };
329
655
 
330
656
  // Define service builders for each LLM purpose
@@ -339,6 +665,12 @@ export const YOUR_PROVIDER_SERVICE_BUILDERS: {
339
665
  [LLMPurposes.Assistance]: (logger, options) => (
340
666
  new YourProviderAssistanceService(logger, options)
341
667
  ),
668
+ [LLMPurposes.SpeechToText]: (logger, options) => (
669
+ new YourProviderSpeechToTextService(logger, options)
670
+ ),
671
+ [LLMPurposes.TextToSpeech]: (logger, options) => (
672
+ new YourProviderTextToSpeechService(logger, options)
673
+ ),
342
674
  };
343
675
  ```
344
676
 
@@ -396,9 +728,8 @@ export class YourProviderCompletionService extends LLMCompletionService<LLMProvi
396
728
  import { Logger } from '../../../LLMService.typedefs';
397
729
  import { LLMAssistanceService } from '../../../services/LLMAssistanceService.abstract';
398
730
  import {
399
- AssistantParams,
400
- ThreadParams,
401
- MessageParams,
731
+ LLMAssistanceOptions,
732
+ LLMAssistanceResult,
402
733
  LLMProviders
403
734
  } from '../../../LLMService.typedefs';
404
735
  import { YourProviderEntity } from '../YourProvider.entity';
@@ -412,6 +743,94 @@ export class YourProviderAssistanceService extends LLMAssistanceService<LLMProvi
412
743
  }
413
744
 
414
745
  // Implement required assistance methods
746
+ async uploadFile(file: LLMUploadFile): Promise<LLMUploadFileResult> {
747
+ // Implementation for file upload
748
+ }
749
+
750
+ async createFileStorage(options: LLMCreateFileStorageOptions): Promise<LLMCreateFileStorageResult> {
751
+ // Implementation for file storage creation
752
+ }
753
+
754
+ async createChat(options: LLMCreateChatOptions): Promise<LLMCreateChatResult> {
755
+ // Implementation for chat creation
756
+ }
757
+
758
+ async assistInChat(options: LLMAssistanceOptions): Promise<LLMAssistanceResult> {
759
+ // Implementation for chat assistance
760
+ }
761
+ }
762
+ ```
763
+
764
+ **SpeechToTextService (if applicable)**:
765
+
766
+ ```typescript
767
+ import { Logger } from '../../../LLMService.typedefs';
768
+ import { LLMSpeechToTextService } from '../../../services/LLMSpeechToTextService.abstract';
769
+ import {
770
+ LLMTranscribeOptions,
771
+ LLMTranscribeResult,
772
+ LLMProviders
773
+ } from '../../../LLMService.typedefs';
774
+ import { YourProviderEntity } from '../YourProvider.entity';
775
+
776
+ export class YourProviderSpeechToTextService extends LLMSpeechToTextService<LLMProviders.YourProvider> {
777
+ constructor(
778
+ logger: Logger,
779
+ private readonly providerEntity: YourProviderEntity,
780
+ ) {
781
+ super(logger);
782
+ }
783
+
784
+ async transcribe(options: LLMTranscribeOptions<typeof this.provider>): Promise<LLMTranscribeResult> {
785
+ this.logger.info('Starting transcription with YourProvider', { options });
786
+
787
+ try {
788
+ // Implement provider-specific transcription logic
789
+ return {
790
+ text: 'Transcribed text from audio',
791
+ };
792
+ } catch (error) {
793
+ this.logger.error('Error in YourProvider transcription', { error });
794
+ throw error;
795
+ }
796
+ }
797
+ }
798
+ ```
799
+
800
+ **TextToSpeechService (if applicable)**:
801
+
802
+ ```typescript
803
+ import { Logger } from '../../../LLMService.typedefs';
804
+ import { LLMTextToSpeechService } from '../../../services/LLMTextToSpeechService.abstract';
805
+ import {
806
+ LLMCreateSpeechOptions,
807
+ LLMCreateSpeechResult,
808
+ LLMProviders
809
+ } from '../../../LLMService.typedefs';
810
+ import { YourProviderEntity } from '../YourProvider.entity';
811
+
812
+ export class YourProviderTextToSpeechService extends LLMTextToSpeechService<LLMProviders.YourProvider> {
813
+ constructor(
814
+ logger: Logger,
815
+ private readonly providerEntity: YourProviderEntity,
816
+ ) {
817
+ super(logger);
818
+ }
819
+
820
+ async createSpeech(options: LLMCreateSpeechOptions<typeof this.provider>): Promise<LLMCreateSpeechResult> {
821
+ this.logger.info('Starting speech creation with YourProvider', { options });
822
+
823
+ try {
824
+ // Implement provider-specific speech creation logic
825
+ return {
826
+ audioBuffer: Buffer.from('audio data'),
827
+ format: 'mp3',
828
+ };
829
+ } catch (error) {
830
+ this.logger.error('Error in YourProvider speech creation', { error });
831
+ throw error;
832
+ }
833
+ }
415
834
  }
416
835
  ```
417
836
 
@@ -528,7 +947,11 @@ export class LLMServiceFactory {
528
947
  switch (provider) {
529
948
  // ... other providers
530
949
  case LLMProviders.YourProvider:
531
- return YourProviderServiceFactory.createCompletionService(logger, options);
950
+ return YourProviderServiceFactory.createService(
951
+ LLMPurposes.Completion,
952
+ logger,
953
+ options
954
+ );
532
955
  default:
533
956
  throw new Error(`Unsupported provider: ${provider}`);
534
957
  }
@@ -542,7 +965,47 @@ export class LLMServiceFactory {
542
965
  switch (provider) {
543
966
  // ... other providers
544
967
  case LLMProviders.YourProvider:
545
- return YourProviderServiceFactory.createAssistanceService(logger, options);
968
+ return YourProviderServiceFactory.createService(
969
+ LLMPurposes.Assistance,
970
+ logger,
971
+ options
972
+ );
973
+ default:
974
+ throw new Error(`Unsupported provider: ${provider}`);
975
+ }
976
+ }
977
+
978
+ static getSpeechToTextService<T extends LLMProviders>(
979
+ provider: T,
980
+ logger: Logger,
981
+ options: any,
982
+ ) {
983
+ switch (provider) {
984
+ // ... other providers
985
+ case LLMProviders.YourProvider:
986
+ return YourProviderServiceFactory.createService(
987
+ LLMPurposes.SpeechToText,
988
+ logger,
989
+ options
990
+ );
991
+ default:
992
+ throw new Error(`Unsupported provider: ${provider}`);
993
+ }
994
+ }
995
+
996
+ static getTextToSpeechService<T extends LLMProviders>(
997
+ provider: T,
998
+ logger: Logger,
999
+ options: any,
1000
+ ) {
1001
+ switch (provider) {
1002
+ // ... other providers
1003
+ case LLMProviders.YourProvider:
1004
+ return YourProviderServiceFactory.createService(
1005
+ LLMPurposes.TextToSpeech,
1006
+ logger,
1007
+ options
1008
+ );
546
1009
  default:
547
1010
  throw new Error(`Unsupported provider: ${provider}`);
548
1011
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mate-academy/llm-gateway",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "A gateway package for LLM services.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -65,7 +65,7 @@
65
65
  "axios": "^1.9.0",
66
66
  "js-tiktoken": "^1.0.20",
67
67
  "mime-types": "^3.0.1",
68
- "openai": "^5.1.1",
68
+ "openai": "^5.2.0",
69
69
  "uuid": "^11.1.0"
70
70
  }
71
71
  }