@mate-academy/llm-gateway 2.5.2 → 3.0.0

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 (56) hide show
  1. package/README.md +295 -110
  2. package/dist/LLMService.factory.d.ts +10 -19
  3. package/dist/LLMService.factory.js +22 -22
  4. package/dist/LLMService.factory.js.map +1 -1
  5. package/dist/LLMService.typedefs.d.ts +51 -4
  6. package/dist/LLMService.typedefs.js.map +1 -1
  7. package/dist/providers/GoogleGenerativeAI/GoogleGenerativeAI.entity.d.ts +2 -3
  8. package/dist/providers/GoogleGenerativeAI/GoogleGenerativeAI.entity.js +1 -1
  9. package/dist/providers/GoogleGenerativeAI/GoogleGenerativeAI.entity.js.map +1 -1
  10. package/dist/providers/GoogleGenerativeAI/GoogleGenerativeAIService.factory.d.ts +3 -4
  11. package/dist/providers/GoogleGenerativeAI/GoogleGenerativeAIService.factory.js +2 -1
  12. package/dist/providers/GoogleGenerativeAI/GoogleGenerativeAIService.factory.js.map +1 -1
  13. package/dist/providers/GoogleGenerativeAI/services/GoogleGenerativeAIAssistance.service.d.ts +2 -3
  14. package/dist/providers/GoogleGenerativeAI/services/GoogleGenerativeAIAssistance.service.js +8 -8
  15. package/dist/providers/GoogleGenerativeAI/services/GoogleGenerativeAIAssistance.service.js.map +1 -1
  16. package/dist/providers/GoogleGenerativeAI/services/GoogleGenerativeAICompletion.service.d.ts +2 -3
  17. package/dist/providers/GoogleGenerativeAI/services/GoogleGenerativeAICompletion.service.js +3 -3
  18. package/dist/providers/GoogleGenerativeAI/services/GoogleGenerativeAICompletion.service.js.map +1 -1
  19. package/dist/providers/GoogleGenerativeAI/services/GoogleGenerativeAISpeechToText.service.d.ts +2 -3
  20. package/dist/providers/GoogleGenerativeAI/services/GoogleGenerativeAISpeechToText.service.js +1 -1
  21. package/dist/providers/GoogleGenerativeAI/services/GoogleGenerativeAISpeechToText.service.js.map +1 -1
  22. package/dist/providers/GoogleGenerativeAI/services/GoogleGenerativeAITextToSpeech.service.d.ts +2 -3
  23. package/dist/providers/GoogleGenerativeAI/services/GoogleGenerativeAITextToSpeech.service.js +1 -1
  24. package/dist/providers/GoogleGenerativeAI/services/GoogleGenerativeAITextToSpeech.service.js.map +1 -1
  25. package/dist/providers/OpenAI/OpenAI.entity.d.ts +3 -4
  26. package/dist/providers/OpenAI/OpenAI.entity.js +1 -1
  27. package/dist/providers/OpenAI/OpenAI.entity.js.map +1 -1
  28. package/dist/providers/OpenAI/OpenAIService.factory.d.ts +3 -4
  29. package/dist/providers/OpenAI/OpenAIService.factory.js +2 -1
  30. package/dist/providers/OpenAI/OpenAIService.factory.js.map +1 -1
  31. package/dist/providers/OpenAI/services/OpenAIAssistance.service.d.ts +2 -3
  32. package/dist/providers/OpenAI/services/OpenAIAssistance.service.js +7 -7
  33. package/dist/providers/OpenAI/services/OpenAIAssistance.service.js.map +1 -1
  34. package/dist/providers/OpenAI/services/OpenAICompletion.service.d.ts +2 -3
  35. package/dist/providers/OpenAI/services/OpenAICompletion.service.js +3 -3
  36. package/dist/providers/OpenAI/services/OpenAICompletion.service.js.map +1 -1
  37. package/dist/providers/OpenAI/services/OpenAISpeechToText.service.d.ts +2 -3
  38. package/dist/providers/OpenAI/services/OpenAISpeechToText.service.js +1 -1
  39. package/dist/providers/OpenAI/services/OpenAISpeechToText.service.js.map +1 -1
  40. package/dist/providers/OpenAI/services/OpenAITextToSpeech.service.d.ts +2 -3
  41. package/dist/providers/OpenAI/services/OpenAITextToSpeech.service.js +1 -1
  42. package/dist/providers/OpenAI/services/OpenAITextToSpeech.service.js.map +1 -1
  43. package/dist/services/LLMAssistanceService.abstract.d.ts +2 -3
  44. package/dist/services/LLMAssistanceService.abstract.js.map +1 -1
  45. package/dist/services/LLMBaseService.abstract.d.ts +3 -4
  46. package/dist/services/LLMBaseService.abstract.js +1 -1
  47. package/dist/services/LLMBaseService.abstract.js.map +1 -1
  48. package/dist/services/LLMCompletionService.abstract.d.ts +2 -3
  49. package/dist/services/LLMCompletionService.abstract.js.map +1 -1
  50. package/dist/services/LLMServicePurposeFactory.abstract.d.ts +7 -3
  51. package/dist/services/LLMServicePurposeFactory.abstract.js.map +1 -1
  52. package/dist/services/LLMSpeechToTextService.abstract.d.ts +2 -3
  53. package/dist/services/LLMSpeechToTextService.abstract.js.map +1 -1
  54. package/dist/services/LLMTextToSpeechService.abstract.d.ts +2 -3
  55. package/dist/services/LLMTextToSpeechService.abstract.js.map +1 -1
  56. package/package.json +8 -9
