genai-lite 0.6.1 → 0.7.2
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/README.md +21 -0
- package/dist/adapters/image/GenaiElectronImageAdapter.js +6 -4
- package/dist/adapters/image/OpenAIImageAdapter.js +5 -3
- package/dist/config/llm-presets.json +40 -44
- package/dist/image/ImageService.d.ts +1 -0
- package/dist/image/ImageService.js +13 -10
- package/dist/index.d.ts +6 -0
- package/dist/index.js +11 -1
- package/dist/llm/LLMService.d.ts +6 -0
- package/dist/llm/LLMService.js +20 -17
- package/dist/llm/clients/AnthropicClientAdapter.js +36 -15
- package/dist/llm/clients/GeminiClientAdapter.js +36 -12
- package/dist/llm/clients/LlamaCppClientAdapter.js +43 -23
- package/dist/llm/clients/MistralClientAdapter.d.ts +94 -0
- package/dist/llm/clients/MistralClientAdapter.js +239 -0
- package/dist/llm/clients/OpenAIClientAdapter.js +36 -17
- package/dist/llm/clients/OpenRouterClientAdapter.d.ts +103 -0
- package/dist/llm/clients/OpenRouterClientAdapter.js +285 -0
- package/dist/llm/config.js +75 -32
- package/dist/llm/services/ModelResolver.d.ts +3 -1
- package/dist/llm/services/ModelResolver.js +5 -3
- package/dist/llm/services/SettingsManager.d.ts +3 -0
- package/dist/llm/services/SettingsManager.js +29 -20
- package/dist/llm/types.d.ts +79 -0
- package/dist/logging/defaultLogger.d.ts +35 -0
- package/dist/logging/defaultLogger.js +94 -0
- package/dist/logging/index.d.ts +2 -0
- package/dist/logging/index.js +7 -0
- package/dist/logging/types.d.ts +23 -0
- package/dist/logging/types.js +2 -0
- package/dist/prompting/parser.js +4 -1
- package/dist/shared/adapters/systemMessageUtils.d.ts +162 -0
- package/dist/shared/adapters/systemMessageUtils.js +172 -0
- package/dist/shared/services/AdapterRegistry.d.ts +4 -1
- package/dist/shared/services/AdapterRegistry.js +12 -9
- package/dist/types/image.d.ts +5 -0
- package/package.json +2 -1
- package/src/config/llm-presets.json +40 -44
package/README.md
CHANGED
|
@@ -14,6 +14,7 @@ A lightweight, portable Node.js/TypeScript library providing a unified interface
|
|
|
14
14
|
- 🛡️ **Provider Normalization** - Consistent responses across different AI APIs
|
|
15
15
|
- 🎨 **Configurable Model Presets** - Built-in presets with full customization options
|
|
16
16
|
- 🎭 **Template Engine** - Sophisticated templating with conditionals and variable substitution
|
|
17
|
+
- 📊 **Configurable Logging** - Debug mode, custom loggers (pino, winston), and silent mode for tests
|
|
17
18
|
|
|
18
19
|
## Installation
|
|
19
20
|
|
|
@@ -112,6 +113,7 @@ Comprehensive documentation is available in the **[`genai-lite-docs`](./genai-li
|
|
|
112
113
|
|
|
113
114
|
### Utilities & Advanced
|
|
114
115
|
- **[Prompting Utilities](./genai-lite-docs/prompting-utilities.md)** - Template engine, token counting, content parsing
|
|
116
|
+
- **[Logging](./genai-lite-docs/logging.md)** - Configure logging and debugging
|
|
115
117
|
- **[TypeScript Reference](./genai-lite-docs/typescript-reference.md)** - Type definitions
|
|
116
118
|
|
|
117
119
|
### Provider Reference
|
|
@@ -154,6 +156,25 @@ const llmService = new LLMService(myKeyProvider);
|
|
|
154
156
|
|
|
155
157
|
See **[Core Concepts](./genai-lite-docs/core-concepts.md#api-key-management)** for detailed examples including Electron integration.
|
|
156
158
|
|
|
159
|
+
## Logging Configuration
|
|
160
|
+
|
|
161
|
+
Control logging verbosity via environment variable or service options:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
# Environment variable (applies to all services)
|
|
165
|
+
export GENAI_LITE_LOG_LEVEL=debug # Options: silent, error, warn, info, debug
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
// Per-service configuration
|
|
170
|
+
const llmService = new LLMService(fromEnvironment, {
|
|
171
|
+
logLevel: 'debug', // Override env var
|
|
172
|
+
logger: customPinoLogger // Inject pino/winston/etc.
|
|
173
|
+
});
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
See **[Logging](./genai-lite-docs/logging.md)** for custom logger integration and testing patterns.
|
|
177
|
+
|
|
157
178
|
## Example Applications
|
|
158
179
|
|
|
159
180
|
The library includes two complete demo applications showcasing all features:
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
19
|
exports.GenaiElectronImageAdapter = void 0;
|
|
20
20
|
const errorUtils_1 = require("../../shared/adapters/errorUtils");
|
|
21
|
+
const defaultLogger_1 = require("../../logging/defaultLogger");
|
|
22
|
+
const logger = (0, defaultLogger_1.createDefaultLogger)();
|
|
21
23
|
/**
|
|
22
24
|
* Adapter for genai-electron's local diffusion image generation
|
|
23
25
|
*/
|
|
@@ -44,7 +46,7 @@ class GenaiElectronImageAdapter {
|
|
|
44
46
|
try {
|
|
45
47
|
// Build request payload
|
|
46
48
|
const payload = this.buildRequestPayload(resolvedPrompt, request, settings);
|
|
47
|
-
|
|
49
|
+
logger.debug(`GenaiElectron Image API: Starting generation`, {
|
|
48
50
|
prompt: resolvedPrompt.substring(0, 100),
|
|
49
51
|
count: payload.count,
|
|
50
52
|
dimensions: `${payload.width}x${payload.height}`,
|
|
@@ -52,15 +54,15 @@ class GenaiElectronImageAdapter {
|
|
|
52
54
|
});
|
|
53
55
|
// Start generation (returns immediately with ID)
|
|
54
56
|
const generationId = await this.startGeneration(payload);
|
|
55
|
-
|
|
57
|
+
logger.info(`GenaiElectron Image API: Generation started with ID: ${generationId}`);
|
|
56
58
|
// Poll for completion
|
|
57
59
|
const result = await this.pollForCompletion(generationId, settings.diffusion?.onProgress);
|
|
58
|
-
|
|
60
|
+
logger.info(`GenaiElectron Image API: Generation complete (${result.timeTaken}ms)`);
|
|
59
61
|
// Convert to ImageGenerationResponse
|
|
60
62
|
return this.convertToResponse(result, request);
|
|
61
63
|
}
|
|
62
64
|
catch (error) {
|
|
63
|
-
|
|
65
|
+
logger.error('GenaiElectron Image API error:', error);
|
|
64
66
|
throw this.handleError(error, request);
|
|
65
67
|
}
|
|
66
68
|
}
|
|
@@ -20,6 +20,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
20
20
|
exports.OpenAIImageAdapter = void 0;
|
|
21
21
|
const openai_1 = __importDefault(require("openai"));
|
|
22
22
|
const errorUtils_1 = require("../../shared/adapters/errorUtils");
|
|
23
|
+
const defaultLogger_1 = require("../../logging/defaultLogger");
|
|
24
|
+
const logger = (0, defaultLogger_1.createDefaultLogger)();
|
|
23
25
|
/**
|
|
24
26
|
* Prompt length limits per model
|
|
25
27
|
*/
|
|
@@ -94,7 +96,7 @@ class OpenAIImageAdapter {
|
|
|
94
96
|
// dall-e-2/dall-e-3: use traditional parameters
|
|
95
97
|
this.addDalleParams(params, settings);
|
|
96
98
|
}
|
|
97
|
-
|
|
99
|
+
logger.debug(`OpenAI Image API call for model: ${request.modelId}`, {
|
|
98
100
|
model: params.model,
|
|
99
101
|
promptLength: resolvedPrompt.length,
|
|
100
102
|
n: params.n,
|
|
@@ -105,12 +107,12 @@ class OpenAIImageAdapter {
|
|
|
105
107
|
if (!response.data || response.data.length === 0) {
|
|
106
108
|
throw new Error('OpenAI API returned no images in response');
|
|
107
109
|
}
|
|
108
|
-
|
|
110
|
+
logger.info(`OpenAI Image API call successful, generated ${response.data.length} images`);
|
|
109
111
|
// Process response
|
|
110
112
|
return await this.processResponse(response, request, isGptImageModel);
|
|
111
113
|
}
|
|
112
114
|
catch (error) {
|
|
113
|
-
|
|
115
|
+
logger.error('OpenAI Image API error:', error);
|
|
114
116
|
throw this.handleError(error, request);
|
|
115
117
|
}
|
|
116
118
|
}
|
|
@@ -369,50 +369,6 @@
|
|
|
369
369
|
}
|
|
370
370
|
}
|
|
371
371
|
},
|
|
372
|
-
{
|
|
373
|
-
"id": "google-gemini-2.0-flash-default",
|
|
374
|
-
"displayName": "Google - Gemini 2.0 Flash",
|
|
375
|
-
"description": "Default preset for Gemini 2.0 Flash.",
|
|
376
|
-
"providerId": "gemini",
|
|
377
|
-
"modelId": "gemini-2.0-flash",
|
|
378
|
-
"settings": {
|
|
379
|
-
"temperature": 0.7,
|
|
380
|
-
"geminiSafetySettings": [
|
|
381
|
-
{ "category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE" },
|
|
382
|
-
{
|
|
383
|
-
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
|
|
384
|
-
"threshold": "BLOCK_NONE"
|
|
385
|
-
},
|
|
386
|
-
{
|
|
387
|
-
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
|
|
388
|
-
"threshold": "BLOCK_NONE"
|
|
389
|
-
},
|
|
390
|
-
{ "category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE" }
|
|
391
|
-
]
|
|
392
|
-
}
|
|
393
|
-
},
|
|
394
|
-
{
|
|
395
|
-
"id": "google-gemini-2.0-flash-lite-default",
|
|
396
|
-
"displayName": "Google - Gemini 2.0 Flash Lite",
|
|
397
|
-
"description": "Default preset for Gemini 2.0 Flash Lite.",
|
|
398
|
-
"providerId": "gemini",
|
|
399
|
-
"modelId": "gemini-2.0-flash-lite",
|
|
400
|
-
"settings": {
|
|
401
|
-
"temperature": 0.7,
|
|
402
|
-
"geminiSafetySettings": [
|
|
403
|
-
{ "category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE" },
|
|
404
|
-
{
|
|
405
|
-
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
|
|
406
|
-
"threshold": "BLOCK_NONE"
|
|
407
|
-
},
|
|
408
|
-
{
|
|
409
|
-
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
|
|
410
|
-
"threshold": "BLOCK_NONE"
|
|
411
|
-
},
|
|
412
|
-
{ "category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE" }
|
|
413
|
-
]
|
|
414
|
-
}
|
|
415
|
-
},
|
|
416
372
|
{
|
|
417
373
|
"id": "google-gemma-3-27b-default",
|
|
418
374
|
"displayName": "Google - Gemma 3 27B (Free)",
|
|
@@ -598,5 +554,45 @@
|
|
|
598
554
|
"settings": {
|
|
599
555
|
"temperature": 0.7
|
|
600
556
|
}
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
"id": "openrouter-gemma-3-27b-free",
|
|
560
|
+
"displayName": "OpenRouter - Gemma 3 27B (Free)",
|
|
561
|
+
"description": "Google's Gemma 3 27B via OpenRouter free tier.",
|
|
562
|
+
"providerId": "openrouter",
|
|
563
|
+
"modelId": "google/gemma-3-27b-it:free",
|
|
564
|
+
"settings": {
|
|
565
|
+
"temperature": 0.7
|
|
566
|
+
}
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
"id": "openrouter-mistral-small-3.1-free",
|
|
570
|
+
"displayName": "OpenRouter - Mistral Small 3.1 (Free)",
|
|
571
|
+
"description": "Mistral Small 3.1 24B via OpenRouter free tier.",
|
|
572
|
+
"providerId": "openrouter",
|
|
573
|
+
"modelId": "mistralai/mistral-small-3.1-24b-instruct:free",
|
|
574
|
+
"settings": {
|
|
575
|
+
"temperature": 0.7
|
|
576
|
+
}
|
|
577
|
+
},
|
|
578
|
+
{
|
|
579
|
+
"id": "mistral-small-default",
|
|
580
|
+
"displayName": "Mistral - Small",
|
|
581
|
+
"description": "Cost-effective Mistral Small model for general tasks.",
|
|
582
|
+
"providerId": "mistral",
|
|
583
|
+
"modelId": "mistral-small-latest",
|
|
584
|
+
"settings": {
|
|
585
|
+
"temperature": 0.7
|
|
586
|
+
}
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
"id": "mistral-codestral-default",
|
|
590
|
+
"displayName": "Mistral - Codestral",
|
|
591
|
+
"description": "Specialized model for code generation with lower temperature.",
|
|
592
|
+
"providerId": "mistral",
|
|
593
|
+
"modelId": "codestral-2501",
|
|
594
|
+
"settings": {
|
|
595
|
+
"temperature": 0.3
|
|
596
|
+
}
|
|
601
597
|
}
|
|
602
598
|
]
|
|
@@ -10,6 +10,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.ImageService = void 0;
|
|
13
|
+
const defaultLogger_1 = require("../logging/defaultLogger");
|
|
13
14
|
const config_1 = require("./config");
|
|
14
15
|
const image_presets_json_1 = __importDefault(require("../config/image-presets.json"));
|
|
15
16
|
const PresetManager_1 = require("../shared/services/PresetManager");
|
|
@@ -28,13 +29,15 @@ const defaultImagePresets = image_presets_json_1.default;
|
|
|
28
29
|
class ImageService {
|
|
29
30
|
constructor(getApiKey, options = {}) {
|
|
30
31
|
this.getApiKey = getApiKey;
|
|
32
|
+
// Initialize logger - custom logger takes precedence over logLevel
|
|
33
|
+
this.logger = options.logger ?? (0, defaultLogger_1.createDefaultLogger)(options.logLevel);
|
|
31
34
|
// Initialize helper services
|
|
32
35
|
this.presetManager = new PresetManager_1.PresetManager(defaultImagePresets, options.presets, options.presetMode);
|
|
33
36
|
// Initialize adapter registry with fallback
|
|
34
37
|
this.adapterRegistry = new AdapterRegistry_1.AdapterRegistry({
|
|
35
38
|
supportedProviders: config_1.SUPPORTED_IMAGE_PROVIDERS,
|
|
36
39
|
fallbackAdapter: new MockImageAdapter_1.MockImageAdapter(),
|
|
37
|
-
});
|
|
40
|
+
}, this.logger);
|
|
38
41
|
// Register OpenAI adapter
|
|
39
42
|
const openaiConfig = config_1.IMAGE_ADAPTER_CONFIGS['openai-images'];
|
|
40
43
|
const openaiBaseURL = options.baseUrls?.['openai-images'] || openaiConfig.baseURL;
|
|
@@ -58,7 +61,7 @@ class ImageService {
|
|
|
58
61
|
this.requestValidator = new ImageRequestValidator_1.ImageRequestValidator();
|
|
59
62
|
this.settingsResolver = new ImageSettingsResolver_1.ImageSettingsResolver();
|
|
60
63
|
this.modelResolver = new ImageModelResolver_1.ImageModelResolver(this.presetManager);
|
|
61
|
-
|
|
64
|
+
this.logger.debug('ImageService: Initialized with OpenAI Images and genai-electron adapters');
|
|
62
65
|
}
|
|
63
66
|
/**
|
|
64
67
|
* Generates images based on the request
|
|
@@ -67,7 +70,7 @@ class ImageService {
|
|
|
67
70
|
* @returns Promise resolving to response or error
|
|
68
71
|
*/
|
|
69
72
|
async generateImage(request) {
|
|
70
|
-
|
|
73
|
+
this.logger.info('ImageService.generateImage called');
|
|
71
74
|
try {
|
|
72
75
|
// Resolve model information
|
|
73
76
|
const resolved = this.modelResolver.resolve(request);
|
|
@@ -114,18 +117,18 @@ class ImageService {
|
|
|
114
117
|
};
|
|
115
118
|
}
|
|
116
119
|
// Generate images
|
|
117
|
-
|
|
120
|
+
this.logger.info(`ImageService: Calling adapter for provider: ${providerId}`);
|
|
118
121
|
const response = await adapter.generate({
|
|
119
122
|
request: fullRequest,
|
|
120
123
|
resolvedPrompt,
|
|
121
124
|
settings: resolvedSettings,
|
|
122
125
|
apiKey,
|
|
123
126
|
});
|
|
124
|
-
|
|
127
|
+
this.logger.info('ImageService: Image generation completed successfully');
|
|
125
128
|
return response;
|
|
126
129
|
}
|
|
127
130
|
catch (error) {
|
|
128
|
-
|
|
131
|
+
this.logger.error('ImageService: Error during image generation:', error);
|
|
129
132
|
return {
|
|
130
133
|
object: 'error',
|
|
131
134
|
providerId: providerId,
|
|
@@ -142,7 +145,7 @@ class ImageService {
|
|
|
142
145
|
}
|
|
143
146
|
}
|
|
144
147
|
catch (error) {
|
|
145
|
-
|
|
148
|
+
this.logger.error('ImageService: Unexpected error:', error);
|
|
146
149
|
const req = request;
|
|
147
150
|
return {
|
|
148
151
|
object: 'error',
|
|
@@ -163,7 +166,7 @@ class ImageService {
|
|
|
163
166
|
* @returns Promise resolving to array of provider information
|
|
164
167
|
*/
|
|
165
168
|
async getProviders() {
|
|
166
|
-
|
|
169
|
+
this.logger.debug('ImageService.getProviders called');
|
|
167
170
|
return [...config_1.SUPPORTED_IMAGE_PROVIDERS];
|
|
168
171
|
}
|
|
169
172
|
/**
|
|
@@ -173,9 +176,9 @@ class ImageService {
|
|
|
173
176
|
* @returns Promise resolving to array of model information
|
|
174
177
|
*/
|
|
175
178
|
async getModels(providerId) {
|
|
176
|
-
|
|
179
|
+
this.logger.debug(`ImageService.getModels called for provider: ${providerId}`);
|
|
177
180
|
const models = (0, config_1.getImageModelsByProvider)(providerId);
|
|
178
|
-
|
|
181
|
+
this.logger.debug(`ImageService: Found ${models.length} models for provider: ${providerId}`);
|
|
179
182
|
return [...models];
|
|
180
183
|
}
|
|
181
184
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,10 @@ export { LlamaCppClientAdapter } from "./llm/clients/LlamaCppClientAdapter";
|
|
|
9
9
|
export { LlamaCppServerClient } from "./llm/clients/LlamaCppServerClient";
|
|
10
10
|
export type { LlamaCppClientConfig, } from "./llm/clients/LlamaCppClientAdapter";
|
|
11
11
|
export type { LlamaCppHealthResponse, LlamaCppTokenizeResponse, LlamaCppDetokenizeResponse, LlamaCppEmbeddingResponse, LlamaCppInfillResponse, LlamaCppPropsResponse, LlamaCppMetricsResponse, LlamaCppSlot, LlamaCppSlotsResponse, LlamaCppModel, LlamaCppModelsResponse, } from "./llm/clients/LlamaCppServerClient";
|
|
12
|
+
export { OpenRouterClientAdapter } from "./llm/clients/OpenRouterClientAdapter";
|
|
13
|
+
export type { OpenRouterClientConfig } from "./llm/clients/OpenRouterClientAdapter";
|
|
14
|
+
export { MistralClientAdapter } from "./llm/clients/MistralClientAdapter";
|
|
15
|
+
export type { MistralClientConfig } from "./llm/clients/MistralClientAdapter";
|
|
12
16
|
export { ImageService } from "./image/ImageService";
|
|
13
17
|
export type { ImageProviderId, ImageMimeType, ImageResponseFormat, ImageQuality, ImageStyle, DiffusionSampler, ImageProgressStage, ImageProgressCallback, DiffusionSettings, OpenAISpecificSettings, ImageGenerationSettings, ResolvedImageGenerationSettings, ImageUsage, GeneratedImage, ImageGenerationRequestBase, ImageGenerationRequest, ImageGenerationRequestWithPreset, ImageGenerationResponse, ImageFailureResponse, ImageProviderCapabilities, ImageModelInfo, ImageProviderInfo, ImagePreset, ImageProviderAdapterConfig, ImageProviderAdapter, ImageServiceOptions, CreatePromptResult, } from "./types/image";
|
|
14
18
|
export { renderTemplate } from "./prompting/template";
|
|
@@ -17,3 +21,5 @@ export { parseStructuredContent, parseRoleTags, extractInitialTaggedContent, par
|
|
|
17
21
|
export type { TemplateMetadata } from "./prompting/parser";
|
|
18
22
|
export { createFallbackModelInfo, detectGgufCapabilities, KNOWN_GGUF_MODELS } from "./llm/config";
|
|
19
23
|
export type { GgufModelPattern } from "./llm/config";
|
|
24
|
+
export type { Logger, LogLevel, LoggingConfig } from "./logging/types";
|
|
25
|
+
export { createDefaultLogger, DEFAULT_LOG_LEVEL, silentLogger } from "./logging/defaultLogger";
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.KNOWN_GGUF_MODELS = exports.detectGgufCapabilities = exports.createFallbackModelInfo = exports.parseTemplateWithMetadata = exports.extractInitialTaggedContent = exports.parseRoleTags = exports.parseStructuredContent = exports.extractRandomVariables = exports.getSmartPreview = exports.countTokens = exports.renderTemplate = exports.ImageService = exports.LlamaCppServerClient = exports.LlamaCppClientAdapter = exports.fromEnvironment = exports.LLMService = void 0;
|
|
17
|
+
exports.silentLogger = exports.DEFAULT_LOG_LEVEL = exports.createDefaultLogger = exports.KNOWN_GGUF_MODELS = exports.detectGgufCapabilities = exports.createFallbackModelInfo = exports.parseTemplateWithMetadata = exports.extractInitialTaggedContent = exports.parseRoleTags = exports.parseStructuredContent = exports.extractRandomVariables = exports.getSmartPreview = exports.countTokens = exports.renderTemplate = exports.ImageService = exports.MistralClientAdapter = exports.OpenRouterClientAdapter = exports.LlamaCppServerClient = exports.LlamaCppClientAdapter = exports.fromEnvironment = exports.LLMService = void 0;
|
|
18
18
|
// --- LLM Service ---
|
|
19
19
|
var LLMService_1 = require("./llm/LLMService");
|
|
20
20
|
Object.defineProperty(exports, "LLMService", { enumerable: true, get: function () { return LLMService_1.LLMService; } });
|
|
@@ -30,6 +30,12 @@ var LlamaCppClientAdapter_1 = require("./llm/clients/LlamaCppClientAdapter");
|
|
|
30
30
|
Object.defineProperty(exports, "LlamaCppClientAdapter", { enumerable: true, get: function () { return LlamaCppClientAdapter_1.LlamaCppClientAdapter; } });
|
|
31
31
|
var LlamaCppServerClient_1 = require("./llm/clients/LlamaCppServerClient");
|
|
32
32
|
Object.defineProperty(exports, "LlamaCppServerClient", { enumerable: true, get: function () { return LlamaCppServerClient_1.LlamaCppServerClient; } });
|
|
33
|
+
// --- OpenRouter Integration ---
|
|
34
|
+
var OpenRouterClientAdapter_1 = require("./llm/clients/OpenRouterClientAdapter");
|
|
35
|
+
Object.defineProperty(exports, "OpenRouterClientAdapter", { enumerable: true, get: function () { return OpenRouterClientAdapter_1.OpenRouterClientAdapter; } });
|
|
36
|
+
// --- Mistral Integration ---
|
|
37
|
+
var MistralClientAdapter_1 = require("./llm/clients/MistralClientAdapter");
|
|
38
|
+
Object.defineProperty(exports, "MistralClientAdapter", { enumerable: true, get: function () { return MistralClientAdapter_1.MistralClientAdapter; } });
|
|
33
39
|
// --- Image Generation ---
|
|
34
40
|
// Export Image Service
|
|
35
41
|
var ImageService_1 = require("./image/ImageService");
|
|
@@ -50,3 +56,7 @@ var config_1 = require("./llm/config");
|
|
|
50
56
|
Object.defineProperty(exports, "createFallbackModelInfo", { enumerable: true, get: function () { return config_1.createFallbackModelInfo; } });
|
|
51
57
|
Object.defineProperty(exports, "detectGgufCapabilities", { enumerable: true, get: function () { return config_1.detectGgufCapabilities; } });
|
|
52
58
|
Object.defineProperty(exports, "KNOWN_GGUF_MODELS", { enumerable: true, get: function () { return config_1.KNOWN_GGUF_MODELS; } });
|
|
59
|
+
var defaultLogger_1 = require("./logging/defaultLogger");
|
|
60
|
+
Object.defineProperty(exports, "createDefaultLogger", { enumerable: true, get: function () { return defaultLogger_1.createDefaultLogger; } });
|
|
61
|
+
Object.defineProperty(exports, "DEFAULT_LOG_LEVEL", { enumerable: true, get: function () { return defaultLogger_1.DEFAULT_LOG_LEVEL; } });
|
|
62
|
+
Object.defineProperty(exports, "silentLogger", { enumerable: true, get: function () { return defaultLogger_1.silentLogger; } });
|
package/dist/llm/LLMService.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ApiKeyProvider, PresetMode } from '../types';
|
|
2
|
+
import type { Logger, LogLevel } from '../logging/types';
|
|
2
3
|
import type { LLMChatRequest, LLMChatRequestWithPreset, LLMResponse, LLMFailureResponse, ProviderInfo, ModelInfo, ApiProviderId, LLMSettings, ModelContext, LLMMessage } from "./types";
|
|
3
4
|
import type { ModelPreset } from "../types/presets";
|
|
4
5
|
export type { PresetMode };
|
|
@@ -10,6 +11,10 @@ export interface LLMServiceOptions {
|
|
|
10
11
|
presets?: ModelPreset[];
|
|
11
12
|
/** The strategy for integrating custom presets. Defaults to 'extend'. */
|
|
12
13
|
presetMode?: PresetMode;
|
|
14
|
+
/** Log level for filtering messages. Defaults to GENAI_LITE_LOG_LEVEL env var or 'warn'. */
|
|
15
|
+
logLevel?: LogLevel;
|
|
16
|
+
/** Custom logger implementation. If provided, logLevel is ignored. */
|
|
17
|
+
logger?: Logger;
|
|
13
18
|
}
|
|
14
19
|
/**
|
|
15
20
|
* Result from createMessages method
|
|
@@ -35,6 +40,7 @@ export interface CreateMessagesResult {
|
|
|
35
40
|
*/
|
|
36
41
|
export declare class LLMService {
|
|
37
42
|
private getApiKey;
|
|
43
|
+
private logger;
|
|
38
44
|
private presetManager;
|
|
39
45
|
private adapterRegistry;
|
|
40
46
|
private requestValidator;
|
package/dist/llm/LLMService.js
CHANGED
|
@@ -6,6 +6,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
6
6
|
};
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
8
|
exports.LLMService = void 0;
|
|
9
|
+
const defaultLogger_1 = require("../logging/defaultLogger");
|
|
9
10
|
const config_1 = require("./config");
|
|
10
11
|
const template_1 = require("../prompting/template");
|
|
11
12
|
const parser_1 = require("../prompting/parser");
|
|
@@ -31,17 +32,19 @@ const ModelResolver_1 = require("./services/ModelResolver");
|
|
|
31
32
|
class LLMService {
|
|
32
33
|
constructor(getApiKey, options = {}) {
|
|
33
34
|
this.getApiKey = getApiKey;
|
|
34
|
-
// Initialize
|
|
35
|
+
// Initialize logger - custom logger takes precedence over logLevel
|
|
36
|
+
this.logger = options.logger ?? (0, defaultLogger_1.createDefaultLogger)(options.logLevel);
|
|
37
|
+
// Initialize services with logger
|
|
35
38
|
this.presetManager = new PresetManager_1.PresetManager(llm_presets_json_1.default, options.presets, options.presetMode);
|
|
36
39
|
this.adapterRegistry = new AdapterRegistry_1.AdapterRegistry({
|
|
37
40
|
supportedProviders: config_1.SUPPORTED_PROVIDERS,
|
|
38
41
|
fallbackAdapter: new MockClientAdapter_1.MockClientAdapter(),
|
|
39
42
|
adapterConstructors: config_1.ADAPTER_CONSTRUCTORS,
|
|
40
43
|
adapterConfigs: config_1.ADAPTER_CONFIGS,
|
|
41
|
-
});
|
|
44
|
+
}, this.logger);
|
|
42
45
|
this.requestValidator = new RequestValidator_1.RequestValidator();
|
|
43
|
-
this.settingsManager = new SettingsManager_1.SettingsManager();
|
|
44
|
-
this.modelResolver = new ModelResolver_1.ModelResolver(this.presetManager, this.adapterRegistry);
|
|
46
|
+
this.settingsManager = new SettingsManager_1.SettingsManager(this.logger);
|
|
47
|
+
this.modelResolver = new ModelResolver_1.ModelResolver(this.presetManager, this.adapterRegistry, this.logger);
|
|
45
48
|
}
|
|
46
49
|
/**
|
|
47
50
|
* Gets list of supported LLM providers
|
|
@@ -49,7 +52,7 @@ class LLMService {
|
|
|
49
52
|
* @returns Promise resolving to array of provider information
|
|
50
53
|
*/
|
|
51
54
|
async getProviders() {
|
|
52
|
-
|
|
55
|
+
this.logger.debug("LLMService.getProviders called");
|
|
53
56
|
return [...config_1.SUPPORTED_PROVIDERS]; // Return a copy to prevent external modification
|
|
54
57
|
}
|
|
55
58
|
/**
|
|
@@ -59,14 +62,14 @@ class LLMService {
|
|
|
59
62
|
* @returns Promise resolving to array of model information
|
|
60
63
|
*/
|
|
61
64
|
async getModels(providerId) {
|
|
62
|
-
|
|
65
|
+
this.logger.debug(`LLMService.getModels called for provider: ${providerId}`);
|
|
63
66
|
// Validate provider exists
|
|
64
67
|
const models = (0, config_1.getModelsByProvider)(providerId);
|
|
65
68
|
if (models.length === 0) {
|
|
66
|
-
|
|
69
|
+
this.logger.warn(`Requested models for unsupported provider: ${providerId}`);
|
|
67
70
|
return [];
|
|
68
71
|
}
|
|
69
|
-
|
|
72
|
+
this.logger.debug(`Found ${models.length} models for provider: ${providerId}`);
|
|
70
73
|
return [...models]; // Return a copy to prevent external modification
|
|
71
74
|
}
|
|
72
75
|
/**
|
|
@@ -76,7 +79,7 @@ class LLMService {
|
|
|
76
79
|
* @returns Promise resolving to either success or failure response
|
|
77
80
|
*/
|
|
78
81
|
async sendMessage(request) {
|
|
79
|
-
|
|
82
|
+
this.logger.info(`LLMService.sendMessage called with presetId: ${request.presetId}, provider: ${request.providerId}, model: ${request.modelId}`);
|
|
80
83
|
try {
|
|
81
84
|
// Resolve model information from preset or direct IDs
|
|
82
85
|
const resolved = await this.modelResolver.resolve(request);
|
|
@@ -130,7 +133,7 @@ class LLMService {
|
|
|
130
133
|
...resolvedRequest,
|
|
131
134
|
settings: filteredSettings,
|
|
132
135
|
};
|
|
133
|
-
|
|
136
|
+
this.logger.debug(`Processing LLM request with (potentially filtered) settings:`, {
|
|
134
137
|
provider: providerId,
|
|
135
138
|
model: modelId,
|
|
136
139
|
settings: filteredSettings,
|
|
@@ -166,7 +169,7 @@ class LLMService {
|
|
|
166
169
|
object: "error",
|
|
167
170
|
};
|
|
168
171
|
}
|
|
169
|
-
|
|
172
|
+
this.logger.info(`Making LLM request with ${clientAdapter.constructor.name} for provider: ${providerId}`);
|
|
170
173
|
const result = await clientAdapter.sendMessage(internalRequest, apiKey);
|
|
171
174
|
// Post-process for thinking tag fallback
|
|
172
175
|
// This feature extracts reasoning from XML tags when native reasoning is not active.
|
|
@@ -186,7 +189,7 @@ class LLMService {
|
|
|
186
189
|
const { extracted, remaining } = (0, parser_1.extractInitialTaggedContent)(choice.message.content, tagName);
|
|
187
190
|
if (extracted !== null) {
|
|
188
191
|
// Success: thinking tag found
|
|
189
|
-
|
|
192
|
+
this.logger.debug(`Extracted <${tagName}> block from response.`);
|
|
190
193
|
// Handle the edge case: append to existing reasoning if present (e.g., native reasoning + thinking tags)
|
|
191
194
|
const existingReasoning = choice.reasoning || '';
|
|
192
195
|
if (existingReasoning) {
|
|
@@ -234,11 +237,11 @@ class LLMService {
|
|
|
234
237
|
}
|
|
235
238
|
}
|
|
236
239
|
}
|
|
237
|
-
|
|
240
|
+
this.logger.info(`LLM request completed successfully for model: ${modelId}`);
|
|
238
241
|
return result;
|
|
239
242
|
}
|
|
240
243
|
catch (error) {
|
|
241
|
-
|
|
244
|
+
this.logger.error("Error in LLMService.sendMessage:", error);
|
|
242
245
|
return {
|
|
243
246
|
provider: providerId,
|
|
244
247
|
model: modelId,
|
|
@@ -255,7 +258,7 @@ class LLMService {
|
|
|
255
258
|
}
|
|
256
259
|
}
|
|
257
260
|
catch (error) {
|
|
258
|
-
|
|
261
|
+
this.logger.error("Error in LLMService.sendMessage (outer):", error);
|
|
259
262
|
return {
|
|
260
263
|
provider: request.providerId || request.presetId || 'unknown',
|
|
261
264
|
model: request.modelId || request.presetId || 'unknown',
|
|
@@ -328,7 +331,7 @@ class LLMService {
|
|
|
328
331
|
* ```
|
|
329
332
|
*/
|
|
330
333
|
async createMessages(options) {
|
|
331
|
-
|
|
334
|
+
this.logger.debug('LLMService.createMessages called');
|
|
332
335
|
// NEW: Step 1 - Parse the template for metadata and content
|
|
333
336
|
const { metadata, content: templateContent } = (0, parser_1.parseTemplateWithMetadata)(options.template);
|
|
334
337
|
// Validate the settings from the template
|
|
@@ -345,7 +348,7 @@ class LLMService {
|
|
|
345
348
|
});
|
|
346
349
|
if (resolved.error) {
|
|
347
350
|
// If resolution fails, proceed without model context
|
|
348
|
-
|
|
351
|
+
this.logger.warn('Model resolution failed, proceeding without model context:', resolved.error);
|
|
349
352
|
}
|
|
350
353
|
else {
|
|
351
354
|
const { providerId, modelId, modelInfo, settings } = resolved;
|
|
@@ -9,6 +9,9 @@ exports.AnthropicClientAdapter = void 0;
|
|
|
9
9
|
const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
|
|
10
10
|
const types_1 = require("./types");
|
|
11
11
|
const errorUtils_1 = require("../../shared/adapters/errorUtils");
|
|
12
|
+
const systemMessageUtils_1 = require("../../shared/adapters/systemMessageUtils");
|
|
13
|
+
const defaultLogger_1 = require("../../logging/defaultLogger");
|
|
14
|
+
const logger = (0, defaultLogger_1.createDefaultLogger)();
|
|
12
15
|
/**
|
|
13
16
|
* Client adapter for Anthropic API integration
|
|
14
17
|
*
|
|
@@ -93,8 +96,8 @@ class AnthropicClientAdapter {
|
|
|
93
96
|
};
|
|
94
97
|
}
|
|
95
98
|
}
|
|
96
|
-
|
|
97
|
-
|
|
99
|
+
logger.info(`Making Anthropic API call for model: ${request.modelId}`);
|
|
100
|
+
logger.debug(`Anthropic API parameters:`, {
|
|
98
101
|
model: messageParams.model,
|
|
99
102
|
temperature: messageParams.temperature,
|
|
100
103
|
max_tokens: messageParams.max_tokens,
|
|
@@ -105,12 +108,12 @@ class AnthropicClientAdapter {
|
|
|
105
108
|
});
|
|
106
109
|
// Make the API call
|
|
107
110
|
const completion = await anthropic.messages.create(messageParams);
|
|
108
|
-
|
|
111
|
+
logger.info(`Anthropic API call successful, response ID: ${completion.id}`);
|
|
109
112
|
// Convert to standardized response format
|
|
110
113
|
return this.createSuccessResponse(completion, request);
|
|
111
114
|
}
|
|
112
115
|
catch (error) {
|
|
113
|
-
|
|
116
|
+
logger.error("Anthropic API error:", error);
|
|
114
117
|
return this.createErrorResponse(error, request);
|
|
115
118
|
}
|
|
116
119
|
}
|
|
@@ -142,18 +145,14 @@ class AnthropicClientAdapter {
|
|
|
142
145
|
*/
|
|
143
146
|
formatMessagesForAnthropic(request) {
|
|
144
147
|
const messages = [];
|
|
145
|
-
|
|
148
|
+
const inlineSystemMessages = [];
|
|
149
|
+
// Check if model supports system messages
|
|
150
|
+
const supportsSystem = request.settings.supportsSystemMessage !== false;
|
|
146
151
|
// Process conversation messages
|
|
147
152
|
for (const message of request.messages) {
|
|
148
153
|
if (message.role === "system") {
|
|
149
|
-
//
|
|
150
|
-
|
|
151
|
-
if (systemMessage) {
|
|
152
|
-
systemMessage += "\n\n" + message.content;
|
|
153
|
-
}
|
|
154
|
-
else {
|
|
155
|
-
systemMessage = message.content;
|
|
156
|
-
}
|
|
154
|
+
// Collect inline system messages
|
|
155
|
+
inlineSystemMessages.push(message.content);
|
|
157
156
|
}
|
|
158
157
|
else if (message.role === "user") {
|
|
159
158
|
messages.push({
|
|
@@ -168,10 +167,32 @@ class AnthropicClientAdapter {
|
|
|
168
167
|
});
|
|
169
168
|
}
|
|
170
169
|
}
|
|
170
|
+
// Use shared utility to collect and combine system content
|
|
171
|
+
const { combinedSystemContent, useNativeSystemMessage } = (0, systemMessageUtils_1.collectSystemContent)(request.systemMessage, inlineSystemMessages, supportsSystem);
|
|
172
|
+
let systemMessage;
|
|
173
|
+
if (combinedSystemContent) {
|
|
174
|
+
if (useNativeSystemMessage) {
|
|
175
|
+
// Model supports system messages - use Anthropic's system parameter
|
|
176
|
+
systemMessage = combinedSystemContent;
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
// Model doesn't support system messages - prepend to first user message
|
|
180
|
+
const simpleMessages = messages.map((m) => ({
|
|
181
|
+
role: m.role,
|
|
182
|
+
content: m.content,
|
|
183
|
+
}));
|
|
184
|
+
const modifiedIndex = (0, systemMessageUtils_1.prependSystemToFirstUserMessage)(simpleMessages, combinedSystemContent, request.settings.systemMessageFallback);
|
|
185
|
+
if (modifiedIndex !== -1) {
|
|
186
|
+
messages[modifiedIndex].content = simpleMessages[modifiedIndex].content;
|
|
187
|
+
logger.debug(`Model ${request.modelId} doesn't support system messages - prepended to first user message`);
|
|
188
|
+
}
|
|
189
|
+
// Don't set systemMessage - it stays undefined
|
|
190
|
+
}
|
|
191
|
+
}
|
|
171
192
|
// Anthropic requires messages to start with 'user' role
|
|
172
193
|
// If the first message is not from user, we need to handle this
|
|
173
194
|
if (messages.length > 0 && messages[0].role !== "user") {
|
|
174
|
-
|
|
195
|
+
logger.warn("Anthropic API requires first message to be from user. Adjusting message order.");
|
|
175
196
|
// Find the first user message and move it to the front, or create a default one
|
|
176
197
|
const firstUserIndex = messages.findIndex((msg) => msg.role === "user");
|
|
177
198
|
if (firstUserIndex > 0) {
|
|
@@ -213,7 +234,7 @@ class AnthropicClientAdapter {
|
|
|
213
234
|
// If roles don't alternate properly, we might need to combine messages
|
|
214
235
|
// or insert a placeholder. For now, we'll skip non-alternating messages
|
|
215
236
|
// and log a warning.
|
|
216
|
-
|
|
237
|
+
logger.warn(`Skipping message with unexpected role: expected ${expectedRole}, got ${message.role}`);
|
|
217
238
|
}
|
|
218
239
|
}
|
|
219
240
|
return cleanedMessages;
|