@loonylabs/tti-middleware 1.10.0 → 1.12.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
@@ -24,6 +24,7 @@
24
24
  - [Configuration](#%EF%B8%8F-configuration)
25
25
  - [Providers & Models](#-providers--models)
26
26
  - [Character Consistency](#-character-consistency)
27
+ - [Inpainting / Image Editing](#inpainting--image-editing)
27
28
  - [GDPR / Compliance](#-gdpr--compliance)
28
29
  - [API Reference](#-api-reference)
29
30
  - [Advanced Features](#-advanced-features)
@@ -44,7 +45,7 @@
44
45
  - **Eden AI**: Aggregator with access to OpenAI, Stability AI, Replicate (experimental)
45
46
  - **IONOS**: German cloud provider with OpenAI-compatible API (experimental)
46
47
  - **Character Consistency**: Generate consistent characters across multiple images (perfect for children's book illustrations)
47
- - **Inpainting**: Fix specific areas of a generated image without regenerating the entire scene — via Vertex AI `imagen-capability` model
48
+ - **Inpainting**: Fix specific areas of a generated image without regenerating the entire scene — via Vertex AI `imagen-capability` model.
48
49
  - **GDPR/DSGVO Compliance**: Built-in EU region support with automatic fallback
49
50
  - **Region Rotation**: Opt-in region rotation on quota errors (429) for Google Cloud — rotate through regions instead of retrying the same exhausted region
50
51
  - **Retry Logic**: Exponential backoff with jitter for transient errors (429, 408, 5xx, timeouts)
@@ -308,6 +309,37 @@ const duelScene = await service.generate({
308
309
 
309
310
  - Model must be `gemini-flash-image` (only model supporting character consistency)
310
311
 
312
+ ## Inpainting / Image Editing
313
+
314
+ The `imagen-capability` model supports mask-based inpainting via Vertex AI. This is the **only** model that supports pixel-precise editing with a mask image.
315
+
316
+ ### Basic Inpainting
317
+
318
+ ```typescript
319
+ const result = await service.generate({
320
+ model: 'imagen-capability',
321
+ prompt: 'Remove the extra arm and fill with matching forest background',
322
+ baseImage: { base64: originalImageBase64, mimeType: 'image/png' },
323
+ maskImage: { base64: maskBase64, mimeType: 'image/png' },
324
+ editMode: 'inpainting-remove', // default: 'inpainting-insert'
325
+ maskDilation: 0.02, // optional, 0.0–1.0, default 0.01
326
+ });
327
+ ```
328
+
329
+ **How the mask works:**
330
+ - White pixels = area the model will regenerate
331
+ - Black pixels = area preserved exactly as-is
332
+ - Mask must have identical dimensions to `baseImage`
333
+
334
+ ### Supported `editMode` Values
335
+
336
+ | Value | Description |
337
+ |-------|-------------|
338
+ | `'inpainting-insert'` | Add or replace content in masked area (default) |
339
+ | `'inpainting-remove'` | Remove content and fill with matching background |
340
+ | `'background-swap'` | Replace background, preserve foreground |
341
+ | `'outpainting'` | Extend image beyond its boundaries into the masked area |
342
+
311
343
  ## GDPR / Compliance
312
344
 
313
345
  ### Provider Compliance Overview
@@ -366,10 +398,16 @@ interface TTIRequest {
366
398
  n?: number; // Number of images (default: 1)
367
399
  aspectRatio?: string; // '1:1', '16:9', '4:3', etc.
368
400
 
369
- // Character consistency
401
+ // Character consistency (Gemini models only)
370
402
  referenceImages?: TTIReferenceImage[];
371
403
  subjectDescription?: string;
372
404
 
405
+ // Inpainting / image editing (imagen-capability only)
406
+ baseImage?: TTIReferenceImage; // Activates edit mode when set
407
+ maskImage?: TTIReferenceImage; // Required when baseImage is set
408
+ maskDilation?: number; // 0.0–1.0, default 0.01
409
+ editMode?: 'inpainting-insert' | 'inpainting-remove' | 'background-swap' | 'outpainting';
410
+
373
411
  // Retry configuration
374
412
  retry?: boolean | RetryOptions; // true (default), false, or custom config
375
413
 
@@ -65,6 +65,7 @@ export declare class GoogleCloudTTIProvider extends BaseTTIProvider {
65
65
  private processImagenResponse;
66
66
  /** Maps our editMode values to the Vertex AI API constants */
67
67
  private static readonly EDIT_MODE_MAP;
68
+ private static readonly SUBJECT_TYPE_MAP;
68
69
  private editWithImagen;
69
70
  private generateWithGemini;
70
71
  private getGenaiClient;
@@ -565,18 +565,31 @@ class GoogleCloudTTIProvider extends base_tti_provider_1.BaseTTIProvider {
565
565
  try {
566
566
  const { client, helpers } = await this.getAiplatformClient(region);
567
567
  const endpoint = `projects/${this.config.projectId}/locations/${region}/publishers/google/models/${internalModelId}`;
568
- // Build referenceImages array: [RAW base image, MASK image]
568
+ // Build referenceImages array.
569
+ //
570
+ // Vertex AI uses sequential, distinct referenceId values for each entry.
571
+ // RAW and MASK have separate IDs — the API pairs them by type/order, not by shared ID.
572
+ // SUBJECT gets the next ID after MASK and MUST be cited in the prompt as [N].
573
+ //
574
+ // Correct layout (confirmed by official Vertex AI docs):
575
+ // REFERENCE_TYPE_RAW referenceId: 1
576
+ // REFERENCE_TYPE_MASK referenceId: 2
577
+ // REFERENCE_TYPE_SUBJECT referenceId: 3 ← referenced in prompt as [3]
578
+ //
579
+ // See: https://cloud.google.com/vertex-ai/generative-ai/docs/image/edit-insert-objects
580
+ const RAW_REFERENCE_ID = 1;
581
+ const MASK_REFERENCE_ID = 2;
569
582
  const referenceImages = [
570
583
  {
571
584
  referenceType: 'REFERENCE_TYPE_RAW',
572
- referenceId: 1,
585
+ referenceId: RAW_REFERENCE_ID,
573
586
  referenceImage: {
574
587
  bytesBase64Encoded: request.baseImage.base64,
575
588
  },
576
589
  },
577
590
  {
578
591
  referenceType: 'REFERENCE_TYPE_MASK',
579
- referenceId: 2,
592
+ referenceId: MASK_REFERENCE_ID,
580
593
  referenceImage: {
581
594
  bytesBase64Encoded: request.maskImage.base64,
582
595
  },
@@ -813,3 +826,10 @@ GoogleCloudTTIProvider.EDIT_MODE_MAP = {
813
826
  'background-swap': 'EDIT_MODE_BGSWAP',
814
827
  'outpainting': 'EDIT_MODE_OUTPAINT',
815
828
  };
829
+ GoogleCloudTTIProvider.SUBJECT_TYPE_MAP = {
830
+ person: 'SUBJECT_TYPE_PERSON',
831
+ // Vertex AI API uses ANIMAL_COMPANION, not ANIMAL
832
+ animal: 'SUBJECT_TYPE_ANIMAL_COMPANION',
833
+ product: 'SUBJECT_TYPE_PRODUCT',
834
+ default: 'SUBJECT_TYPE_DEFAULT',
835
+ };
package/package.json CHANGED
@@ -1,90 +1,90 @@
1
- {
2
- "name": "@loonylabs/tti-middleware",
3
- "version": "1.10.0",
4
- "description": "Provider-agnostic Text-to-Image middleware with GDPR compliance. Supports Google Cloud (Imagen, Gemini), Eden AI, and IONOS.",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
7
- "files": [
8
- "dist",
9
- "LICENSE",
10
- "README.md",
11
- ".env.example"
12
- ],
13
- "scripts": {
14
- "build": "tsc",
15
- "test": "npm run test:unit",
16
- "test:unit": "jest --testPathPattern=tests/unit",
17
- "test:unit:watch": "jest --testPathPattern=tests/unit --watch",
18
- "test:unit:coverage": "jest --testPathPattern=tests/unit --coverage",
19
- "test:integration": "cross-env TTI_INTEGRATION_TESTS=true jest --testPathPattern=tests/integration",
20
- "test:ci": "jest --runInBand --ci --coverage --testPathPattern=tests/unit",
21
- "test:live": "TTI_INTEGRATION_TESTS=true jest tests/integration/*.integration.test.ts --testTimeout=120000",
22
- "test:manual": "ts-node scripts/manual-test-edenai.ts",
23
- "test:manual:google-cloud": "ts-node scripts/manual-test-google-cloud.ts",
24
- "lint": "eslint src/**/*.ts",
25
- "format": "prettier --write \"src/**/*.ts\"",
26
- "clean": "node -e \"const fs=require('fs');if(fs.existsSync('dist'))fs.rmSync('dist',{recursive:true})\"",
27
- "prepare": "npm run build",
28
- "prepublishOnly": "npm run clean && npm run build && npm run test"
29
- },
30
- "keywords": [
31
- "tti",
32
- "text-to-image",
33
- "image-generation",
34
- "ai",
35
- "vertex-ai",
36
- "google-cloud",
37
- "gemini",
38
- "imagen",
39
- "middleware",
40
- "provider-agnostic",
41
- "typescript",
42
- "gdpr",
43
- "dsgvo",
44
- "character-consistency"
45
- ],
46
- "author": "loonylabs-dev",
47
- "license": "MIT",
48
- "repository": {
49
- "type": "git",
50
- "url": "https://github.com/loonylabs-dev/tti-middleware.git"
51
- },
52
- "bugs": {
53
- "url": "https://github.com/loonylabs-dev/tti-middleware/issues"
54
- },
55
- "homepage": "https://github.com/loonylabs-dev/tti-middleware#readme",
56
- "peerDependencies": {
57
- "@google-cloud/aiplatform": ">=3.0.0",
58
- "@google/genai": ">=1.40.0"
59
- },
60
- "peerDependenciesMeta": {
61
- "@google-cloud/aiplatform": {
62
- "optional": true
63
- },
64
- "@google/genai": {
65
- "optional": true
66
- }
67
- },
68
- "devDependencies": {
69
- "@google-cloud/aiplatform": "^3.29.0",
70
- "@google/genai": "^1.40.0",
71
- "@types/jest": "^29.5.8",
72
- "@types/node": "^20.10.0",
73
- "@typescript-eslint/eslint-plugin": "^6.13.0",
74
- "@typescript-eslint/parser": "^6.13.0",
75
- "cross-env": "^10.1.0",
76
- "dotenv": "^17.2.3",
77
- "eslint": "^8.54.0",
78
- "jest": "^29.7.0",
79
- "prettier": "^3.1.0",
80
- "ts-jest": "^29.1.1",
81
- "ts-node": "^10.9.2",
82
- "typescript": "^5.3.2"
83
- },
84
- "engines": {
85
- "node": ">=18.0.0"
86
- },
87
- "publishConfig": {
88
- "access": "public"
89
- }
90
- }
1
+ {
2
+ "name": "@loonylabs/tti-middleware",
3
+ "version": "1.12.0",
4
+ "description": "Provider-agnostic Text-to-Image middleware with GDPR compliance. Supports Google Cloud (Imagen, Gemini), Eden AI, and IONOS.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist",
9
+ "LICENSE",
10
+ "README.md",
11
+ ".env.example"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "test": "npm run test:unit",
16
+ "test:unit": "jest --testPathPattern=tests/unit",
17
+ "test:unit:watch": "jest --testPathPattern=tests/unit --watch",
18
+ "test:unit:coverage": "jest --testPathPattern=tests/unit --coverage",
19
+ "test:integration": "cross-env TTI_INTEGRATION_TESTS=true jest --testPathPattern=tests/integration",
20
+ "test:ci": "jest --runInBand --ci --coverage --testPathPattern=tests/unit",
21
+ "test:live": "TTI_INTEGRATION_TESTS=true jest tests/integration/*.integration.test.ts --testTimeout=120000",
22
+ "test:manual": "ts-node scripts/manual-test-edenai.ts",
23
+ "test:manual:google-cloud": "ts-node scripts/manual-test-google-cloud.ts",
24
+ "lint": "eslint src/**/*.ts",
25
+ "format": "prettier --write \"src/**/*.ts\"",
26
+ "clean": "node -e \"const fs=require('fs');if(fs.existsSync('dist'))fs.rmSync('dist',{recursive:true})\"",
27
+ "prepare": "npm run build",
28
+ "prepublishOnly": "npm run clean && npm run build && npm run test"
29
+ },
30
+ "keywords": [
31
+ "tti",
32
+ "text-to-image",
33
+ "image-generation",
34
+ "ai",
35
+ "vertex-ai",
36
+ "google-cloud",
37
+ "gemini",
38
+ "imagen",
39
+ "middleware",
40
+ "provider-agnostic",
41
+ "typescript",
42
+ "gdpr",
43
+ "dsgvo",
44
+ "character-consistency"
45
+ ],
46
+ "author": "loonylabs-dev",
47
+ "license": "MIT",
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "https://github.com/loonylabs-dev/tti-middleware.git"
51
+ },
52
+ "bugs": {
53
+ "url": "https://github.com/loonylabs-dev/tti-middleware/issues"
54
+ },
55
+ "homepage": "https://github.com/loonylabs-dev/tti-middleware#readme",
56
+ "peerDependencies": {
57
+ "@google-cloud/aiplatform": ">=3.0.0",
58
+ "@google/genai": ">=1.40.0"
59
+ },
60
+ "peerDependenciesMeta": {
61
+ "@google-cloud/aiplatform": {
62
+ "optional": true
63
+ },
64
+ "@google/genai": {
65
+ "optional": true
66
+ }
67
+ },
68
+ "devDependencies": {
69
+ "@google-cloud/aiplatform": "^3.29.0",
70
+ "@google/genai": "^1.40.0",
71
+ "@types/jest": "^29.5.8",
72
+ "@types/node": "^20.10.0",
73
+ "@typescript-eslint/eslint-plugin": "^6.13.0",
74
+ "@typescript-eslint/parser": "^6.13.0",
75
+ "cross-env": "^10.1.0",
76
+ "dotenv": "^17.2.3",
77
+ "eslint": "^8.54.0",
78
+ "jest": "^29.7.0",
79
+ "prettier": "^3.1.0",
80
+ "ts-jest": "^29.1.1",
81
+ "ts-node": "^10.9.2",
82
+ "typescript": "^5.3.2"
83
+ },
84
+ "engines": {
85
+ "node": ">=18.0.0"
86
+ },
87
+ "publishConfig": {
88
+ "access": "public"
89
+ }
90
+ }