npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@loonylabs/tti-middleware

v1.2.0

Published

Provider-agnostic Text-to-Image middleware with GDPR compliance. Supports Google Cloud (Imagen, Gemini), Eden AI, and IONOS.

Readme

TTI Middleware

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.

npm version npm downloads TypeScript Node.js MIT License GitHub


Features

  • Multi-Provider Architecture: Unified API for all TTI providers
    • Google Cloud (Recommended): Imagen 3 & Gemini Flash Image with EU data residency
    • Eden AI: Aggregator with access to OpenAI, Stability AI, Replicate (experimental)
    • IONOS: German cloud provider with OpenAI-compatible API (experimental)
  • Character Consistency: Generate consistent characters across multiple images (perfect for children's book illustrations)
  • GDPR/DSGVO Compliance: Built-in EU region support with automatic fallback
  • Retry Logic: Exponential backoff with jitter for transient errors (429, 408, 5xx, timeouts)
  • TypeScript First: Full type safety with comprehensive interfaces
  • Logging Control: Configurable log levels via environment or API
  • Debug Logging: Markdown file logging for debugging prompts and responses
  • Error Handling: Typed error classes for precise error handling

Quick Start

Installation

Install from npm:

npm install @loonylabs/tti-middleware

# For Google Cloud provider (recommended):
npm install @google-cloud/aiplatform @google/genai

Or install directly from GitHub:

npm install github:loonylabs-dev/tti-middleware

Basic Usage

import { TTIService, GoogleCloudTTIProvider, TTIProvider } from '@loonylabs/tti-middleware';

// Create service and register provider
const service = new TTIService();
service.registerProvider(new GoogleCloudTTIProvider({
  projectId: process.env.GOOGLE_CLOUD_PROJECT,
  region: 'europe-west4', // EU region for GDPR
}));

// Generate an image
const result = await service.generate({
  prompt: 'A futuristic city with flying cars, cyberpunk style',
  model: 'imagen-3',
});

console.log('Image generated:', result.images[0].base64?.substring(0, 50) + '...');
console.log('Duration:', result.metadata.duration, 'ms');

Generate consistent characters across multiple images:

// 1. Create the initial character
const character = await service.generate({
  prompt: 'A cute cartoon bear with a red hat and blue scarf, watercolor style',
  model: 'gemini-flash-image', // Only this model supports character consistency!
});

// 2. Generate new scenes with the same character (Structured Mode)
const scene = await service.generate({
  prompt: 'dancing happily in the rain, jumping in puddles',
  model: 'gemini-flash-image',
  referenceImages: [{
    base64: character.images[0].base64!,
    mimeType: 'image/png',
  }],
  subjectDescription: 'cute cartoon bear with red hat and blue scarf',
});

// 3. Or use Index-Based Mode for multiple characters
const multiCharScene = await service.generate({
  prompt: 'The FIRST reference image character meets the SECOND reference image character',
  model: 'gemini-flash-image',
  referenceImages: [
    { base64: character1.images[0].base64!, mimeType: 'image/png' },
    { base64: character2.images[0].base64!, mimeType: 'image/png' },
  ],
  // subjectDescription omitted = Index-Based Mode
});

Important: Character consistency is only supported by gemini-flash-image model!

// Use Google Cloud (recommended for EU)
const googleResult = await service.generate({
  prompt: 'A mountain landscape',
  model: 'imagen-3',
}, TTIProvider.GOOGLE_CLOUD);

// Use Eden AI (experimental)
const edenResult = await service.generate({
  prompt: 'A mountain landscape',
  model: 'openai', // Uses DALL-E via Eden AI
}, TTIProvider.EDENAI);

// Use IONOS (experimental)
const ionosResult = await service.generate({
  prompt: 'A mountain landscape',
}, TTIProvider.IONOS);

Prerequisites

  • Node.js 18+
  • TypeScript 5.3+
  • Google Cloud SDK (optional, for Google Cloud provider)

For Google Cloud provider:

npm install @google-cloud/aiplatform @google/genai

Configuration

Create a .env file in your project root:

# Default provider
TTI_DEFAULT_PROVIDER=google-cloud

# Logging level (debug, info, warn, error, silent)
TTI_LOG_LEVEL=info

# Google Cloud (recommended for EU/GDPR)
GOOGLE_CLOUD_PROJECT=your-project-id
GOOGLE_APPLICATION_CREDENTIALS=./service-account.json
GOOGLE_CLOUD_REGION=europe-west4  # Recommended for Gemini

# Eden AI (experimental)
EDENAI_API_KEY=your-api-key

# IONOS (experimental)
IONOS_API_KEY=your-api-key
IONOS_API_URL=https://api.ionos.cloud/ai/v1

Providers & Models

Google Cloud (Recommended)

| Model | ID | Character Consistency | EU Regions | |-------|-----|----------------------|------------| | Imagen 3 | imagen-3 | No | All EU regions | | Gemini Flash Image | gemini-flash-image | Yes | europe-west1, europe-west4, europe-north1 |

Important: gemini-flash-image is NOT available in europe-west3 (Frankfurt)!

Eden AI (Experimental)

| Model | ID | Notes | |-------|-----|-------| | OpenAI DALL-E | openai | Via Eden AI aggregator | | Stability AI | stabilityai | Via Eden AI aggregator | | Replicate | replicate | Via Eden AI aggregator |

IONOS (Experimental)

| Model | ID | Notes | |-------|-----|-------| | Default | default | OpenAI-compatible API |

Google Cloud Region Availability

| Region | Location | Imagen 3 | Gemini Flash Image | |--------|----------|----------|-------------------| | europe-west1 | Belgium | Yes | Yes | | europe-west3 | Frankfurt | Yes | No | | europe-west4 | Netherlands | Yes | Yes (Recommended) | | europe-north1 | Finland | Yes | Yes | | europe-west9 | Paris | Yes | No |

Character Consistency

Generate consistent characters across multiple images - perfect for children's book illustrations.

Mode 1: Structured Mode (Single Character)

Best for scenes with a single consistent character:

// Step 1: Create a character
const bear = await service.generate({
  prompt: 'A cute cartoon bear with a red hat, watercolor style',
  model: 'gemini-flash-image',
});

// Step 2: Use in different scenes
const scenes = ['playing in the park', 'reading a book', 'eating honey'];

for (const scene of scenes) {
  const result = await service.generate({
    prompt: scene,
    model: 'gemini-flash-image',
    referenceImages: [{ base64: bear.images[0].base64!, mimeType: 'image/png' }],
    subjectDescription: 'cute cartoon bear with red hat',  // Required in structured mode
  });
  // Save result...
}

Mode 2: Index-Based Mode (Multiple Characters)

Best for scenes with multiple distinct characters. Reference images directly in your prompt by their position:

// Load two different character references
const cowboy1 = await loadImage('cowboy1.png');
const cowboy2 = await loadImage('cowboy2.png');

// Reference each image by index in the prompt
const duelScene = await service.generate({
  prompt: `Generate a cinematic wide shot of a western duel.
    - The character on the LEFT should look exactly like the person in the FIRST reference image.
    - The character on the RIGHT should look exactly like the person in the SECOND reference image.
    They are standing in a dusty street at high noon.`,
  model: 'gemini-flash-image',
  referenceImages: [
    { base64: cowboy1, mimeType: 'image/png' },
    { base64: cowboy2, mimeType: 'image/png' },
  ],
  // subjectDescription intentionally omitted for index-based mode
  aspectRatio: '16:9',
});

Reference keywords: Use "FIRST reference image", "SECOND reference image" or "Image 1", "Image 2" etc.

Requirements

| Mode | subjectDescription | Use Case | |------|---------------------|----------| | Structured | Required | Single character across scenes | | Index-Based | Omitted | Multiple characters in one scene |

  • Model must be gemini-flash-image (only model supporting character consistency)

GDPR / Compliance

Provider Compliance Overview

| Provider | DPA | GDPR | EU Data Residency | Document | |----------|-----|------|-------------------|----------| | Google Cloud | Yes | Yes | Yes | CDPA | | Eden AI | Yes | Depends* | Depends* | Privacy Policy | | IONOS | Yes | Yes | Yes | AGB |

*Eden AI is an aggregator - compliance depends on the underlying provider.

Google Cloud Data Usage

  • Customer data is NOT used for training AI models
  • Data stays in configured region (e.g., europe-west4)
  • Zero data retention option available
  • Vertex AI Privacy Whitepaper
import { GoogleCloudTTIProvider } from '@loonylabs/tti-middleware';

const provider = new GoogleCloudTTIProvider({
  projectId: 'my-project',
  region: 'europe-west4',
});

console.log('Is EU region:', provider.isEURegion()); // true
console.log('Current region:', provider.getRegion()); // 'europe-west4'

API Reference

TTIService

class TTIService {
  registerProvider(provider: BaseTTIProvider): void;
  generate(request: TTIRequest, provider?: TTIProvider): Promise<TTIResponse>;
  getProvider(name: TTIProvider): BaseTTIProvider | undefined;
  listAllModels(): Array<{ provider: TTIProvider; models: ModelInfo[] }>;
}

TTIRequest

interface TTIRequest {
  prompt: string;
  model?: string;           // 'imagen-3', 'gemini-flash-image', etc.
  n?: number;               // Number of images (default: 1)
  aspectRatio?: string;     // '1:1', '16:9', '4:3', etc.

  // Character consistency
  referenceImages?: TTIReferenceImage[];
  subjectDescription?: string;

  // Retry configuration
  retry?: boolean | RetryOptions;  // true (default), false, or custom config

  providerOptions?: Record<string, unknown>;
}

TTIResponse

interface TTIResponse {
  images: TTIImage[];
  metadata: {
    provider: string;
    model: string;
    region?: string;
    duration: number;
  };
  usage: {
    imagesGenerated: number;
    modelId: string;
  };
  billing?: {          // Only if provider returns costs
    cost: number;
    currency: string;
    source: 'provider' | 'estimated';
  };
}

Advanced Features

Automatic retry with exponential backoff and jitter for transient errors (429, 408, 5xx, network timeouts). Follows Google Cloud best practices.

// Default: 3 retries, exponential backoff (1s → 2s → 4s), jitter enabled
const result = await service.generate({
  prompt: 'A sunset over mountains',
  model: 'imagen-3',
  // retry: true (default)
});

// Custom retry configuration
const result = await service.generate({
  prompt: 'A sunset over mountains',
  model: 'imagen-3',
  retry: {
    maxRetries: 5,
    delayMs: 1000,
    backoffMultiplier: 2.0,  // 1s, 2s, 4s, 8s, 16s
    maxDelayMs: 30000,       // Cap at 30s
    jitter: true,            // Randomize to prevent thundering herd
  },
});

// Disable retry
const result = await service.generate({
  prompt: 'A sunset over mountains',
  model: 'imagen-3',
  retry: false,
});

Retryable errors: 429, 408, 500, 502, 503, 504, timeouts, ECONNRESET, ECONNREFUSED, socket hang up Not retried: 400, 401, 403, and other client errors

| Option | Default | Description | |--------|---------|-------------| | maxRetries | 3 | Maximum retry attempts | | delayMs | 1000 | Base delay between retries (ms) | | backoffMultiplier | 2.0 | Exponential multiplier per attempt | | maxDelayMs | 30000 | Maximum delay cap (ms) | | jitter | true | Randomize delay to prevent thundering herd |

Control logging via environment variable or API:

import { setLogLevel } from '@loonylabs/tti-middleware';

// Set log level programmatically
setLogLevel('warn');  // Only show warnings and errors

// Or via environment variable
// TTI_LOG_LEVEL=error

Available levels: debug, info, warn, error, silent

Log all TTI requests and responses to markdown files for debugging:

import { TTIDebugger } from '@loonylabs/tti-middleware';

// Enable via environment variable
// DEBUG_TTI_REQUESTS=true

// Or programmatically
TTIDebugger.setEnabled(true);
TTIDebugger.setLogsDir('./logs/tti/requests');

// Configure all options at once
TTIDebugger.configure({
  enabled: true,
  logsDir: './logs/tti/requests',
  consoleLog: true,      // Also log to console
  includeBase64: false,  // Exclude base64 data (default)
});

Log file contents:

  • Provider, model, and region
  • Full prompt text
  • Subject description (for character consistency)
  • Reference image metadata
  • Response data (duration, image count)
  • Errors with full details

Use case: Debug why character consistency isn't working by inspecting exactly what prompt and subjectDescription are being sent to the API.

Typed error classes for precise error handling:

import {
  TTIError,
  InvalidConfigError,
  QuotaExceededError,
  ProviderUnavailableError,
  GenerationFailedError,
  NetworkError,
  CapabilityNotSupportedError,
} from '@loonylabs/tti-middleware';

try {
  const result = await service.generate({ prompt: 'test' });
} catch (error) {
  if (error instanceof QuotaExceededError) {
    console.log('Rate limit hit, try again later');
  } else if (error instanceof CapabilityNotSupportedError) {
    console.log('Model does not support this feature');
  } else if (error instanceof TTIError) {
    console.log(`TTI Error [${error.code}]: ${error.message}`);
  }
}

Testing

# Run all tests
npm test

# Unit tests only (123 tests, >95% coverage)
npm run test:unit

# Unit tests with watch mode
npm run test:unit:watch

# Unit tests with coverage report
npm run test:unit:coverage

# Integration tests (requires TTI_INTEGRATION_TESTS=true)
npm run test:integration

# CI/CD mode (unit tests only, in band)
npm run test:ci

# Manual test scripts
npm run test:manual:google-cloud

Integration Tests

Integration tests make real API calls. They are skipped by default.

# Enable and run integration tests
TTI_INTEGRATION_TESTS=true npm run test:integration

Prerequisites:

  • GOOGLE_CLOUD_PROJECT environment variable
  • GOOGLE_APPLICATION_CREDENTIALS pointing to service account JSON

Documentation

Contributing

We welcome contributions! Please ensure:

  1. Tests: Add tests for new features

  2. Linting: Run npm run lint before committing

  3. Conventions: Follow the existing project structure

  4. Fork the repository

  5. Create your feature branch (git checkout -b feature/amazing-feature)

  6. Commit your changes (git commit -m 'Add some amazing feature')

  7. Push to the branch (git push origin feature/amazing-feature)

  8. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Links


Made with care by the LoonyLabs Team

GitHub stars Follow on GitHub