package/README.md CHANGED
@@ -30,6 +30,23 @@ npm install @mate-academy/llm-gateway
30
30
 
31
31
  ## Usage
32
32
 
33
+ ### Logger Interface
34
+
35
+ The package accepts an optional logger that implements the `LLMLogger` interface. Most logging libraries are compatible (`@mate-academy/logger`, winston, pino, etc.). If no logger is provided, no logging will occur.
36
+
37
+ ```typescript
38
+ // Using @mate-academy/logger (recommended)
39
+ import { logger } from '@mate-academy/logger';
40
+
41
+ // Or simple console logger
42
+ const logger = {
43
+ info: (msg, meta) => console.log(msg, meta),
44
+ error: (msg, meta) => console.error(msg, meta),
45
+ warn: (msg, meta) => console.warn(msg, meta),
46
+ child: (context) => logger,
47
+ };
48
+ ```
49
+
33
50
  ### Basic Setup
34
51
 
35
52
  ```typescript
@@ -47,6 +64,7 @@ import {
47
64
  LLMModel,
48
65
  LLMSchema,
49
66
  createPromptTemplate,
67
+ LLMLogger,
50
68
  } from '@mate-academy/llm-gateway';
51
69
 
52
70
  // Define provider options
@@ -65,32 +83,32 @@ const llmProviderOptions = LLMServiceFactory.resolveProviderOptions(
65
83
  );
66
84
 
67
85
  // Get completion service
68
- const completionService = LLMServiceFactory.getCompletionService(
69
- LLMProviders.OpenAI,
70
- logger, // your logger instance
71
- llmProviderOptions,
72
- );
86
+ const completionService = LLMServiceFactory.getCompletionService({
87
+ provider: LLMProviders.OpenAI,
88
+ options: llmProviderOptions,
89
+ logger, // optional - omit for no logging
90
+ });
73
91
 
74
92
  // Get assistance service
75
- const assistanceService = LLMServiceFactory.getAssistanceService(
76
- LLMProviders.OpenAI,
77
- logger, // your logger instance
78
- llmProviderOptions,
79
- );
93
+ const assistanceService = LLMServiceFactory.getAssistanceService({
94
+ provider: LLMProviders.OpenAI,
95
+ options: llmProviderOptions,
96
+ logger, // optional - omit for no logging
97
+ });
80
98
 
81
99
  // Get speech-to-text service
82
- const speechToTextService = LLMServiceFactory.getSpeechToTextService(
83
- LLMProviders.OpenAI,
84
- logger, // your logger instance
85
- llmProviderOptions,
86
- );
100
+ const speechToTextService = LLMServiceFactory.getSpeechToTextService({
101
+ provider: LLMProviders.OpenAI,
102
+ options: llmProviderOptions,
103
+ logger, // optional - omit for no logging
104
+ });
87
105
 
88
106
  // Get text-to-speech service
89
- const textToSpeechService = LLMServiceFactory.getTextToSpeechService(
90
- LLMProviders.OpenAI,
91
- logger, // your logger instance
92
- llmProviderOptions,
93
- );
107
+ const textToSpeechService = LLMServiceFactory.getTextToSpeechService({
108
+ provider: LLMProviders.OpenAI,
109
+ options: llmProviderOptions,
110
+ logger, // optional - omit for no logging
111
+ });
94
112
  ```
95
113
 
96
114
  ### Real-world Example
@@ -128,29 +146,29 @@ class MyUseCase {
128
146
  },
129
147
  );
130
148
 
131
- this.llmCompletionService = LLMServiceFactory.getCompletionService(
132
- config.llmProvider,
149
+ this.llmCompletionService = LLMServiceFactory.getCompletionService({
150
+ provider: config.llmProvider,
151
+ options: llmProviderOptions,
133
152
  logger,
134
- llmProviderOptions,
135
- );
153
+ });
136
154
 
137
- this.llmAssistanceService = LLMServiceFactory.getAssistanceService(
138
- config.llmProvider,
155
+ this.llmAssistanceService = LLMServiceFactory.getAssistanceService({
156
+ provider: config.llmProvider,
157
+ options: llmProviderOptions,
139
158
  logger,
140
- llmProviderOptions,
141
- );
159
+ });
142
160
 
