@loonylabs/tti-middleware 1.1.0 → 1.2.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.
package/README.md CHANGED
@@ -1,602 +1,609 @@
1
- <div align="center">
2
-
3
- # TTI Middleware
4
-
5
- *Provider-agnostic Text-to-Image middleware with **GDPR compliance** and **character consistency** support. Currently supports Google Cloud (Imagen 3, Gemini Flash Image), Eden AI, and IONOS. Features EU data residency via Vertex AI, automatic region fallback, retry logic, and comprehensive error handling.*
6
-
7
- <!-- Horizontal Badge Navigation Bar -->
8
- [![npm version](https://img.shields.io/npm/v/@loonylabs/tti-middleware.svg?style=for-the-badge&logo=npm&logoColor=white)](https://www.npmjs.com/package/@loonylabs/tti-middleware)
9
- [![npm downloads](https://img.shields.io/npm/dm/@loonylabs/tti-middleware.svg?style=for-the-badge&logo=npm&logoColor=white)](https://www.npmjs.com/package/@loonylabs/tti-middleware)
10
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.3+-blue.svg?style=for-the-badge&logo=typescript&logoColor=white)](#-features)
11
- [![Node.js](https://img.shields.io/badge/Node.js-18+-339933?style=for-the-badge&logo=nodedotjs&logoColor=white)](#-prerequisites)
12
- [![MIT License](https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge&logo=opensource&logoColor=white)](#-license)
13
- [![GitHub](https://img.shields.io/badge/GitHub-Repository-181717?style=for-the-badge&logo=github&logoColor=white)](https://github.com/loonylabs-dev/tti-middleware)
14
-
15
- </div>
16
-
17
- <!-- Table of Contents -->
18
- <details>
19
- <summary><strong>Table of Contents</strong></summary>
20
-
21
- - [Features](#-features)
22
- - [Quick Start](#-quick-start)
23
- - [Prerequisites](#-prerequisites)
24
- - [Configuration](#%EF%B8%8F-configuration)
25
- - [Providers & Models](#-providers--models)
26
- - [Character Consistency](#-character-consistency)
27
- - [GDPR / Compliance](#-gdpr--compliance)
28
- - [API Reference](#-api-reference)
29
- - [Advanced Features](#-advanced-features)
30
- - [Testing](#-testing)
31
- - [Documentation](#-documentation)
32
- - [Contributing](#-contributing)
33
- - [License](#-license)
34
- - [Links](#-links)
35
-
36
- </details>
37
-
38
- ---
39
-
40
- ## Features
41
-
42
- - **Multi-Provider Architecture**: Unified API for all TTI providers
43
- - **Google Cloud** (Recommended): Imagen 3 & Gemini Flash Image with EU data residency
44
- - **Eden AI**: Aggregator with access to OpenAI, Stability AI, Replicate (experimental)
45
- - **IONOS**: German cloud provider with OpenAI-compatible API (experimental)
46
- - **Character Consistency**: Generate consistent characters across multiple images (perfect for children's book illustrations)
47
- - **GDPR/DSGVO Compliance**: Built-in EU region support with automatic fallback
48
- - **Retry Logic**: Automatic retry for rate limits (429) with configurable backoff
49
- - **TypeScript First**: Full type safety with comprehensive interfaces
50
- - **Logging Control**: Configurable log levels via environment or API
51
- - **Debug Logging**: Markdown file logging for debugging prompts and responses
52
- - **Error Handling**: Typed error classes for precise error handling
53
-
54
- ## Quick Start
55
-
56
- ### Installation
57
-
58
- Install from npm:
59
-
60
- ```bash
61
- npm install @loonylabs/tti-middleware
62
-
63
- # For Google Cloud provider (recommended):
64
- npm install @google-cloud/aiplatform @google/genai
65
- ```
66
-
67
- Or install directly from GitHub:
68
-
69
- ```bash
70
- npm install github:loonylabs-dev/tti-middleware
71
- ```
72
-
73
- ### Basic Usage
74
-
75
- ```typescript
76
- import { TTIService, GoogleCloudTTIProvider, TTIProvider } from '@loonylabs/tti-middleware';
77
-
78
- // Create service and register provider
79
- const service = new TTIService();
80
- service.registerProvider(new GoogleCloudTTIProvider({
81
- projectId: process.env.GOOGLE_CLOUD_PROJECT,
82
- region: 'europe-west4', // EU region for GDPR
83
- }));
84
-
85
- // Generate an image
86
- const result = await service.generate({
87
- prompt: 'A futuristic city with flying cars, cyberpunk style',
88
- model: 'imagen-3',
89
- });
90
-
91
- console.log('Image generated:', result.images[0].base64?.substring(0, 50) + '...');
92
- console.log('Duration:', result.metadata.duration, 'ms');
93
- ```
94
-
95
- <details>
96
- <summary><strong>Using Character Consistency</strong></summary>
97
-
98
- Generate consistent characters across multiple images:
99
-
100
- ```typescript
101
- // 1. Create the initial character
102
- const character = await service.generate({
103
- prompt: 'A cute cartoon bear with a red hat and blue scarf, watercolor style',
104
- model: 'gemini-flash-image', // Only this model supports character consistency!
105
- });
106
-
107
- // 2. Generate new scenes with the same character (Structured Mode)
108
- const scene = await service.generate({
109
- prompt: 'dancing happily in the rain, jumping in puddles',
110
- model: 'gemini-flash-image',
111
- referenceImages: [{
112
- base64: character.images[0].base64!,
113
- mimeType: 'image/png',
114
- }],
115
- subjectDescription: 'cute cartoon bear with red hat and blue scarf',
116
- });
117
-
118
- // 3. Or use Index-Based Mode for multiple characters
119
- const multiCharScene = await service.generate({
120
- prompt: 'The FIRST reference image character meets the SECOND reference image character',
121
- model: 'gemini-flash-image',
122
- referenceImages: [
123
- { base64: character1.images[0].base64!, mimeType: 'image/png' },
124
- { base64: character2.images[0].base64!, mimeType: 'image/png' },
125
- ],
126
- // subjectDescription omitted = Index-Based Mode
127
- });
128
- ```
129
-
130
- **Important:** Character consistency is only supported by `gemini-flash-image` model!
131
-
132
- </details>
133
-
134
- <details>
135
- <summary><strong>Switching Providers</strong></summary>
136
-
137
- ```typescript
138
- // Use Google Cloud (recommended for EU)
139
- const googleResult = await service.generate({
140
- prompt: 'A mountain landscape',
141
- model: 'imagen-3',
142
- }, TTIProvider.GOOGLE_CLOUD);
143
-
144
- // Use Eden AI (experimental)
145
- const edenResult = await service.generate({
146
- prompt: 'A mountain landscape',
147
- model: 'openai', // Uses DALL-E via Eden AI
148
- }, TTIProvider.EDENAI);
149
-
150
- // Use IONOS (experimental)
151
- const ionosResult = await service.generate({
152
- prompt: 'A mountain landscape',
153
- }, TTIProvider.IONOS);
154
- ```
155
-
156
- </details>
157
-
158
- ## Prerequisites
159
-
160
- <details>
161
- <summary><strong>Required Dependencies</strong></summary>
162
-
163
- - **Node.js** 18+
164
- - **TypeScript** 5.3+
165
- - **Google Cloud SDK** (optional, for Google Cloud provider)
166
-
167
- For Google Cloud provider:
168
- ```bash
169
- npm install @google-cloud/aiplatform @google/genai
170
- ```
171
-
172
- </details>
173
-
174
- ## Configuration
175
-
176
- <details>
177
- <summary><strong>Environment Setup</strong></summary>
178
-
179
- Create a `.env` file in your project root:
180
-
181
- ```env
182
- # Default provider
183
- TTI_DEFAULT_PROVIDER=google-cloud
184
-
185
- # Logging level (debug, info, warn, error, silent)
186
- TTI_LOG_LEVEL=info
187
-
188
- # Google Cloud (recommended for EU/GDPR)
189
- GOOGLE_CLOUD_PROJECT=your-project-id
190
- GOOGLE_APPLICATION_CREDENTIALS=./service-account.json
191
- GOOGLE_CLOUD_REGION=europe-west4 # Recommended for Gemini
192
-
193
- # Eden AI (experimental)
194
- EDENAI_API_KEY=your-api-key
195
-
196
- # IONOS (experimental)
197
- IONOS_API_KEY=your-api-key
198
- IONOS_API_URL=https://api.ionos.cloud/ai/v1
199
- ```
200
-
201
- </details>
202
-
203
- ## Providers & Models
204
-
205
- ### Google Cloud (Recommended)
206
-
207
- | Model | ID | Character Consistency | EU Regions |
208
- |-------|-----|----------------------|------------|
209
- | **Imagen 3** | `imagen-3` | No | All EU regions |
210
- | **Gemini Flash Image** | `gemini-flash-image` | **Yes** | europe-west1, europe-west4, europe-north1 |
211
-
212
- **Important:** `gemini-flash-image` is **NOT available** in `europe-west3` (Frankfurt)!
213
-
214
- ### Eden AI (Experimental)
215
-
216
- | Model | ID | Notes |
217
- |-------|-----|-------|
218
- | OpenAI DALL-E | `openai` | Via Eden AI aggregator |
219
- | Stability AI | `stabilityai` | Via Eden AI aggregator |
220
- | Replicate | `replicate` | Via Eden AI aggregator |
221
-
222
- ### IONOS (Experimental)
223
-
224
- | Model | ID | Notes |
225
- |-------|-----|-------|
226
- | Default | `default` | OpenAI-compatible API |
227
-
228
- ### Google Cloud Region Availability
229
-
230
- | Region | Location | Imagen 3 | Gemini Flash Image |
231
- |--------|----------|----------|-------------------|
232
- | `europe-west1` | Belgium | Yes | Yes |
233
- | `europe-west3` | Frankfurt | Yes | **No** |
234
- | `europe-west4` | Netherlands | Yes | **Yes (Recommended)** |
235
- | `europe-north1` | Finland | Yes | Yes |
236
- | `europe-west9` | Paris | Yes | No |
237
-
238
- ## Character Consistency
239
-
240
- Generate consistent characters across multiple images - perfect for children's book illustrations.
241
-
242
- ### Mode 1: Structured Mode (Single Character)
243
-
244
- Best for scenes with a single consistent character:
245
-
246
- ```typescript
247
- // Step 1: Create a character
248
- const bear = await service.generate({
249
- prompt: 'A cute cartoon bear with a red hat, watercolor style',
250
- model: 'gemini-flash-image',
251
- });
252
-
253
- // Step 2: Use in different scenes
254
- const scenes = ['playing in the park', 'reading a book', 'eating honey'];
255
-
256
- for (const scene of scenes) {
257
- const result = await service.generate({
258
- prompt: scene,
259
- model: 'gemini-flash-image',
260
- referenceImages: [{ base64: bear.images[0].base64!, mimeType: 'image/png' }],
261
- subjectDescription: 'cute cartoon bear with red hat', // Required in structured mode
262
- });
263
- // Save result...
264
- }
265
- ```
266
-
267
- ### Mode 2: Index-Based Mode (Multiple Characters)
268
-
269
- Best for scenes with multiple distinct characters. Reference images directly in your prompt by their position:
270
-
271
- ```typescript
272
- // Load two different character references
273
- const cowboy1 = await loadImage('cowboy1.png');
274
- const cowboy2 = await loadImage('cowboy2.png');
275
-
276
- // Reference each image by index in the prompt
277
- const duelScene = await service.generate({
278
- prompt: `Generate a cinematic wide shot of a western duel.
279
- - The character on the LEFT should look exactly like the person in the FIRST reference image.
280
- - The character on the RIGHT should look exactly like the person in the SECOND reference image.
281
- They are standing in a dusty street at high noon.`,
282
- model: 'gemini-flash-image',
283
- referenceImages: [
284
- { base64: cowboy1, mimeType: 'image/png' },
285
- { base64: cowboy2, mimeType: 'image/png' },
286
- ],
287
- // subjectDescription intentionally omitted for index-based mode
288
- aspectRatio: '16:9',
289
- });
290
- ```
291
-
292
- **Reference keywords:** Use "FIRST reference image", "SECOND reference image" or "Image 1", "Image 2" etc.
293
-
294
- ### Requirements
295
-
296
- | Mode | `subjectDescription` | Use Case |
297
- |------|---------------------|----------|
298
- | **Structured** | Required | Single character across scenes |
299
- | **Index-Based** | Omitted | Multiple characters in one scene |
300
-
301
- - Model must be `gemini-flash-image` (only model supporting character consistency)
302
-
303
- ## GDPR / Compliance
304
-
305
- ### Provider Compliance Overview
306
-
307
- | Provider | DPA | GDPR | EU Data Residency | Document |
308
- |----------|-----|------|-------------------|----------|
309
- | **Google Cloud** | Yes | Yes | Yes | [CDPA](https://cloud.google.com/terms/data-processing-addendum) |
310
- | **Eden AI** | Yes | Depends* | Depends* | [Privacy Policy](https://www.edenai.co/privacy-policy) |
311
- | **IONOS** | Yes | Yes | Yes | [AGB](https://cloud.ionos.de/agb) |
312
-
313
- *Eden AI is an aggregator - compliance depends on the underlying provider.
314
-
315
- ### Google Cloud Data Usage
316
-
317
- - Customer data is **NOT used for training** AI models
318
- - Data stays in configured region (e.g., `europe-west4`)
319
- - Zero data retention option available
320
- - [Vertex AI Privacy Whitepaper](https://services.google.com/fh/files/misc/genai_privacy_google_cloud_202308.pdf)
321
-
322
- <details>
323
- <summary><strong>Checking EU Region Status</strong></summary>
324
-
325
- ```typescript
326
- import { GoogleCloudTTIProvider } from '@loonylabs/tti-middleware';
327
-
328
- const provider = new GoogleCloudTTIProvider({
329
- projectId: 'my-project',
330
- region: 'europe-west4',
331
- });
332
-
333
- console.log('Is EU region:', provider.isEURegion()); // true
334
- console.log('Current region:', provider.getRegion()); // 'europe-west4'
335
- ```
336
-
337
- </details>
338
-
339
- ## API Reference
340
-
341
- ### TTIService
342
-
343
- ```typescript
344
- class TTIService {
345
- registerProvider(provider: BaseTTIProvider): void;
346
- generate(request: TTIRequest, provider?: TTIProvider): Promise<TTIResponse>;
347
- getProvider(name: TTIProvider): BaseTTIProvider | undefined;
348
- listAllModels(): Array<{ provider: TTIProvider; models: ModelInfo[] }>;
349
- }
350
- ```
351
-
352
- ### TTIRequest
353
-
354
- ```typescript
355
- interface TTIRequest {
356
- prompt: string;
357
- model?: string; // 'imagen-3', 'gemini-flash-image', etc.
358
- n?: number; // Number of images (default: 1)
359
- aspectRatio?: string; // '1:1', '16:9', '4:3', etc.
360
-
361
- // Character consistency
362
- referenceImages?: TTIReferenceImage[];
363
- subjectDescription?: string;
364
-
365
- // Retry configuration
366
- retry?: boolean | RetryOptions; // true (default), false, or custom config
367
-
368
- providerOptions?: Record<string, unknown>;
369
- }
370
- ```
371
-
372
- ### TTIResponse
373
-
374
- ```typescript
375
- interface TTIResponse {
376
- images: TTIImage[];
377
- metadata: {
378
- provider: string;
379
- model: string;
380
- region?: string;
381
- duration: number;
382
- };
383
- usage: {
384
- imagesGenerated: number;
385
- modelId: string;
386
- };
387
- billing?: { // Only if provider returns costs
388
- cost: number;
389
- currency: string;
390
- source: 'provider' | 'estimated';
391
- };
392
- }
393
- ```
394
-
395
- ## Advanced Features
396
-
397
- <details>
398
- <summary><strong>Retry Configuration</strong></summary>
399
-
400
- Automatic retry for rate limit errors (429):
401
-
402
- ```typescript
403
- // Default: 2 retries, 1s delay, no backoff
404
- const result = await service.generate({
405
- prompt: 'A sunset over mountains',
406
- model: 'imagen-3',
407
- // retry: true (default)
408
- });
409
-
410
- // Custom retry configuration
411
- const result = await service.generate({
412
- prompt: 'A sunset over mountains',
413
- model: 'imagen-3',
414
- retry: {
415
- maxRetries: 3,
416
- delayMs: 2000,
417
- incrementalBackoff: true, // 2s, 4s, 6s...
418
- },
419
- });
420
-
421
- // Disable retry
422
- const result = await service.generate({
423
- prompt: 'A sunset over mountains',
424
- model: 'imagen-3',
425
- retry: false,
426
- });
427
- ```
428
-
429
- | Option | Default | Description |
430
- |--------|---------|-------------|
431
- | `maxRetries` | 2 | Maximum retry attempts |
432
- | `delayMs` | 1000 | Base delay between retries (ms) |
433
- | `incrementalBackoff` | false | If true: delay x attempt number |
434
-
435
- </details>
436
-
437
- <details>
438
- <summary><strong>Logging Configuration</strong></summary>
439
-
440
- Control logging via environment variable or API:
441
-
442
- ```typescript
443
- import { setLogLevel } from '@loonylabs/tti-middleware';
444
-
445
- // Set log level programmatically
446
- setLogLevel('warn'); // Only show warnings and errors
447
-
448
- // Or via environment variable
449
- // TTI_LOG_LEVEL=error
450
- ```
451
-
452
- Available levels: `debug`, `info`, `warn`, `error`, `silent`
453
-
454
- </details>
455
-
456
- <details>
457
- <summary><strong>Debug Logging (Markdown Files)</strong></summary>
458
-
459
- Log all TTI requests and responses to markdown files for debugging:
460
-
461
- ```typescript
462
- import { TTIDebugger } from '@loonylabs/tti-middleware';
463
-
464
- // Enable via environment variable
465
- // DEBUG_TTI_REQUESTS=true
466
-
467
- // Or programmatically
468
- TTIDebugger.setEnabled(true);
469
- TTIDebugger.setLogsDir('./logs/tti/requests');
470
-
471
- // Configure all options at once
472
- TTIDebugger.configure({
473
- enabled: true,
474
- logsDir: './logs/tti/requests',
475
- consoleLog: true, // Also log to console
476
- includeBase64: false, // Exclude base64 data (default)
477
- });
478
- ```
479
-
480
- **Log file contents:**
481
- - Provider, model, and region
482
- - Full prompt text
483
- - Subject description (for character consistency)
484
- - Reference image metadata
485
- - Response data (duration, image count)
486
- - Errors with full details
487
-
488
- **Use case:** Debug why character consistency isn't working by inspecting exactly what prompt and `subjectDescription` are being sent to the API.
489
-
490
- </details>
491
-
492
- <details>
493
- <summary><strong>Error Handling</strong></summary>
494
-
495
- Typed error classes for precise error handling:
496
-
497
- ```typescript
498
- import {
499
- TTIError,
500
- InvalidConfigError,
501
- QuotaExceededError,
502
- ProviderUnavailableError,
503
- GenerationFailedError,
504
- NetworkError,
505
- CapabilityNotSupportedError,
506
- } from '@loonylabs/tti-middleware';
507
-
508
- try {
509
- const result = await service.generate({ prompt: 'test' });
510
- } catch (error) {
511
- if (error instanceof QuotaExceededError) {
512
- console.log('Rate limit hit, try again later');
513
- } else if (error instanceof CapabilityNotSupportedError) {
514
- console.log('Model does not support this feature');
515
- } else if (error instanceof TTIError) {
516
- console.log(`TTI Error [${error.code}]: ${error.message}`);
517
- }
518
- }
519
- ```
520
-
521
- </details>
522
-
523
- ## Testing
524
-
525
- ```bash
526
- # Run all tests
527
- npm test
528
-
529
- # Unit tests only (123 tests, >95% coverage)
530
- npm run test:unit
531
-
532
- # Unit tests with watch mode
533
- npm run test:unit:watch
534
-
535
- # Unit tests with coverage report
536
- npm run test:unit:coverage
537
-
538
- # Integration tests (requires TTI_INTEGRATION_TESTS=true)
539
- npm run test:integration
540
-
541
- # CI/CD mode (unit tests only, in band)
542
- npm run test:ci
543
-
544
- # Manual test scripts
545
- npm run test:manual:google-cloud
546
- ```
547
-
548
- ### Integration Tests
549
-
550
- Integration tests make real API calls. They are **skipped by default**.
551
-
552
- ```bash
553
- # Enable and run integration tests
554
- TTI_INTEGRATION_TESTS=true npm run test:integration
555
- ```
556
-
557
- **Prerequisites:**
558
- - `GOOGLE_CLOUD_PROJECT` environment variable
559
- - `GOOGLE_APPLICATION_CREDENTIALS` pointing to service account JSON
560
-
561
- ## Documentation
562
-
563
- - [Getting Started](docs/getting-started.md) - Detailed setup guide
564
- - [Google Cloud Provider](docs/providers/google-cloud.md) - Imagen 3 & Gemini Flash Image
565
- - [GDPR/Compliance](docs/compliance.md) - Data processing agreements
566
- - [Testing Guide](docs/testing.md) - Unit & integration tests
567
- - [CHANGELOG](CHANGELOG.md) - Release notes
568
-
569
- ## Contributing
570
-
571
- We welcome contributions! Please ensure:
572
-
573
- 1. **Tests:** Add tests for new features
574
- 2. **Linting:** Run `npm run lint` before committing
575
- 3. **Conventions:** Follow the existing project structure
576
-
577
- 1. Fork the repository
578
- 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
579
- 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
580
- 4. Push to the branch (`git push origin feature/amazing-feature`)
581
- 5. Open a Pull Request
582
-
583
- ## License
584
-
585
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
586
-
587
- ## Links
588
-
589
- - [Documentation](https://github.com/loonylabs-dev/tti-middleware/docs)
590
- - [Issues](https://github.com/loonylabs-dev/tti-middleware/issues)
591
- - [NPM Package](https://www.npmjs.com/package/@loonylabs/tti-middleware)
592
-
593
- ---
594
-
595
- <div align="center">
596
-
597
- **Made with care by the LoonyLabs Team**
598
-
599
- [![GitHub stars](https://img.shields.io/github/stars/loonylabs-dev/tti-middleware?style=social)](https://github.com/loonylabs-dev/tti-middleware/stargazers)
600
- [![Follow on GitHub](https://img.shields.io/github/followers/loonylabs-dev?style=social&label=Follow)](https://github.com/loonylabs-dev)
601
-
602
- </div>
1
+ <div align="center">
2
+
3
+ # TTI Middleware
4
+
5
+ *Provider-agnostic Text-to-Image middleware with **GDPR compliance** and **character consistency** support. Currently supports Google Cloud (Imagen 3, Gemini Flash Image), Eden AI, and IONOS. Features EU data residency via Vertex AI, automatic region fallback, retry logic, and comprehensive error handling.*
6
+
7
+ <!-- Horizontal Badge Navigation Bar -->
8
+ [![npm version](https://img.shields.io/npm/v/@loonylabs/tti-middleware.svg?style=for-the-badge&logo=npm&logoColor=white)](https://www.npmjs.com/package/@loonylabs/tti-middleware)
9
+ [![npm downloads](https://img.shields.io/npm/dm/@loonylabs/tti-middleware.svg?style=for-the-badge&logo=npm&logoColor=white)](https://www.npmjs.com/package/@loonylabs/tti-middleware)
10
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.3+-blue.svg?style=for-the-badge&logo=typescript&logoColor=white)](#-features)
11
+ [![Node.js](https://img.shields.io/badge/Node.js-18+-339933?style=for-the-badge&logo=nodedotjs&logoColor=white)](#-prerequisites)
12
+ [![MIT License](https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge&logo=opensource&logoColor=white)](#-license)
13
+ [![GitHub](https://img.shields.io/badge/GitHub-Repository-181717?style=for-the-badge&logo=github&logoColor=white)](https://github.com/loonylabs-dev/tti-middleware)
14
+
15
+ </div>
16
+
17
+ <!-- Table of Contents -->
18
+ <details>
19
+ <summary><strong>Table of Contents</strong></summary>
20
+
21
+ - [Features](#-features)
22
+ - [Quick Start](#-quick-start)
23
+ - [Prerequisites](#-prerequisites)
24
+ - [Configuration](#%EF%B8%8F-configuration)
25
+ - [Providers & Models](#-providers--models)
26
+ - [Character Consistency](#-character-consistency)
27
+ - [GDPR / Compliance](#-gdpr--compliance)
28
+ - [API Reference](#-api-reference)
29
+ - [Advanced Features](#-advanced-features)
30
+ - [Testing](#-testing)
31
+ - [Documentation](#-documentation)
32
+ - [Contributing](#-contributing)
33
+ - [License](#-license)
34
+ - [Links](#-links)
35
+
36
+ </details>
37
+
38
+ ---
39
+
40
+ ## Features
41
+
42
+ - **Multi-Provider Architecture**: Unified API for all TTI providers
43
+ - **Google Cloud** (Recommended): Imagen 3 & Gemini Flash Image with EU data residency
44
+ - **Eden AI**: Aggregator with access to OpenAI, Stability AI, Replicate (experimental)
45
+ - **IONOS**: German cloud provider with OpenAI-compatible API (experimental)
46
+ - **Character Consistency**: Generate consistent characters across multiple images (perfect for children's book illustrations)
47
+ - **GDPR/DSGVO Compliance**: Built-in EU region support with automatic fallback
48
+ - **Retry Logic**: Exponential backoff with jitter for transient errors (429, 408, 5xx, timeouts)
49
+ - **TypeScript First**: Full type safety with comprehensive interfaces
50
+ - **Logging Control**: Configurable log levels via environment or API
51
+ - **Debug Logging**: Markdown file logging for debugging prompts and responses
52
+ - **Error Handling**: Typed error classes for precise error handling
53
+
54
+ ## Quick Start
55
+
56
+ ### Installation
57
+
58
+ Install from npm:
59
+
60
+ ```bash
61
+ npm install @loonylabs/tti-middleware
62
+
63
+ # For Google Cloud provider (recommended):
64
+ npm install @google-cloud/aiplatform @google/genai
65
+ ```
66
+
67
+ Or install directly from GitHub:
68
+
69
+ ```bash
70
+ npm install github:loonylabs-dev/tti-middleware
71
+ ```
72
+
73
+ ### Basic Usage
74
+
75
+ ```typescript
76
+ import { TTIService, GoogleCloudTTIProvider, TTIProvider } from '@loonylabs/tti-middleware';
77
+
78
+ // Create service and register provider
79
+ const service = new TTIService();
80
+ service.registerProvider(new GoogleCloudTTIProvider({
81
+ projectId: process.env.GOOGLE_CLOUD_PROJECT,
82
+ region: 'europe-west4', // EU region for GDPR
83
+ }));
84
+
85
+ // Generate an image
86
+ const result = await service.generate({
87
+ prompt: 'A futuristic city with flying cars, cyberpunk style',
88
+ model: 'imagen-3',
89
+ });
90
+
91
+ console.log('Image generated:', result.images[0].base64?.substring(0, 50) + '...');
92
+ console.log('Duration:', result.metadata.duration, 'ms');
93
+ ```
94
+
95
+ <details>
96
+ <summary><strong>Using Character Consistency</strong></summary>
97
+
98
+ Generate consistent characters across multiple images:
99
+
100
+ ```typescript
101
+ // 1. Create the initial character
102
+ const character = await service.generate({
103
+ prompt: 'A cute cartoon bear with a red hat and blue scarf, watercolor style',
104
+ model: 'gemini-flash-image', // Only this model supports character consistency!
105
+ });
106
+
107
+ // 2. Generate new scenes with the same character (Structured Mode)
108
+ const scene = await service.generate({
109
+ prompt: 'dancing happily in the rain, jumping in puddles',
110
+ model: 'gemini-flash-image',
111
+ referenceImages: [{
112
+ base64: character.images[0].base64!,
113
+ mimeType: 'image/png',
114
+ }],
115
+ subjectDescription: 'cute cartoon bear with red hat and blue scarf',
116
+ });
117
+
118
+ // 3. Or use Index-Based Mode for multiple characters
119
+ const multiCharScene = await service.generate({
120
+ prompt: 'The FIRST reference image character meets the SECOND reference image character',
121
+ model: 'gemini-flash-image',
122
+ referenceImages: [
123
+ { base64: character1.images[0].base64!, mimeType: 'image/png' },
124
+ { base64: character2.images[0].base64!, mimeType: 'image/png' },
125
+ ],
126
+ // subjectDescription omitted = Index-Based Mode
127
+ });
128
+ ```
129
+
130
+ **Important:** Character consistency is only supported by `gemini-flash-image` model!
131
+
132
+ </details>
133
+
134
+ <details>
135
+ <summary><strong>Switching Providers</strong></summary>
136
+
137
+ ```typescript
138
+ // Use Google Cloud (recommended for EU)
139
+ const googleResult = await service.generate({
140
+ prompt: 'A mountain landscape',
141
+ model: 'imagen-3',
142
+ }, TTIProvider.GOOGLE_CLOUD);
143
+
144
+ // Use Eden AI (experimental)
145
+ const edenResult = await service.generate({
146
+ prompt: 'A mountain landscape',
147
+ model: 'openai', // Uses DALL-E via Eden AI
148
+ }, TTIProvider.EDENAI);
149
+
150
+ // Use IONOS (experimental)
151
+ const ionosResult = await service.generate({
152
+ prompt: 'A mountain landscape',
153
+ }, TTIProvider.IONOS);
154
+ ```
155
+
156
+ </details>
157
+
158
+ ## Prerequisites
159
+
160
+ <details>
161
+ <summary><strong>Required Dependencies</strong></summary>
162
+
163
+ - **Node.js** 18+
164
+ - **TypeScript** 5.3+
165
+ - **Google Cloud SDK** (optional, for Google Cloud provider)
166
+
167
+ For Google Cloud provider:
168
+ ```bash
169
+ npm install @google-cloud/aiplatform @google/genai
170
+ ```
171
+
172
+ </details>
173
+
174
+ ## Configuration
175
+
176
+ <details>
177
+ <summary><strong>Environment Setup</strong></summary>
178
+
179
+ Create a `.env` file in your project root:
180
+
181
+ ```env
182
+ # Default provider
183
+ TTI_DEFAULT_PROVIDER=google-cloud
184
+
185
+ # Logging level (debug, info, warn, error, silent)
186
+ TTI_LOG_LEVEL=info
187
+
188
+ # Google Cloud (recommended for EU/GDPR)
189
+ GOOGLE_CLOUD_PROJECT=your-project-id
190
+ GOOGLE_APPLICATION_CREDENTIALS=./service-account.json
191
+ GOOGLE_CLOUD_REGION=europe-west4 # Recommended for Gemini
192
+
193
+ # Eden AI (experimental)
194
+ EDENAI_API_KEY=your-api-key
195
+
196
+ # IONOS (experimental)
197
+ IONOS_API_KEY=your-api-key
198
+ IONOS_API_URL=https://api.ionos.cloud/ai/v1
199
+ ```
200
+
201
+ </details>
202
+
203
+ ## Providers & Models
204
+
205
+ ### Google Cloud (Recommended)
206
+
207
+ | Model | ID | Character Consistency | EU Regions |
208
+ |-------|-----|----------------------|------------|
209
+ | **Imagen 3** | `imagen-3` | No | All EU regions |
210
+ | **Gemini Flash Image** | `gemini-flash-image` | **Yes** | europe-west1, europe-west4, europe-north1 |
211
+
212
+ **Important:** `gemini-flash-image` is **NOT available** in `europe-west3` (Frankfurt)!
213
+
214
+ ### Eden AI (Experimental)
215
+
216
+ | Model | ID | Notes |
217
+ |-------|-----|-------|
218
+ | OpenAI DALL-E | `openai` | Via Eden AI aggregator |
219
+ | Stability AI | `stabilityai` | Via Eden AI aggregator |
220
+ | Replicate | `replicate` | Via Eden AI aggregator |
221
+
222
+ ### IONOS (Experimental)
223
+
224
+ | Model | ID | Notes |
225
+ |-------|-----|-------|
226
+ | Default | `default` | OpenAI-compatible API |
227
+
228
+ ### Google Cloud Region Availability
229
+
230
+ | Region | Location | Imagen 3 | Gemini Flash Image |
231
+ |--------|----------|----------|-------------------|
232
+ | `europe-west1` | Belgium | Yes | Yes |
233
+ | `europe-west3` | Frankfurt | Yes | **No** |
234
+ | `europe-west4` | Netherlands | Yes | **Yes (Recommended)** |
235
+ | `europe-north1` | Finland | Yes | Yes |
236
+ | `europe-west9` | Paris | Yes | No |
237
+
238
+ ## Character Consistency
239
+
240
+ Generate consistent characters across multiple images - perfect for children's book illustrations.
241
+
242
+ ### Mode 1: Structured Mode (Single Character)
243
+
244
+ Best for scenes with a single consistent character:
245
+
246
+ ```typescript
247
+ // Step 1: Create a character
248
+ const bear = await service.generate({
249
+ prompt: 'A cute cartoon bear with a red hat, watercolor style',
250
+ model: 'gemini-flash-image',
251
+ });
252
+
253
+ // Step 2: Use in different scenes
254
+ const scenes = ['playing in the park', 'reading a book', 'eating honey'];
255
+
256
+ for (const scene of scenes) {
257
+ const result = await service.generate({
258
+ prompt: scene,
259
+ model: 'gemini-flash-image',
260
+ referenceImages: [{ base64: bear.images[0].base64!, mimeType: 'image/png' }],
261
+ subjectDescription: 'cute cartoon bear with red hat', // Required in structured mode
262
+ });
263
+ // Save result...
264
+ }
265
+ ```
266
+
267
+ ### Mode 2: Index-Based Mode (Multiple Characters)
268
+
269
+ Best for scenes with multiple distinct characters. Reference images directly in your prompt by their position:
270
+
271
+ ```typescript
272
+ // Load two different character references
273
+ const cowboy1 = await loadImage('cowboy1.png');
274
+ const cowboy2 = await loadImage('cowboy2.png');
275
+
276
+ // Reference each image by index in the prompt
277
+ const duelScene = await service.generate({
278
+ prompt: `Generate a cinematic wide shot of a western duel.
279
+ - The character on the LEFT should look exactly like the person in the FIRST reference image.
280
+ - The character on the RIGHT should look exactly like the person in the SECOND reference image.
281
+ They are standing in a dusty street at high noon.`,
282
+ model: 'gemini-flash-image',
283
+ referenceImages: [
284
+ { base64: cowboy1, mimeType: 'image/png' },
285
+ { base64: cowboy2, mimeType: 'image/png' },
286
+ ],
287
+ // subjectDescription intentionally omitted for index-based mode
288
+ aspectRatio: '16:9',
289
+ });
290
+ ```
291
+
292
+ **Reference keywords:** Use "FIRST reference image", "SECOND reference image" or "Image 1", "Image 2" etc.
293
+
294
+ ### Requirements
295
+
296
+ | Mode | `subjectDescription` | Use Case |
297
+ |------|---------------------|----------|
298
+ | **Structured** | Required | Single character across scenes |
299
+ | **Index-Based** | Omitted | Multiple characters in one scene |
300
+
301
+ - Model must be `gemini-flash-image` (only model supporting character consistency)
302
+
303
+ ## GDPR / Compliance
304
+
305
+ ### Provider Compliance Overview
306
+
307
+ | Provider | DPA | GDPR | EU Data Residency | Document |
308
+ |----------|-----|------|-------------------|----------|
309
+ | **Google Cloud** | Yes | Yes | Yes | [CDPA](https://cloud.google.com/terms/data-processing-addendum) |
310
+ | **Eden AI** | Yes | Depends* | Depends* | [Privacy Policy](https://www.edenai.co/privacy-policy) |
311
+ | **IONOS** | Yes | Yes | Yes | [AGB](https://cloud.ionos.de/agb) |
312
+
313
+ *Eden AI is an aggregator - compliance depends on the underlying provider.
314
+
315
+ ### Google Cloud Data Usage
316
+
317
+ - Customer data is **NOT used for training** AI models
318
+ - Data stays in configured region (e.g., `europe-west4`)
319
+ - Zero data retention option available
320
+ - [Vertex AI Privacy Whitepaper](https://services.google.com/fh/files/misc/genai_privacy_google_cloud_202308.pdf)
321
+
322
+ <details>
323
+ <summary><strong>Checking EU Region Status</strong></summary>
324
+
325
+ ```typescript
326
+ import { GoogleCloudTTIProvider } from '@loonylabs/tti-middleware';
327
+
328
+ const provider = new GoogleCloudTTIProvider({
329
+ projectId: 'my-project',
330
+ region: 'europe-west4',
331
+ });
332
+
333
+ console.log('Is EU region:', provider.isEURegion()); // true
334
+ console.log('Current region:', provider.getRegion()); // 'europe-west4'
335
+ ```
336
+
337
+ </details>
338
+
339
+ ## API Reference
340
+
341
+ ### TTIService
342
+
343
+ ```typescript
344
+ class TTIService {
345
+ registerProvider(provider: BaseTTIProvider): void;
346
+ generate(request: TTIRequest, provider?: TTIProvider): Promise<TTIResponse>;
347
+ getProvider(name: TTIProvider): BaseTTIProvider | undefined;
348
+ listAllModels(): Array<{ provider: TTIProvider; models: ModelInfo[] }>;
349
+ }
350
+ ```
351
+
352
+ ### TTIRequest
353
+
354
+ ```typescript
355
+ interface TTIRequest {
356
+ prompt: string;
357
+ model?: string; // 'imagen-3', 'gemini-flash-image', etc.
358
+ n?: number; // Number of images (default: 1)
359
+ aspectRatio?: string; // '1:1', '16:9', '4:3', etc.
360
+
361
+ // Character consistency
362
+ referenceImages?: TTIReferenceImage[];
363
+ subjectDescription?: string;
364
+
365
+ // Retry configuration
366
+ retry?: boolean | RetryOptions; // true (default), false, or custom config
367
+
368
+ providerOptions?: Record<string, unknown>;
369
+ }
370
+ ```
371
+
372
+ ### TTIResponse
373
+
374
+ ```typescript
375
+ interface TTIResponse {
376
+ images: TTIImage[];
377
+ metadata: {
378
+ provider: string;
379
+ model: string;
380
+ region?: string;
381
+ duration: number;
382
+ };
383
+ usage: {
384
+ imagesGenerated: number;
385
+ modelId: string;
386
+ };
387
+ billing?: { // Only if provider returns costs
388
+ cost: number;
389
+ currency: string;
390
+ source: 'provider' | 'estimated';
391
+ };
392
+ }
393
+ ```
394
+
395
+ ## Advanced Features
396
+
397
+ <details>
398
+ <summary><strong>Retry Configuration</strong></summary>
399
+
400
+ Automatic retry with **exponential backoff and jitter** for transient errors (429, 408, 5xx, network timeouts). Follows [Google Cloud best practices](https://cloud.google.com/storage/docs/retry-strategy).
401
+
402
+ ```typescript
403
+ // Default: 3 retries, exponential backoff (1s → 2s → 4s), jitter enabled
404
+ const result = await service.generate({
405
+ prompt: 'A sunset over mountains',
406
+ model: 'imagen-3',
407
+ // retry: true (default)
408
+ });
409
+
410
+ // Custom retry configuration
411
+ const result = await service.generate({
412
+ prompt: 'A sunset over mountains',
413
+ model: 'imagen-3',
414
+ retry: {
415
+ maxRetries: 5,
416
+ delayMs: 1000,
417
+ backoffMultiplier: 2.0, // 1s, 2s, 4s, 8s, 16s
418
+ maxDelayMs: 30000, // Cap at 30s
419
+ jitter: true, // Randomize to prevent thundering herd
420
+ },
421
+ });
422
+
423
+ // Disable retry
424
+ const result = await service.generate({
425
+ prompt: 'A sunset over mountains',
426
+ model: 'imagen-3',
427
+ retry: false,
428
+ });
429
+ ```
430
+
431
+ **Retryable errors:** 429, 408, 500, 502, 503, 504, timeouts, ECONNRESET, ECONNREFUSED, socket hang up
432
+ **Not retried:** 400, 401, 403, and other client errors
433
+
434
+ | Option | Default | Description |
435
+ |--------|---------|-------------|
436
+ | `maxRetries` | 3 | Maximum retry attempts |
437
+ | `delayMs` | 1000 | Base delay between retries (ms) |
438
+ | `backoffMultiplier` | 2.0 | Exponential multiplier per attempt |
439
+ | `maxDelayMs` | 30000 | Maximum delay cap (ms) |
440
+ | `jitter` | true | Randomize delay to prevent thundering herd |
441
+
442
+ </details>
443
+
444
+ <details>
445
+ <summary><strong>Logging Configuration</strong></summary>
446
+
447
+ Control logging via environment variable or API:
448
+
449
+ ```typescript
450
+ import { setLogLevel } from '@loonylabs/tti-middleware';
451
+
452
+ // Set log level programmatically
453
+ setLogLevel('warn'); // Only show warnings and errors
454
+
455
+ // Or via environment variable
456
+ // TTI_LOG_LEVEL=error
457
+ ```
458
+
459
+ Available levels: `debug`, `info`, `warn`, `error`, `silent`
460
+
461
+ </details>
462
+
463
+ <details>
464
+ <summary><strong>Debug Logging (Markdown Files)</strong></summary>
465
+
466
+ Log all TTI requests and responses to markdown files for debugging:
467
+
468
+ ```typescript
469
+ import { TTIDebugger } from '@loonylabs/tti-middleware';
470
+
471
+ // Enable via environment variable
472
+ // DEBUG_TTI_REQUESTS=true
473
+
474
+ // Or programmatically
475
+ TTIDebugger.setEnabled(true);
476
+ TTIDebugger.setLogsDir('./logs/tti/requests');
477
+
478
+ // Configure all options at once
479
+ TTIDebugger.configure({
480
+ enabled: true,
481
+ logsDir: './logs/tti/requests',
482
+ consoleLog: true, // Also log to console
483
+ includeBase64: false, // Exclude base64 data (default)
484
+ });
485
+ ```
486
+
487
+ **Log file contents:**
488
+ - Provider, model, and region
489
+ - Full prompt text
490
+ - Subject description (for character consistency)
491
+ - Reference image metadata
492
+ - Response data (duration, image count)
493
+ - Errors with full details
494
+
495
+ **Use case:** Debug why character consistency isn't working by inspecting exactly what prompt and `subjectDescription` are being sent to the API.
496
+
497
+ </details>
498
+
499
+ <details>
500
+ <summary><strong>Error Handling</strong></summary>
501
+
502
+ Typed error classes for precise error handling:
503
+
504
+ ```typescript
505
+ import {
506
+ TTIError,
507
+ InvalidConfigError,
508
+ QuotaExceededError,
509
+ ProviderUnavailableError,
510
+ GenerationFailedError,
511
+ NetworkError,
512
+ CapabilityNotSupportedError,
513
+ } from '@loonylabs/tti-middleware';
514
+
515
+ try {
516
+ const result = await service.generate({ prompt: 'test' });
517
+ } catch (error) {
518
+ if (error instanceof QuotaExceededError) {
519
+ console.log('Rate limit hit, try again later');
520
+ } else if (error instanceof CapabilityNotSupportedError) {
521
+ console.log('Model does not support this feature');
522
+ } else if (error instanceof TTIError) {
523
+ console.log(`TTI Error [${error.code}]: ${error.message}`);
524
+ }
525
+ }
526
+ ```
527
+
528
+ </details>
529
+
530
+ ## Testing
531
+
532
+ ```bash
533
+ # Run all tests
534
+ npm test
535
+
536
+ # Unit tests only (123 tests, >95% coverage)
537
+ npm run test:unit
538
+
539
+ # Unit tests with watch mode
540
+ npm run test:unit:watch
541
+
542
+ # Unit tests with coverage report
543
+ npm run test:unit:coverage
544
+
545
+ # Integration tests (requires TTI_INTEGRATION_TESTS=true)
546
+ npm run test:integration
547
+
548
+ # CI/CD mode (unit tests only, in band)
549
+ npm run test:ci
550
+
551
+ # Manual test scripts
552
+ npm run test:manual:google-cloud
553
+ ```
554
+
555
+ ### Integration Tests
556
+
557
+ Integration tests make real API calls. They are **skipped by default**.
558
+
559
+ ```bash
560
+ # Enable and run integration tests
561
+ TTI_INTEGRATION_TESTS=true npm run test:integration
562
+ ```
563
+
564
+ **Prerequisites:**
565
+ - `GOOGLE_CLOUD_PROJECT` environment variable
566
+ - `GOOGLE_APPLICATION_CREDENTIALS` pointing to service account JSON
567
+
568
+ ## Documentation
569
+
570
+ - [Getting Started](docs/getting-started.md) - Detailed setup guide
571
+ - [Google Cloud Provider](docs/providers/google-cloud.md) - Imagen 3 & Gemini Flash Image
572
+ - [GDPR/Compliance](docs/compliance.md) - Data processing agreements
573
+ - [Testing Guide](docs/testing.md) - Unit & integration tests
574
+ - [CHANGELOG](CHANGELOG.md) - Release notes
575
+
576
+ ## Contributing
577
+
578
+ We welcome contributions! Please ensure:
579
+
580
+ 1. **Tests:** Add tests for new features
581
+ 2. **Linting:** Run `npm run lint` before committing
582
+ 3. **Conventions:** Follow the existing project structure
583
+
584
+ 1. Fork the repository
585
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
586
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
587
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
588
+ 5. Open a Pull Request
589
+
590
+ ## License
591
+
592
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
593
+
594
+ ## Links
595
+
596
+ - [Documentation](https://github.com/loonylabs-dev/tti-middleware/docs)
597
+ - [Issues](https://github.com/loonylabs-dev/tti-middleware/issues)
598
+ - [NPM Package](https://www.npmjs.com/package/@loonylabs/tti-middleware)
599
+
600
+ ---
601
+
602
+ <div align="center">
603
+
604
+ **Made with care by the LoonyLabs Team**
605
+
606
+ [![GitHub stars](https://img.shields.io/github/stars/loonylabs-dev/tti-middleware?style=social)](https://github.com/loonylabs-dev/tti-middleware/stargazers)
607
+ [![Follow on GitHub](https://img.shields.io/github/followers/loonylabs-dev?style=social&label=Follow)](https://github.com/loonylabs-dev)
608
+
609
+ </div>