genai-lite 0.4.2 → 0.5.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 (53) hide show
  1. package/README.md +399 -22
  2. package/dist/adapters/image/ElectronDiffusionAdapter.d.ts +10 -0
  3. package/dist/adapters/image/ElectronDiffusionAdapter.js +11 -0
  4. package/dist/adapters/image/GenaiElectronImageAdapter.d.ts +69 -0
  5. package/dist/adapters/image/GenaiElectronImageAdapter.js +273 -0
  6. package/dist/adapters/image/MockImageAdapter.d.ts +23 -0
  7. package/dist/adapters/image/MockImageAdapter.js +55 -0
  8. package/dist/adapters/image/OpenAIImageAdapter.d.ts +62 -0
  9. package/dist/adapters/image/OpenAIImageAdapter.js +303 -0
  10. package/dist/config/image-presets.json +194 -0
  11. package/dist/config/llm-presets.json +326 -0
  12. package/dist/image/ImageService.d.ts +53 -0
  13. package/dist/image/ImageService.js +199 -0
  14. package/dist/image/config.d.ts +48 -0
  15. package/dist/image/config.js +221 -0
  16. package/dist/image/services/ImageAdapterRegistry.d.ts +61 -0
  17. package/dist/image/services/ImageAdapterRegistry.js +95 -0
  18. package/dist/image/services/ImageModelResolver.d.ts +26 -0
  19. package/dist/image/services/ImageModelResolver.js +98 -0
  20. package/dist/image/services/ImagePresetManager.d.ts +27 -0
  21. package/dist/image/services/ImagePresetManager.js +52 -0
  22. package/dist/image/services/ImageRequestValidator.d.ts +37 -0
  23. package/dist/image/services/ImageRequestValidator.js +133 -0
  24. package/dist/image/services/ImageSettingsResolver.d.ts +25 -0
  25. package/dist/image/services/ImageSettingsResolver.js +76 -0
  26. package/dist/index.d.ts +7 -4
  27. package/dist/index.js +7 -1
  28. package/dist/llm/LLMService.d.ts +3 -4
  29. package/dist/llm/LLMService.js +17 -7
  30. package/dist/llm/clients/AnthropicClientAdapter.js +2 -2
  31. package/dist/llm/clients/GeminiClientAdapter.js +2 -2
  32. package/dist/llm/clients/LlamaCppClientAdapter.d.ts +20 -2
  33. package/dist/llm/clients/LlamaCppClientAdapter.js +91 -11
  34. package/dist/llm/clients/LlamaCppServerClient.d.ts +36 -0
  35. package/dist/llm/clients/LlamaCppServerClient.js +25 -0
  36. package/dist/llm/clients/OpenAIClientAdapter.js +2 -2
  37. package/dist/llm/config.d.ts +43 -2
  38. package/dist/llm/config.js +171 -32
  39. package/dist/llm/services/ModelResolver.d.ts +8 -4
  40. package/dist/llm/services/ModelResolver.js +22 -5
  41. package/dist/providers/fromEnvironment.d.ts +5 -1
  42. package/dist/providers/fromEnvironment.js +29 -4
  43. package/dist/shared/adapters/errorUtils.d.ts +26 -0
  44. package/dist/shared/adapters/errorUtils.js +107 -0
  45. package/dist/shared/services/AdapterRegistry.d.ts +89 -0
  46. package/dist/shared/services/AdapterRegistry.js +144 -0
  47. package/dist/shared/services/PresetManager.d.ts +33 -0
  48. package/dist/shared/services/PresetManager.js +56 -0
  49. package/dist/types/image.d.ts +399 -0
  50. package/dist/types/image.js +8 -0
  51. package/dist/types.d.ts +6 -0
  52. package/package.json +1 -1
  53. package/src/config/presets.json +0 -327