143
- this.llmSpeechToTextService = LLMServiceFactory.getSpeechToTextService(
144
- config.llmProvider,
161
+ this.llmSpeechToTextService = LLMServiceFactory.getSpeechToTextService({
162
+ provider: config.llmProvider,
163
+ options: llmProviderOptions,
145
164
  logger,
146
- llmProviderOptions,
147
- );
165
+ });
148
166
 
149
- this.llmTextToSpeechService = LLMServiceFactory.getTextToSpeechService(
150
- config.llmProvider,
167
+ this.llmTextToSpeechService = LLMServiceFactory.getTextToSpeechService({
168
+ provider: config.llmProvider,
169
+ options: llmProviderOptions,
151
170
  logger,
152
- llmProviderOptions,
153
- );
171
+ });
154
172
  }
155
173
 
156
174
  async processRequest(prompt) {
@@ -257,7 +275,7 @@ const result = await completionService.sendMessage({
257
275
  // Type-safe access to structured data
258
276
  if (result.data) {
259
277
  console.log(result.data.name); // string
260
- console.log(result.data.age); // number
278
+ console.log(result.data.age); // number
261
279
  console.log(result.data.email); // string
262
280
  console.log(result.data.isActive); // boolean
263
281
  }
@@ -380,6 +398,59 @@ if (result.data) {
380
398
  - Use `.min()` and `.max()` instead of `.positive()`, `.negative()`
381
399
  - Test schemas with both providers if cross-compatibility is important
382
400
 
401
+ ### Model Configuration
402
+
403
+ Each model comes with default configuration values that can be customized for your specific needs.
404
+
405
+ #### Accessing and Customizing Models
406
+
407
+ ```typescript
408
+ // Get available models from the service
409
+ const models = completionService.models;
410
+
411
+ // Get a specific model
412
+ const model = models[OpenAIModelNames.GPT_4_OMNI];
413
+
414
+ // Customize model configuration
415
+ const customModel = {
416
+ ...model,
417
+ config: {
418
+ ...model.config,
419
+ temperature: 0.8, // Override temperature (0-2, controls randomness)
420
+ top_p: 0.9, // Override top_p (nucleus sampling)
421
+ }
422
+ };
423
+
424
+ // Use customized model in requests
425
+ const result = await completionService.sendMessage({
426
+ message: {
427
+ role: LLMRoles.User,
428
+ content: [{ type: LLMMessageContentType.TEXT, text: 'Hello!' }]
429
+ },
430
+ model: customModel,
431
+ });
432
+ ```
433
+
434
+ #### Model-Specific Configuration
435
+
436
+ **GPT-5 Models** have special configuration options:
437
+
438
+ ```typescript
439
+ const gpt5Model = models[OpenAIModelNames.GPT_5];
440
+
441
+ const customGPT5Model = {
442
+ ...gpt5Model,
443
+ config: {
444
+ ...gpt5Model.config,
445
+ temperature: 1, // Note: GPT-5 only supports temperature=1
446
+ reasoning_effort: 'high', // 'minimal' | 'medium' | 'high'
447
+ verbosity: 'low', // 'low' | 'medium' | 'high'
448
+ }
449
+ };
450
+ ```
451
+
452
+ **Note:** GPT-5 models require `temperature: 1` and cannot be changed. GPT-5-MINI only supports the default temperature.
453
+
383
454
  #### File-based Assistance
384
455
 
385
456
  ```typescript
@@ -390,11 +461,11 @@ const uploadedFile = await assistanceService.uploadFile({
390
461
  mimeType: LLMUploadFileMimeTypes.PLAIN_TEXT,
391
462
  });
392
463
 
393
- // Create file storage
464
+ // Create file storage with instructions
394
465
  const storage = await assistanceService.createFileStorage({
395
466
  uploadedFiles: [uploadedFile],
396
467
  model: preferredModel,
397
- instructions: 'Help me analyze this document',
468
+ instructions: 'Help me analyze this document', // Optional context for the storage
398
469
  });
399
470
 
400
471
  // Create chat with file context
@@ -403,7 +474,7 @@ const chat = await assistanceService.createChat({
403
474
  model: preferredModel,
404
475
  history: [],
405
476
  files: [uploadedFile],
406
- instructions: 'Answer questions about the uploaded document',
477
+ instructions: 'Answer questions about the uploaded document', // Initial assistant instructions
407
478
  });
408
479
  ```
409
480
 
@@ -414,7 +485,7 @@ const transcription = await speechToTextService.transcribe({
414
485
  pathToAudio: '/path/to/audio.mp3',
415
486
  mimeType: LLMUploadFileMimeTypes.AUDIO_MP3, // Optional, but recommended
416
487
  model: preferredModel,
417
- instructions: 'Transcribe the audio file', // Optional instructions
488
+ instructions: 'Transcribe the audio file', // Optional: custom prompt for transcription context
418
489
  });
419
490
 
420
491
  console.log(transcription.text); // Transcribed text
@@ -563,6 +634,12 @@ Interface for text completion services.
563
634
  #### Methods
564
635
 
565
636
  - `sendMessage(options)`: Send a message to the LLM and get a completion response
637
+ - `message`: The message to send
638
+ - `model`: The LLM model to use (with optional config overrides)
639
+ - `history`: Optional conversation history
640
+ - `instructions`: Optional system instructions to guide the model's behavior
641
+ - `schema`: Optional schema for structured output
642
+ - `abortSignal`: Optional abort signal for cancellation
566
643
 
567
644
  ### LLMAssistanceService
568
645
 
@@ -573,10 +650,22 @@ Interface for chat/assistance services with file handling capabilities.
573
650
  - `uploadFile(fileOptions)`: Upload a file to the LLM service
574
651
  - `deleteFile(fileId)`: Delete a file from the LLM service
575
652
  - `createFileStorage(options)`: Create a file storage for organizing files
653
+ - `uploadedFiles`: Array of previously uploaded files
654
+ - `model`: The LLM model to use
655
+ - `instructions`: Optional instructions for how to use the stored files
576
656
  - `deleteFileStorage(fileStorageId)`: Delete a file storage
577
657
  - `createAssistant(options)`: Create an AI assistant with specific instructions
658
+ - `model`: The LLM model to use
659
+ - `instructions`: Optional instructions defining the assistant's behavior
660
+ - `storageIds`: Optional array of storage IDs to give the assistant access to
661
+ - `name`: Optional name for the assistant
578
662
  - `deleteAssistant(assistantId)`: Delete an assistant
579
663
  - `createChat(options)`: Create a new chat/conversation
664
+ - `model`: The LLM model to use
665
+ - `instructions`: Optional initial instructions for the conversation
666
+ - `history`: Optional conversation history
667
+ - `files`: Optional array of uploaded files
668
+ - `storageId`: Optional storage ID to use
580
669
  - `deleteChat(chatId)`: Delete a chat
581
670
  - `assistInChat(options)`: Send a message in an existing chat and get an assistant response
582
671
  - `countTokens(messages, model)`: Count tokens in messages for context management
@@ -588,6 +677,10 @@ Interface for converting speech audio to text.
588
677
  #### Methods
589
678
 
590
679
  - `transcribe(options)`: Convert audio file to text transcription
680
+ - `pathToAudio`: Path to the audio file
681
+ - `mimeType`: Optional MIME type of the audio file
682
+ - `model`: The LLM model to use
683
+ - `instructions`: Optional custom transcription prompt or context
591
684
 
592
685
  ### LLMTextToSpeechService
593
686
 
@@ -665,6 +758,7 @@ const fullLesson = coursePrompt({
665
758
  const basicLesson = coursePrompt({
666
759
  topicTitle: 'React Hooks',
667
760
  hasPrerequisites: false,
761
+ prerequisites: '',
668
762
  includeExercises: false,
669
763
  difficultyLevel: 'beginner'
670
764
  });
@@ -733,7 +827,7 @@ const reviewInstruction = codeReviewPrompt({
733
827
  outputFormat: 'structured',
734
828
  includeCode: true,
735
829
  codeSnippet: 'function example() { /* code here */ }',
736
- provideExamples: false
830
+ provideExamples: false,
737
831
  });
738
832
  ```
739
833
 
@@ -887,15 +981,33 @@ Creates a prompt template function from a template string.
887
981
  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).
888
982
 
889
983
  **Available Models:**
890
- - GPT-4 models for completion and assistance
891
- - TTS models for text-to-speech generation
984
+
985
+ | Model | Purpose | Max Input | Max Output | Notes |
986
+ |-------|---------|-----------|------------|-------|
987
+ | gpt-4o | Completion, Assistance | 128K | 16K | General purpose, balanced |
988
+ | gpt-4o-mini | Completion, Assistance | 128K | 16K | Faster, cost-effective |
989
+ | gpt-4.1 | Completion, Assistance | 1M+ | 32K | Extended context window |
990
+ | gpt-5 | Completion, Assistance | 400K | 128K | Advanced reasoning, requires temperature=1 |
991
+ | gpt-5-mini | Completion, Assistance | 400K | 128K | Smaller GPT-5 variant |
992
+ | gpt-4o-transcribe | Speech-to-Text | 16K | 2K | Optimized for transcription |
993
+ | gpt-4o-mini-transcribe | Speech-to-Text | 16K | 2K | Cost-effective transcription |
994
+ | tts-1 | Text-to-Speech | - | - | Standard TTS model |
995
+ | gpt-4o-mini-tts | Text-to-Speech | - | - | Alternative TTS model |
892
996
 
893
997
  ### Google Generative AI
894
998
 
895
- Supports completion and assistance APIs through Google's Generative AI models. For more information, see [Google Generative AI documentation](https://ai.google.dev/docs).
999
+ Supports completion, assistance, speech-to-text, and text-to-speech APIs through Google's Generative AI models. For more information, see [Google Generative AI documentation](https://ai.google.dev/docs).
896
1000
 
897
1001
  **Available Models:**
898
- - Gemini models for completion, assistance and speech-to-text
1002
+
1003
+ | Model | Purpose | Max Input | Max Output | Notes |
1004
+ |-------|---------|-----------|------------|-------|
1005
+ | gemini-2.5-flash | Completion, Assistance, Speech-to-Text | 1M+ | 65K | Fast, cost-effective, supports caching for long context |
1006
+ | gemini-2.5-pro | Completion, Assistance, Speech-to-Text | 1M+ | 65K | Advanced reasoning, supports caching for long context |
1007
+ | gemini-2.5-flash-preview-tts | Text-to-Speech | 8K | 16K | Preview TTS model with flash performance |
1008
+ | gemini-2.5-pro-preview-tts | Text-to-Speech | 8K | 16K | Preview TTS model with pro capabilities |
1009
+
1010
+ **Note:** Google Generative AI models support context caching for content longer than 32,768 tokens, which can significantly reduce costs for repeated queries on the same large context.
899
1011
 
900
1012
  ## Testing
901
1013
 
@@ -968,33 +1080,84 @@ The integration tests cover:
968
1080
 
969
1081
  The package provides several test utilities:
970
1082
 
971
- ```typescript
972
- import {
973
- mockLogger,
974
- resolveTestConfig,
975
- hasText,
976
- hasError,
977
- } from '@mate-academy/llm-gateway/tests/helpers';
1083
+ #### resolveTestConfig Function
1084
+
1085
+ The `resolveTestConfig` function creates standardized test configurations for all supported providers based on the service purpose:
978
1086
 
979
- // Mock logger for testing
980
- const logger = mockLogger;
1087
+ ```typescript
1088
+ import { resolveTestConfig } from '@mate-academy/llm-gateway/tests/helpers';
981
1089
 
982
- // Get test configuration for specific service purpose
1090
+ // Get test config for completion services
983
1091
  const testConfig = resolveTestConfig(LLMPurposes.Completion);
984
1092
 
1093
+ // testConfig contains configuration for all providers:
1094
+ // {
1095
+ // [LLMProviders.OpenAI]: {
1096
+ // provider: LLMProviders.OpenAI,
1097
+ // availableModels: {...}, // Models available for completion
1098
+ // clientOptions: { apiKey: process.env.OPENAI_SECRET_API_KEY, ... },
1099
+ // requireCredentials: () => void, // Throws if credentials missing
1100
+ // isEnabled: true
1101
+ // },
1102
+ // [LLMProviders.GoogleGenerativeAI]: {
1103
+ // provider: LLMProviders.GoogleGenerativeAI,
1104
+ // availableModels: {...}, // Models available for completion
1105
+ // clientOptions: { apiKey: process.env.GOOGLE_GENERATIVE_AI_API_KEY },
1106
+ // requireCredentials: () => void, // Throws if credentials missing
1107
+ // isEnabled: true
1108
+ // }
1109
+ // }
1110
+
1111
+ // Use in tests to iterate over all providers
1112
+ Object.values(testConfig).forEach((config) => {
1113
+ const { provider, clientOptions, availableModels, requireCredentials } = config;
1114
+
1115
+ describe(`${provider} Provider`, () => {
1116
+ beforeAll(() => {
1117
+ requireCredentials(); // Ensures API keys are present
1118
+ });
1119
+
1120
+ it('should create service', () => {
1121
+ const service = LLMServiceFactory.getCompletionService({
1122
+ provider,
1123
+ options: clientOptions,
1124
+ logger: mockLogger, // Optional
1125
+ });
1126
+ expect(service).toBeDefined();
1127
+ });
1128
+ });
1129
+ });
1130
+ ```
1131
+
1132
+ #### Mock Logger
1133
+
1134
+ ```typescript
1135
+ // Note: Test helpers are for internal use only
1136
+ // When testing your integration, create your own mock logger:
1137
+ const mockLogger = {
1138
+ info: jest.fn(),
1139
+ error: jest.fn(),
1140
+ warn: jest.fn(),
1141
+ child: jest.fn(() => mockLogger),
1142
+ };
1143
+ ```
1144
+
1145
+ #### Type Guards
1146
+
1147
+ ```typescript
985
1148
  // Type guards for test assertions
986
- if (hasText(result)) {
1149
+ if ('text' in result && result.text) {
987
1150
  expect(result.text).toContain('expected content');
988
1151
  }
989
1152
 
990
- if (hasError(result)) {
1153
+ if ('error' in result && result.error) {
991
1154
  expect(result.error).toBeDefined();
992
1155
  }
993
1156
  ```
994
1157
 
995
1158
  ### Writing Custom Tests
996
1159
 
997
- Example of writing a custom integration test:
1160
+ Example of writing a custom integration test using `resolveTestConfig`:
998
1161
 
999
1162
  ```typescript
1000
1163
  import {
@@ -1007,27 +1170,55 @@ import {
1007
1170
  LLMServiceFactory,
1008
1171
  LLMProviders,
1009
1172
  LLMPurposes,
1173
+ resolveTestConfig,
1010
1174
  } from '@mate-academy/llm-gateway';
1011
- import { mockLogger, resolveTestConfig } from '@mate-academy/llm-gateway/tests/helpers';
1175
+
1176
+ // Create your own mock logger
1177
+ const mockLogger = {
1178
+ info: jest.fn(),
1179
+ error: jest.fn(),
1180
+ warn: jest.fn(),
1181
+ child: jest.fn(() => mockLogger),
1182
+ };
1012
1183
 
1013
1184
  describe('Custom LLM Integration Test', () => {
1185
+ // Use resolveTestConfig for consistent test configuration
1014
1186
  const testConfig = resolveTestConfig(LLMPurposes.Completion);
1015
1187
 
1016
- Object.values(testConfig).forEach(({ provider, clientOptions, requireCredentials }) => {
1188
+ // Test all enabled providers
1189
+ Object.values(testConfig).forEach((config) => {
1190
+ const { provider, clientOptions, requireCredentials } = config;
1191
+
1017
1192
  describe(`${provider} Provider`, () => {
1018
1193
  let service;
1019
1194
 
1020
1195
  beforeAll(() => {
1021
- requireCredentials();
1022
- service = LLMServiceFactory.getCompletionService(
1196
+ requireCredentials(); // Validates API keys are present
1197
+
1198
+ service = LLMServiceFactory.getCompletionService({
1023
1199
  provider,
1024
- mockLogger,
1025
- clientOptions
1026
- );
1200
+ options: clientOptions,
1201
+ logger: mockLogger, // Optional - can be omitted
1202
+ });
1027
1203
  });
1028
1204
 
1029
1205
  it('should process custom request', async () => {
1030
1206
  // Your custom test logic here
1207
+ const result = await service.sendMessage({
1208
+ message: {
1209
+ role: LLMRoles.User,
1210
+ content: [{ type: LLMMessageContentType.TEXT, text: 'Test message' }],
1211
+ },
1212
+ model: Object.values(config.availableModels)[0], // Use first available model
1213
+ });
1214
+
1215
+ // Use type guards for assertions
1216
+ if ('text' in result && result.text) {
1217
+ expect(result.text).toBeDefined();
1218
+ expect(typeof result.text).toBe('string');
1219
+ } else if ('error' in result && result.error) {
1220
+ throw result.error;
1221
+ }
1031
1222
  });
1032
1223
  });
1033
1224
  });
@@ -1238,21 +1429,21 @@ Create service implementations in the `services` directory:
1238
1429
  **CompletionService (src/providers/YourProvider/services/YourProviderCompletionService.ts)**:
1239
1430
 
1240
1431
  ```typescript
1241
- import { Logger } from '../../../LLMService.typedefs';
1432
+ import { LLMLogger } from '../../../LLMService.typedefs';
1242
1433
  import { LLMCompletionService } from '../../../services/LLMCompletionService.abstract';
1243
1434
  import { CompletionParams, CompletionResult, LLMProviders } from '../../../LLMService.typedefs';
1244
1435
  import { YourProviderEntity } from '../YourProvider.entity';
1245
1436
 
1246
1437
  export class YourProviderCompletionService extends LLMCompletionService<LLMProviders.YourProvider> {
1247
1438
  constructor(
1248
- logger: Logger,
1439
+ logger: LLMLogger | undefined,
1249
1440
  private readonly providerEntity: YourProviderEntity,
1250
1441
  ) {
1251
1442
  super(logger);
1252
1443
  }
1253
1444
 
1254
1445
  async complete(params: CompletionParams): Promise<CompletionResult> {
1255
- this.logger.info('Starting completion with YourProvider', { params });
1446
+ this.logger?.info('Starting completion with YourProvider', { params });
1256
1447
 
1257
1448
  try {
1258
1449
  // Implement provider-specific completion logic
@@ -1262,7 +1453,7 @@ export class YourProviderCompletionService extends LLMCompletionService<LLMProvi
1262
1453
  // Include other required fields
1263
1454
  };
1264
1455
  } catch (error) {
1265
- this.logger.error('Error in YourProvider completion', { error });
1456
+ this.logger?.error('Error in YourProvider completion', { error });
1266
1457
  throw error;
1267
1458
  }
1268
1459
  }
@@ -1272,7 +1463,7 @@ export class YourProviderCompletionService extends LLMCompletionService<LLMProvi
1272
1463
  **AssistanceService (if applicable)**:
1273
1464
 
1274
1465
  ```typescript
1275
- import { Logger } from '../../../LLMService.typedefs';
1466
+ import { LLMLogger } from '../../../LLMService.typedefs';
1276
1467
  import { LLMAssistanceService } from '../../../services/LLMAssistanceService.abstract';
1277
1468
  import {
1278
1469
  LLMAssistanceOptions,
@@ -1283,7 +1474,7 @@ import { YourProviderEntity } from '../YourProvider.entity';
1283
1474
 
1284
1475
  export class YourProviderAssistanceService extends LLMAssistanceService<LLMProviders.YourProvider> {
1285
1476
  constructor(
1286
- logger: Logger,
1477
+ logger: LLMLogger,
1287
1478
  private readonly providerEntity: YourProviderEntity,
1288
1479
  ) {
1289
1480
  super(logger);
@@ -1311,7 +1502,7 @@ export class YourProviderAssistanceService extends LLMAssistanceService<LLMProvi
1311
1502
  **SpeechToTextService (if applicable)**:
1312
1503
 
1313
1504
  ```typescript
1314
- import { Logger } from '../../../LLMService.typedefs';
1505
+ import { LLMLogger } from '../../../LLMService.typedefs';
1315
1506
  import { LLMSpeechToTextService } from '../../../services/LLMSpeechToTextService.abstract';
1316
1507
  import {
1317
1508
  LLMTranscribeOptions,
@@ -1322,14 +1513,14 @@ import { YourProviderEntity } from '../YourProvider.entity';
1322
1513
 
1323
1514
  export class YourProviderSpeechToTextService extends LLMSpeechToTextService<LLMProviders.YourProvider> {
1324
1515
  constructor(
1325
- logger: Logger,
1516
+ logger: LLMLogger | undefined,
1326
1517
  private readonly providerEntity: YourProviderEntity,
1327
1518
  ) {
1328
1519
  super(logger);
1329
1520
  }
1330
1521
 
1331
1522
  async transcribe(options: LLMTranscribeOptions<typeof this.provider>): Promise<LLMTranscribeResult> {
1332
- this.logger.info('Starting transcription with YourProvider', { options });
1523
+ this.logger?.info('Starting transcription with YourProvider', { options });
1333
1524
 
1334
1525
  try {
1335
1526
  // Implement provider-specific transcription logic
@@ -1337,7 +1528,7 @@ export class YourProviderSpeechToTextService extends LLMSpeechToTextService<LLMP
1337
1528
  text: 'Transcribed text from audio',
1338
1529
  };
1339
1530
  } catch (error) {
1340
- this.logger.error('Error in YourProvider transcription', { error });
1531
+ this.logger?.error('Error in YourProvider transcription', { error });
1341
1532
  throw error;
1342
1533
  }
1343
1534
  }
@@ -1347,7 +1538,7 @@ export class YourProviderSpeechToTextService extends LLMSpeechToTextService<LLMP
1347
1538
  **TextToSpeechService (if applicable)**:
1348
1539
 
1349
1540
  ```typescript
1350
- import { Logger } from '../../../LLMService.typedefs';
1541
+ import { LLMLogger } from '../../../LLMService.typedefs';
1351
1542
  import { LLMTextToSpeechService } from '../../../services/LLMTextToSpeechService.abstract';
1352
1543
  import {
1353
1544
  LLMCreateSpeechOptions,
@@ -1358,14 +1549,14 @@ import { YourProviderEntity } from '../YourProvider.entity';
1358
1549
 
1359
1550
  export class YourProviderTextToSpeechService extends LLMTextToSpeechService<LLMProviders.YourProvider> {
1360
1551
  constructor(
1361
- logger: Logger,
1552
+ logger: LLMLogger | undefined,
1362
1553
  private readonly providerEntity: YourProviderEntity,
1363
1554
  ) {
1364
1555
  super(logger);
1365
1556
  }
1366
1557
 
1367
1558
  async createSpeech(options: LLMCreateSpeechOptions<typeof this.provider>): Promise<LLMCreateSpeechResult> {
1368
- this.logger.info('Starting speech creation with YourProvider', { options });
1559
+ this.logger?.info('Starting speech creation with YourProvider', { options });
1369
1560
 
1370
1561
  try {
1371
1562
  // Implement provider-specific speech creation logic
@@ -1374,7 +1565,7 @@ export class YourProviderTextToSpeechService extends LLMTextToSpeechService<LLMP
1374
1565
  mimeType: 'audio/mp3',
1375
1566
  };
1376
1567
  } catch (error) {
1377
- this.logger.error('Error in YourProvider speech creation', { error });
1568
+ this.logger?.error('Error in YourProvider speech creation', { error });
1378
1569
  throw error;
1379
1570
  }
1380
1571
  }
@@ -1412,14 +1603,13 @@ export const YOUR_PROVIDER_SERVICE_BUILDERS: {
1412
1603
  Then, implement the service factory in `src/providers/YourProvider/YourProviderService.factory.ts`:
1413
1604
 
1414
1605
  ```typescript
1415
- import { type Logger } from '@mate-academy/core';
1416
1606
  import {
1417
1607
  type LLMInstanceOptions,
1418
1608
  LLMProviders,
1419
1609
  type LLMPurposes,
1420
1610
  type LLMServiceByPurpose,
1421
1611
  } from '../../LLMService.typedefs';
1422
- import { LLMServicePurposeFactory } from '../../services';
1612
+ import { LLMServicePurposeFactory, type LLMCreateServiceOptions } from '../../services';
1423
1613
  import { YOUR_PROVIDER_SERVICE_BUILDERS } from './YourProvider.constants';
1424
1614
 
1425
1615
  export class YourProviderServiceFactory extends LLMServicePurposeFactory<
@@ -1428,10 +1618,9 @@ export class YourProviderServiceFactory extends LLMServicePurposeFactory<
1428
1618
  createService<
1429
1619
  Purpose extends LLMPurposes
1430
1620
  >(
1431
- purpose: Purpose,
1432
- logger: Logger,
1433
- options: LLMInstanceOptions[LLMProviders.YourProvider],
1621
+ serviceOptions: LLMCreateServiceOptions<LLMProviders.YourProvider, Purpose>,
1434
1622
  ): LLMServiceByPurpose<LLMProviders.YourProvider>[Purpose] {
1623
+ const { purpose, logger, options } = serviceOptions;
1435
1624
  const serviceBuilder = YOUR_PROVIDER_SERVICE_BUILDERS[purpose];
1436
1625
 
1437
1626
  if (!serviceBuilder) {
@@ -1469,7 +1658,7 @@ Modify `src/LLMService.factory.ts` to include your new provider:
1469
1658
  ```typescript
1470
1659
  import {
1471
1660
  LLMProviders,
1472
- Logger,
1661
+ LLMServiceOptions,
1473
1662
  YourProviderOptions,
1474
1663
  } from './LLMService.typedefs';
1475
1664
  import { YourProviderServiceFactory } from './providers/YourProvider';
@@ -1487,72 +1676,68 @@ export class LLMServiceFactory {
1487
1676
  }
1488
1677
 
1489
1678
  static getCompletionService<T extends LLMProviders>(
1490
- provider: T,
1491
- logger: Logger,
1492
- options: any,
1679
+ serviceOptions: LLMServiceOptions<T>,
1493
1680
  ) {
1681
+ const { provider, options, logger } = serviceOptions;
1494
1682
  switch (provider) {
1495
1683
  // ... other providers
1496
1684
  case LLMProviders.YourProvider:
1497
- return YourProviderServiceFactory.createService(
1498
- LLMPurposes.Completion,
1685
+ return YourProviderServiceFactory.createService({
1686
+ purpose: LLMPurposes.Completion,
1499
1687
  logger,
1500
1688
  options
1501
- );
1689
+ });
1502
1690
  default:
1503
1691
  throw new Error(`Unsupported provider: ${provider}`);
1504
1692
  }
1505
1693
  }
1506
1694
 
1507
1695
  static getAssistanceService<T extends LLMProviders>(
1508
- provider: T,
1509
- logger: Logger,
1510
- options: any,
1696
+ serviceOptions: LLMServiceOptions<T>,
1511
1697
  ) {
1698
+ const { provider, options, logger } = serviceOptions;
1512
1699
  switch (provider) {
1513
1700
  // ... other providers
1514
1701
  case LLMProviders.YourProvider:
1515
- return YourProviderServiceFactory.createService(
1516
- LLMPurposes.Assistance,
1702
+ return YourProviderServiceFactory.createService({
1703
+ purpose: LLMPurposes.Assistance,
1517
1704
  logger,
1518
1705
  options
1519
- );
1706
+ });
1520
1707
  default:
1521
1708
  throw new Error(`Unsupported provider: ${provider}`);
1522
1709
  }
1523
1710
  }
1524
1711
 
1525
1712
  static getSpeechToTextService<T extends LLMProviders>(
1526
- provider: T,
1527
- logger: Logger,
1528
- options: any,
1713
+ serviceOptions: LLMServiceOptions<T>,
1529
1714
  ) {
1715
+ const { provider, options, logger } = serviceOptions;
1530
1716
  switch (provider) {
1531
1717
  // ... other providers
1532
1718
  case LLMProviders.YourProvider:
1533
- return YourProviderServiceFactory.createService(
1534
- LLMPurposes.SpeechToText,
1719
+ return YourProviderServiceFactory.createService({
1720
+ purpose: LLMPurposes.SpeechToText,
1535
1721
  logger,
1536
1722
  options
1537
- );
1723
+ });
1538
1724
  default:
1539
1725
  throw new Error(`Unsupported provider: ${provider}`);
1540
1726
  }
1541
1727
  }
1542
1728
 
1543
1729
  static getTextToSpeechService<T extends LLMProviders>(
1544
- provider: T,
1545
- logger: Logger,
1546
- options: any,
1730
+ serviceOptions: LLMServiceOptions<T>,
1547
1731
  ) {
1732
+ const { provider, options, logger } = serviceOptions;
1548
1733
  switch (provider) {
1549
1734
  // ... other providers
1550
1735
  case LLMProviders.YourProvider:
1551
- return YourProviderServiceFactory.createService(
1552
- LLMPurposes.TextToSpeech,
1736
+ return YourProviderServiceFactory.createService({
1737
+ purpose: LLMPurposes.TextToSpeech,
1553
1738
  logger,
1554
1739
  options
1555
- );
1740
+ });
1556
1741
  default:
1557
1742
  throw new Error(`Unsupported provider: ${provider}`);
1558
1743
  }