@loonylabs/tti-middleware 1.0.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.
@@ -0,0 +1,70 @@
1
+ /**
2
+ * TTI Service
3
+ *
4
+ * Main entry point for Text-to-Image generation.
5
+ * Manages providers and routes requests to the appropriate provider.
6
+ */
7
+ import { BaseTTIProvider } from './providers';
8
+ import { TTIRequest, TTIResponse, TTIProvider, ModelInfo } from '../../types';
9
+ export declare class TTIService {
10
+ private providers;
11
+ private defaultProvider;
12
+ constructor();
13
+ /**
14
+ * Parse a string to TTIProvider enum
15
+ */
16
+ private parseProvider;
17
+ /**
18
+ * Register a TTI provider
19
+ */
20
+ registerProvider(provider: BaseTTIProvider): void;
21
+ /**
22
+ * Get a registered provider
23
+ */
24
+ getProvider(name: TTIProvider): BaseTTIProvider | undefined;
25
+ /**
26
+ * Get all registered providers
27
+ */
28
+ getAvailableProviders(): TTIProvider[];
29
+ /**
30
+ * Check if a provider is available
31
+ */
32
+ isProviderAvailable(provider: TTIProvider): boolean;
33
+ /**
34
+ * Get the default provider
35
+ */
36
+ getDefaultProvider(): TTIProvider;
37
+ /**
38
+ * Set the default provider
39
+ */
40
+ setDefaultProvider(provider: TTIProvider): void;
41
+ /**
42
+ * List all models available across all registered providers
43
+ */
44
+ listAllModels(): Array<{
45
+ provider: TTIProvider;
46
+ models: ModelInfo[];
47
+ }>;
48
+ /**
49
+ * Find providers that support a specific capability
50
+ */
51
+ findProvidersWithCapability(capability: keyof ModelInfo['capabilities']): Array<{
52
+ provider: TTIProvider;
53
+ models: ModelInfo[];
54
+ }>;
55
+ /**
56
+ * Generate an image
57
+ *
58
+ * @param request The generation request
59
+ * @param provider Optional provider to use (defaults to default provider)
60
+ */
61
+ generate(request: TTIRequest, provider?: TTIProvider): Promise<TTIResponse>;
62
+ /**
63
+ * @deprecated Use generate() instead
64
+ */
65
+ generateImage(request: TTIRequest, provider?: TTIProvider | string): Promise<TTIResponse>;
66
+ /**
67
+ * @deprecated Use generate() with referenceImages in request instead
68
+ */
69
+ generateWithReference(request: TTIRequest, provider?: TTIProvider | string): Promise<TTIResponse>;
70
+ }
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ /**
3
+ * TTI Service
4
+ *
5
+ * Main entry point for Text-to-Image generation.
6
+ * Manages providers and routes requests to the appropriate provider.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.TTIService = void 0;
10
+ const providers_1 = require("./providers");
11
+ const types_1 = require("../../types");
12
+ class TTIService {
13
+ constructor() {
14
+ this.providers = new Map();
15
+ this.defaultProvider = types_1.TTIProvider.GOOGLE_CLOUD;
16
+ // Check for default provider from environment
17
+ const envDefault = process.env.TTI_DEFAULT_PROVIDER?.toLowerCase();
18
+ if (envDefault) {
19
+ const parsed = this.parseProvider(envDefault);
20
+ if (parsed) {
21
+ this.defaultProvider = parsed;
22
+ }
23
+ }
24
+ }
25
+ /**
26
+ * Parse a string to TTIProvider enum
27
+ */
28
+ parseProvider(value) {
29
+ const normalized = value.toLowerCase().trim();
30
+ const providerMap = {
31
+ // Google Cloud
32
+ 'google-cloud': types_1.TTIProvider.GOOGLE_CLOUD,
33
+ 'google_cloud': types_1.TTIProvider.GOOGLE_CLOUD,
34
+ googlecloud: types_1.TTIProvider.GOOGLE_CLOUD,
35
+ // Legacy names (backwards compatibility)
36
+ vertex_ai: types_1.TTIProvider.GOOGLE_CLOUD,
37
+ vertexai: types_1.TTIProvider.GOOGLE_CLOUD,
38
+ vertex: types_1.TTIProvider.GOOGLE_CLOUD,
39
+ gemini: types_1.TTIProvider.GOOGLE_CLOUD,
40
+ // Eden AI
41
+ edenai: types_1.TTIProvider.EDENAI,
42
+ eden_ai: types_1.TTIProvider.EDENAI,
43
+ // IONOS
44
+ ionos: types_1.TTIProvider.IONOS,
45
+ };
46
+ return providerMap[normalized] || null;
47
+ }
48
+ /**
49
+ * Register a TTI provider
50
+ */
51
+ registerProvider(provider) {
52
+ this.providers.set(provider.getName(), provider);
53
+ console.log(`[TTIService] Registered provider: ${provider.getDisplayName()}`);
54
+ }
55
+ /**
56
+ * Get a registered provider
57
+ */
58
+ getProvider(name) {
59
+ return this.providers.get(name);
60
+ }
61
+ /**
62
+ * Get all registered providers
63
+ */
64
+ getAvailableProviders() {
65
+ return Array.from(this.providers.keys());
66
+ }
67
+ /**
68
+ * Check if a provider is available
69
+ */
70
+ isProviderAvailable(provider) {
71
+ return this.providers.has(provider);
72
+ }
73
+ /**
74
+ * Get the default provider
75
+ */
76
+ getDefaultProvider() {
77
+ return this.defaultProvider;
78
+ }
79
+ /**
80
+ * Set the default provider
81
+ */
82
+ setDefaultProvider(provider) {
83
+ if (!this.providers.has(provider)) {
84
+ console.warn(`[TTIService] Provider ${provider} is not registered. Setting as default anyway.`);
85
+ }
86
+ this.defaultProvider = provider;
87
+ }
88
+ /**
89
+ * List all models available across all registered providers
90
+ */
91
+ listAllModels() {
92
+ const result = [];
93
+ for (const [name, provider] of this.providers) {
94
+ result.push({
95
+ provider: name,
96
+ models: provider.listModels(),
97
+ });
98
+ }
99
+ return result;
100
+ }
101
+ /**
102
+ * Find providers that support a specific capability
103
+ */
104
+ findProvidersWithCapability(capability) {
105
+ const result = [];
106
+ for (const [name, provider] of this.providers) {
107
+ const supportingModels = provider
108
+ .listModels()
109
+ .filter((m) => m.capabilities[capability]);
110
+ if (supportingModels.length > 0) {
111
+ result.push({
112
+ provider: name,
113
+ models: supportingModels,
114
+ });
115
+ }
116
+ }
117
+ return result;
118
+ }
119
+ /**
120
+ * Generate an image
121
+ *
122
+ * @param request The generation request
123
+ * @param provider Optional provider to use (defaults to default provider)
124
+ */
125
+ async generate(request, provider) {
126
+ const providerKey = provider || this.defaultProvider;
127
+ const providerInstance = this.providers.get(providerKey);
128
+ if (!providerInstance) {
129
+ // Try to find any registered provider as fallback
130
+ if (this.providers.size > 0) {
131
+ const entries = Array.from(this.providers.entries());
132
+ const [fallbackKey, fallbackProvider] = entries[0];
133
+ console.warn(`[TTIService] Provider ${providerKey} not found. Using fallback: ${fallbackKey}`);
134
+ return fallbackProvider.generate(request);
135
+ }
136
+ throw new providers_1.InvalidConfigError('TTIService', `Provider '${providerKey}' not found and no other providers registered.`);
137
+ }
138
+ return providerInstance.generate(request);
139
+ }
140
+ // ============================================================
141
+ // LEGACY METHODS (deprecated, for backwards compatibility)
142
+ // ============================================================
143
+ /**
144
+ * @deprecated Use generate() instead
145
+ */
146
+ async generateImage(request, provider) {
147
+ const providerKey = typeof provider === 'string'
148
+ ? this.parseProvider(provider) || this.defaultProvider
149
+ : provider || this.defaultProvider;
150
+ return this.generate(request, providerKey);
151
+ }
152
+ /**
153
+ * @deprecated Use generate() with referenceImages in request instead
154
+ */
155
+ async generateWithReference(request, provider) {
156
+ const providerKey = typeof provider === 'string'
157
+ ? this.parseProvider(provider) || this.defaultProvider
158
+ : provider || this.defaultProvider;
159
+ return this.generate(request, providerKey);
160
+ }
161
+ }
162
+ exports.TTIService = TTIService;
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Available TTI Providers
3
+ * Each provider represents a single backend/contract partner with one DPA
4
+ */
5
+ export declare enum TTIProvider {
6
+ /** Google Cloud Platform (Vertex AI) - includes Imagen and Gemini models. RECOMMENDED for EU/GDPR. */
7
+ GOOGLE_CLOUD = "google-cloud",
8
+ /** Eden AI - aggregator with multiple underlying models. EXPERIMENTAL - limited testing. */
9
+ EDENAI = "edenai",
10
+ /** IONOS Cloud. EXPERIMENTAL - limited testing. */
11
+ IONOS = "ionos"
12
+ }
13
+ /**
14
+ * Log levels for provider logging
15
+ */
16
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';
17
+ /**
18
+ * Log level priority (higher = more severe)
19
+ */
20
+ export declare const LOG_LEVEL_PRIORITY: Record<LogLevel, number>;
21
+ /**
22
+ * Capabilities of a specific model
23
+ */
24
+ export interface TTICapabilities {
25
+ /** Basic text-to-image generation */
26
+ textToImage: boolean;
27
+ /** Character consistency via reference images */
28
+ characterConsistency: boolean;
29
+ /** Image editing (inpainting, outpainting) - future */
30
+ imageEditing: boolean;
31
+ /** Maximum images per request */
32
+ maxImagesPerRequest: number;
33
+ }
34
+ /**
35
+ * Information about a specific model within a provider
36
+ */
37
+ export interface ModelInfo {
38
+ /** Internal model ID used in API calls */
39
+ id: string;
40
+ /** Human-readable display name */
41
+ displayName: string;
42
+ /** What the model can do */
43
+ capabilities: TTICapabilities;
44
+ /** Available regions (for providers with regional endpoints) */
45
+ availableRegions?: string[];
46
+ /** Link to official pricing page */
47
+ pricingUrl?: string;
48
+ }
49
+ /**
50
+ * Google Cloud regions
51
+ * EU regions are GDPR-compliant
52
+ */
53
+ export type GoogleCloudRegion = 'europe-west1' | 'europe-west2' | 'europe-west3' | 'europe-west4' | 'europe-west9' | 'us-central1' | 'us-east4';
54
+ /**
55
+ * Reference image for character consistency
56
+ */
57
+ export interface TTIReferenceImage {
58
+ /** Base64-encoded image data */
59
+ base64: string;
60
+ /** MIME type of the image (e.g., 'image/png', 'image/jpeg') */
61
+ mimeType?: string;
62
+ }
63
+ /**
64
+ * Unified TTI generation request
65
+ * Works for both simple text-to-image and character consistency
66
+ */
67
+ export interface TTIRequest {
68
+ /** The text prompt describing what to generate */
69
+ prompt: string;
70
+ /** Model ID to use (provider-specific, e.g., 'imagen-3', 'gemini-flash-image') */
71
+ model?: string;
72
+ /** Number of images to generate (default: 1) */
73
+ n?: number;
74
+ /** Aspect ratio (e.g., '1:1', '16:9', '4:3') */
75
+ aspectRatio?: string;
76
+ /**
77
+ * Reference images for character consistency
78
+ * If provided, the model will try to maintain visual consistency
79
+ */
80
+ referenceImages?: TTIReferenceImage[];
81
+ /**
82
+ * Description of the subject in reference images
83
+ * Required when using referenceImages (e.g., "cute cartoon bear with red hat")
84
+ */
85
+ subjectDescription?: string;
86
+ /** Additional provider-specific options */
87
+ providerOptions?: Record<string, unknown>;
88
+ /**
89
+ * Retry configuration for rate limit errors (429)
90
+ * - true: use default retry (2 retries, 1s delay)
91
+ * - false: disable retry
92
+ * - RetryOptions: custom configuration
93
+ * Default: true (retry enabled with defaults)
94
+ */
95
+ retry?: boolean | RetryOptions;
96
+ }
97
+ /**
98
+ * Generated image
99
+ */
100
+ export interface TTIImage {
101
+ /** URL to the generated image (if available) */
102
+ url?: string;
103
+ /** Base64-encoded image data */
104
+ base64?: string;
105
+ /** MIME type (e.g., 'image/png', 'image/jpeg') */
106
+ contentType?: string;
107
+ }
108
+ /**
109
+ * Usage metrics for a generation request
110
+ */
111
+ export interface TTIUsage {
112
+ /** Number of images generated */
113
+ imagesGenerated: number;
114
+ /** Size of generated images */
115
+ imageSize?: string;
116
+ /** Model used for generation */
117
+ modelId: string;
118
+ /** Input tokens (for token-based models) */
119
+ inputTokens?: number;
120
+ /** Output tokens (for token-based models) */
121
+ outputTokens?: number;
122
+ }
123
+ /**
124
+ * Billing information (only if provider returns it)
125
+ */
126
+ export interface TTIBilling {
127
+ /** Cost of the request */
128
+ cost: number;
129
+ /** Currency */
130
+ currency: string;
131
+ /** Source of the cost information */
132
+ source: 'provider' | 'estimated';
133
+ }
134
+ /**
135
+ * Response from a TTI generation request
136
+ */
137
+ export interface TTIResponse {
138
+ /** Generated images */
139
+ images: TTIImage[];
140
+ /** Request metadata */
141
+ metadata: {
142
+ /** Provider that handled the request */
143
+ provider: string;
144
+ /** Model used */
145
+ model: string;
146
+ /** Region where request was processed (if applicable) */
147
+ region?: string;
148
+ /** Request duration in milliseconds */
149
+ duration: number;
150
+ };
151
+ /** Usage metrics */
152
+ usage: TTIUsage;
153
+ /** Billing info (only if provider returns actual costs) */
154
+ billing?: TTIBilling;
155
+ }
156
+ export type TTIErrorCode = 'INVALID_CONFIG' | 'QUOTA_EXCEEDED' | 'PROVIDER_UNAVAILABLE' | 'GENERATION_FAILED' | 'NETWORK_ERROR' | 'UNAUTHORIZED' | 'CAPABILITY_NOT_SUPPORTED';
157
+ /**
158
+ * Configuration for retry behavior on rate limits (429 errors)
159
+ */
160
+ export interface RetryOptions {
161
+ /**
162
+ * Maximum number of retry attempts (default: 2)
163
+ * Total attempts = 1 (initial) + maxRetries
164
+ */
165
+ maxRetries?: number;
166
+ /**
167
+ * Base delay between retries in milliseconds (default: 1000)
168
+ */
169
+ delayMs?: number;
170
+ /**
171
+ * Use incremental backoff: delay increases by delayMs each retry
172
+ * false: always wait delayMs (e.g., 1s, 1s, 1s)
173
+ * true: wait delayMs * attempt (e.g., 1s, 2s, 3s)
174
+ * Default: false
175
+ */
176
+ incrementalBackoff?: boolean;
177
+ }
178
+ /**
179
+ * Default retry configuration
180
+ */
181
+ export declare const DEFAULT_RETRY_OPTIONS: Required<RetryOptions>;
182
+ /**
183
+ * Interface that all TTI providers must implement
184
+ */
185
+ export interface ITTIProvider {
186
+ /** Get the provider identifier */
187
+ getName(): TTIProvider;
188
+ /** Get human-readable display name */
189
+ getDisplayName(): string;
190
+ /** List all available models */
191
+ listModels(): ModelInfo[];
192
+ /** Get the default model ID */
193
+ getDefaultModel(): string;
194
+ /** Generate images from a request */
195
+ generate(request: TTIRequest): Promise<TTIResponse>;
196
+ }
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ // ============================================================
3
+ // PROVIDER IDENTIFICATION
4
+ // ============================================================
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DEFAULT_RETRY_OPTIONS = exports.LOG_LEVEL_PRIORITY = exports.TTIProvider = void 0;
7
+ /**
8
+ * Available TTI Providers
9
+ * Each provider represents a single backend/contract partner with one DPA
10
+ */
11
+ var TTIProvider;
12
+ (function (TTIProvider) {
13
+ /** Google Cloud Platform (Vertex AI) - includes Imagen and Gemini models. RECOMMENDED for EU/GDPR. */
14
+ TTIProvider["GOOGLE_CLOUD"] = "google-cloud";
15
+ /** Eden AI - aggregator with multiple underlying models. EXPERIMENTAL - limited testing. */
16
+ TTIProvider["EDENAI"] = "edenai";
17
+ /** IONOS Cloud. EXPERIMENTAL - limited testing. */
18
+ TTIProvider["IONOS"] = "ionos";
19
+ })(TTIProvider || (exports.TTIProvider = TTIProvider = {}));
20
+ /**
21
+ * Log level priority (higher = more severe)
22
+ */
23
+ exports.LOG_LEVEL_PRIORITY = {
24
+ debug: 0,
25
+ info: 1,
26
+ warn: 2,
27
+ error: 3,
28
+ silent: 4,
29
+ };
30
+ /**
31
+ * Default retry configuration
32
+ */
33
+ exports.DEFAULT_RETRY_OPTIONS = {
34
+ maxRetries: 2,
35
+ delayMs: 1000,
36
+ incrementalBackoff: false,
37
+ };
package/package.json ADDED
@@ -0,0 +1,90 @@
1
+ {
2
+ "name": "@loonylabs/tti-middleware",
3
+ "version": "1.0.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": ">=0.14.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": "^0.14.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
+ }