@@ -0,0 +1,273 @@
1
+ "use strict";
2
+ /**
3
+ * genai-electron Image Adapter
4
+ *
5
+ * Adapter for local diffusion models via genai-electron's image generation server.
6
+ * Supports stable-diffusion.cpp through HTTP wrapper with async polling for progress.
7
+ *
8
+ * Provider ID: 'genai-electron-images'
9
+ * Default endpoint: http://localhost:8081
10
+ * Configure via: GENAI_ELECTRON_IMAGE_BASE_URL environment variable
11
+ *
12
+ * This adapter uses genai-electron's async image generation API which:
13
+ * - Returns immediately with a generation ID
14
+ * - Allows polling for progress updates
15
+ * - Supports full diffusion settings (negative prompts, steps, samplers, etc.)
16
+ * - Handles batching via count parameter
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.GenaiElectronImageAdapter = void 0;
20
+ const errorUtils_1 = require("../../shared/adapters/errorUtils");
21
+ /**
22
+ * Adapter for genai-electron's local diffusion image generation
23
+ */
24
+ class GenaiElectronImageAdapter {
25
+ constructor(config) {
26
+ this.id = 'genai-electron-images';
27
+ this.supports = {
28
+ supportsMultipleImages: true, // via count parameter
29
+ supportsB64Json: true, // returns base64
30
+ supportsHostedUrls: false, // local generation only
31
+ supportsProgressEvents: true, // via polling
32
+ supportsNegativePrompt: true, // full diffusion support
33
+ defaultModelId: 'sdxl',
34
+ };
35
+ this.baseURL = config?.baseURL || 'http://localhost:8081';
36
+ this.timeout = config?.timeout || 120000; // 120 seconds for diffusion
37
+ this.pollInterval = 500; // Poll every 500ms
38
+ }
39
+ /**
40
+ * Generates images using genai-electron's async API with progress polling
41
+ */
42
+ async generate(config) {
43
+ const { request, resolvedPrompt, settings } = config;
44
+ try {
45
+ // Build request payload
46
+ const payload = this.buildRequestPayload(resolvedPrompt, request, settings);
47
+ console.log(`GenaiElectron Image API: Starting generation`, {
48
+ prompt: resolvedPrompt.substring(0, 100),
49
+ count: payload.count,
50
+ dimensions: `${payload.width}x${payload.height}`,
51
+ steps: payload.steps,
52
+ });
53
+ // Start generation (returns immediately with ID)
54
+ const generationId = await this.startGeneration(payload);
55
+ console.log(`GenaiElectron Image API: Generation started with ID: ${generationId}`);
56
+ // Poll for completion
57
+ const result = await this.pollForCompletion(generationId, settings.diffusion?.onProgress);
58
+ console.log(`GenaiElectron Image API: Generation complete (${result.timeTaken}ms)`);
59
+ // Convert to ImageGenerationResponse
60
+ return this.convertToResponse(result, request);
61
+ }
62
+ catch (error) {
63
+ console.error('GenaiElectron Image API error:', error);
64
+ throw this.handleError(error, request);
65
+ }
66
+ }
67
+ /**
68
+ * Builds the request payload for genai-electron
69
+ */
70
+ buildRequestPayload(prompt, request, settings) {
71
+ const diffusion = settings.diffusion;
72
+ // Use dimensions from base settings (universal for all providers)
73
+ const width = settings.width;
74
+ const height = settings.height;
75
+ return {
76
+ prompt,
77
+ negativePrompt: diffusion?.negativePrompt,
78
+ width,
79
+ height,
80
+ steps: diffusion?.steps || 20,
81
+ cfgScale: diffusion?.cfgScale || 7.5,
82
+ seed: diffusion?.seed, // undefined = random
83
+ sampler: diffusion?.sampler || 'euler_a',
84
+ count: request.count || 1,
85
+ };
86
+ }
87
+ /**
88
+ * Starts generation and returns the generation ID
89
+ */
90
+ async startGeneration(payload) {
91
+ const url = `${this.baseURL}/v1/images/generations`;
92
+ // Create abort controller for timeout
93
+ const controller = new AbortController();
94
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
95
+ try {
96
+ const response = await fetch(url, {
97
+ method: 'POST',
98
+ headers: { 'Content-Type': 'application/json' },
99
+ body: JSON.stringify(payload),
100
+ signal: controller.signal,
101
+ });
102
+ clearTimeout(timeoutId);
103
+ if (!response.ok) {
104
+ const errorText = await response.text();
105
+ throw this.createHttpError(response.status, errorText, url);
106
+ }
107
+ const data = await response.json();
108
+ return data.id;
109
+ }
110
+ catch (error) {
111
+ clearTimeout(timeoutId);
112
+ // Handle AbortError
113
+ if (error.name === 'AbortError') {
114
+ throw new Error(`Request timeout after ${this.timeout}ms (connecting to ${this.baseURL})`);
115
+ }
116
+ throw error;
117
+ }
118
+ }
119
+ /**
120
+ * Polls for generation completion with progress updates
121
+ */
122
+ async pollForCompletion(generationId, onProgress) {
123
+ const url = `${this.baseURL}/v1/images/generations/${generationId}`;
124
+ const startTime = Date.now();
125
+ while (true) {
126
+ // Check overall timeout
127
+ if (Date.now() - startTime > this.timeout) {
128
+ throw new Error(`Generation timeout after ${this.timeout}ms (ID: ${generationId})`);
129
+ }
130
+ // Fetch status
131
+ const response = await fetch(url);
132
+ if (!response.ok) {
133
+ const errorText = await response.text();
134
+ throw this.createHttpError(response.status, errorText, url);
135
+ }
136
+ const state = await response.json();
137
+ // Handle progress updates
138
+ if (state.status === 'in_progress' && state.progress && onProgress) {
139
+ onProgress({
140
+ currentStep: state.progress.currentStep,
141
+ totalSteps: state.progress.totalSteps,
142
+ stage: state.progress.stage,
143
+ percentage: state.progress.percentage,
144
+ });
145
+ }
146
+ // Handle completion
147
+ if (state.status === 'complete') {
148
+ if (!state.result) {
149
+ throw new Error('Generation marked complete but no result available');
150
+ }
151
+ return state.result;
152
+ }
153
+ // Handle error
154
+ if (state.status === 'error') {
155
+ const error = state.error || { message: 'Unknown error', code: 'UNKNOWN_ERROR' };
156
+ throw this.createGenerationError(error.message, error.code);
157
+ }
158
+ // Wait before next poll
159
+ await this.sleep(this.pollInterval);
160
+ }
161
+ }
162
+ /**
163
+ * Converts genai-electron result to ImageGenerationResponse
164
+ */
165
+ convertToResponse(result, request) {
166
+ const images = result.images.map((img, index) => {
167
+ // Convert base64 to Buffer
168
+ const imageBuffer = Buffer.from(img.image, 'base64');
169
+ return {
170
+ index,
171
+ mimeType: 'image/png',
172
+ data: imageBuffer,
173
+ b64Json: img.image, // Preserve base64
174
+ prompt: request.prompt,
175
+ seed: img.seed,
176
+ metadata: {
177
+ width: img.width,
178
+ height: img.height,
179
+ },
180
+ };
181
+ });
182
+ return {
183
+ object: 'image.result',
184
+ created: Math.floor(Date.now() / 1000),
185
+ providerId: this.id,
186
+ modelId: request.modelId,
187
+ data: images,
188
+ usage: {
189
+ cost: 0, // Local generation is free
190
+ credits: result.timeTaken, // Use timeTaken as credits
191
+ },
192
+ };
193
+ }
194
+ /**
195
+ * Creates an HTTP error with context
196
+ */
197
+ createHttpError(status, errorText, url) {
198
+ let errorMessage = `HTTP ${status} error`;
199
+ try {
200
+ const errorData = JSON.parse(errorText);
201
+ if (errorData.error?.message) {
202
+ errorMessage = errorData.error.message;
203
+ }
204
+ }
205
+ catch {
206
+ // Not JSON, use raw text
207
+ if (errorText) {
208
+ errorMessage = `HTTP ${status}: ${errorText}`;
209
+ }
210
+ }
211
+ const error = new Error(`${errorMessage} (${url})`);
212
+ error.status = status;
213
+ error.url = url;
214
+ return error;
215
+ }
216
+ /**
217
+ * Creates a generation error from genai-electron error codes
218
+ */
219
+ createGenerationError(message, code) {
220
+ const error = new Error(`Generation failed: ${message}`);
221
+ error.code = code;
222
+ return error;
223
+ }
224
+ /**
225
+ * Handles errors and converts to standard format
226
+ */
227
+ handleError(error, request) {
228
+ // Use shared error mapping utility
229
+ const mapped = (0, errorUtils_1.getCommonMappedErrorDetails)(error);
230
+ // Enhance error message with context
231
+ let errorMessage = mapped.errorMessage;
232
+ // Special handling for genai-electron specific errors
233
+ if (error.code === 'SERVER_BUSY') {
234
+ errorMessage = 'Image generation server is busy. Wait for current generation to complete.';
235
+ error.type = 'rate_limit_error';
236
+ }
237
+ else if (error.code === 'SERVER_NOT_RUNNING') {
238
+ errorMessage = `Image generation server is not running (connecting to ${this.baseURL})`;
239
+ error.type = 'connection_error';
240
+ }
241
+ else if (error.code === 'BACKEND_ERROR') {
242
+ errorMessage = `Diffusion backend error: ${error.message}`;
243
+ error.type = 'server_error';
244
+ }
245
+ else if (error.code === 'IO_ERROR') {
246
+ errorMessage = `Image I/O error: ${error.message}`;
247
+ error.type = 'server_error';
248
+ }
249
+ // Add baseURL context for network errors
250
+ if (mapped.errorCode === 'NETWORK_ERROR') {
251
+ errorMessage = `${errorMessage} (connecting to ${this.baseURL})`;
252
+ }
253
+ // Add timeout context
254
+ if (errorMessage.includes('timeout')) {
255
+ errorMessage = `${errorMessage}. Try increasing the timeout or reducing generation steps.`;
256
+ }
257
+ // Create enhanced error with all details
258
+ const enhancedError = new Error(errorMessage);
259
+ enhancedError.code = mapped.errorCode;
260
+ enhancedError.type = mapped.errorType;
261
+ enhancedError.status = mapped.status;
262
+ enhancedError.providerId = this.id;
263
+ enhancedError.modelId = request.modelId;
264
+ return enhancedError;
265
+ }
266
+ /**
267
+ * Sleep helper for polling
268
+ */
269
+ sleep(ms) {
270
+ return new Promise((resolve) => setTimeout(resolve, ms));
271
+ }
272
+ }
273
+ exports.GenaiElectronImageAdapter = GenaiElectronImageAdapter;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Mock Image Adapter for testing and fallback
3
+ *
4
+ * This adapter provides a simple mock implementation for testing purposes
5
+ * and serves as a fallback for unsupported providers during development.
6
+ */
7
+ import type { ImageProviderAdapter, ImageGenerationRequest, ImageGenerationResponse, ImageProviderCapabilities, ResolvedImageGenerationSettings } from '../../types/image';
8
+ /**
9
+ * Mock adapter for testing image generation
10
+ */
11
+ export declare class MockImageAdapter implements ImageProviderAdapter {
12
+ readonly id = "mock-image-provider";
13
+ readonly supports: ImageProviderCapabilities;
14
+ /**
15
+ * Generates mock images
16
+ */
17
+ generate(config: {
18
+ request: ImageGenerationRequest;
19
+ resolvedPrompt: string;
20
+ settings: ResolvedImageGenerationSettings;
21
+ apiKey: string | null;
22
+ }): Promise<ImageGenerationResponse>;
23
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ /**
3
+ * Mock Image Adapter for testing and fallback
4
+ *
5
+ * This adapter provides a simple mock implementation for testing purposes
6
+ * and serves as a fallback for unsupported providers during development.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.MockImageAdapter = void 0;
10
+ /**
11
+ * Mock adapter for testing image generation
12
+ */
13
+ class MockImageAdapter {
14
+ constructor() {
15
+ this.id = 'mock-image-provider';
16
+ this.supports = {
17
+ supportsMultipleImages: true,
18
+ supportsB64Json: true,
19
+ supportsHostedUrls: false,
20
+ supportsProgressEvents: false,
21
+ supportsNegativePrompt: true,
22
+ defaultModelId: 'mock-model',
23
+ };
24
+ }
25
+ /**
26
+ * Generates mock images
27
+ */
28
+ async generate(config) {
29
+ const { request, resolvedPrompt } = config;
30
+ const count = request.count || 1;
31
+ // Create mock image data (1x1 PNG)
32
+ const mockImageBuffer = Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==', 'base64');
33
+ // Generate mock images based on count
34
+ const images = Array.from({ length: count }, (_, index) => ({
35
+ index,
36
+ mimeType: 'image/png',
37
+ data: mockImageBuffer,
38
+ prompt: resolvedPrompt,
39
+ seed: Math.floor(Math.random() * 1000000),
40
+ width: 1,
41
+ height: 1,
42
+ }));
43
+ return {
44
+ object: 'image.result',
45
+ created: Math.floor(Date.now() / 1000),
46
+ providerId: request.providerId,
47
+ modelId: request.modelId,
48
+ data: images,
49
+ usage: {
50
+ timeTaken: 100, // Mock timing
51
+ },
52
+ };
53
+ }
54
+ }
55
+ exports.MockImageAdapter = MockImageAdapter;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * OpenAI Images API Adapter
3
+ *
4
+ * Adapter for OpenAI's image generation API (DALL-E and GPT-Image models).
5
+ * Supports the /v1/images/generations endpoint.
6
+ *
7
+ * Supported models:
8
+ * - gpt-image-1-mini (default): Fast, efficient, 32K char prompts
9
+ * - gpt-image-1: Highest quality, 32K char prompts, advanced features
10
+ * - dall-e-3: High quality, 4K char prompts, only n=1
11
+ * - dall-e-2: Standard quality, 1K char prompts
12
+ *
13
+ * Based on: https://platform.openai.com/docs/api-reference/images/create
14
+ */
15
+ import type { ImageProviderAdapter, ImageGenerationRequest, ImageGenerationResponse, ImageProviderCapabilities, ResolvedImageGenerationSettings, ImageProviderAdapterConfig } from '../../types/image';
16
+ /**
17
+ * Adapter for OpenAI's image generation API
18
+ */
19
+ export declare class OpenAIImageAdapter implements ImageProviderAdapter {
20
+ readonly id = "openai-images";
21
+ readonly supports: ImageProviderCapabilities;
22
+ private baseURL?;
23
+ private timeout;
24
+ constructor(config?: ImageProviderAdapterConfig);
25
+ /**
26
+ * Validates OpenAI API key format
27
+ */
28
+ validateApiKey(apiKey: string): boolean;
29
+ /**
30
+ * Generates images using OpenAI's API
31
+ */
32
+ generate(config: {
33
+ request: ImageGenerationRequest;
34
+ resolvedPrompt: string;
35
+ settings: ResolvedImageGenerationSettings;
36
+ apiKey: string | null;
37
+ }): Promise<ImageGenerationResponse>;
38
+ /**
39
+ * Validates prompt length for the given model
40
+ */
41
+ private validatePromptLength;
42
+ /**
43
+ * Converts width and height to OpenAI size format (e.g., "1024x1024")
44
+ */
45
+ private toSizeString;
46
+ /**
47
+ * Adds gpt-image-1 specific parameters to the request
48
+ */
49
+ private addGptImageParams;
50
+ /**
51
+ * Adds dall-e-2/dall-e-3 specific parameters to the request
52
+ */
53
+ private addDalleParams;
54
+ /**
55
+ * Processes the OpenAI API response and converts to ImageGenerationResponse
56
+ */
57
+ private processResponse;
58
+ /**
59
+ * Handles errors from OpenAI API and converts to standard format
60
+ */
61
+ private handleError;
62
+ }