@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,98 @@
1
+ /**
2
+ * Base TTI Provider Abstract Class
3
+ *
4
+ * All providers must extend this class and implement the ITTIProvider interface.
5
+ * Provides common functionality for error handling, logging, and validation.
6
+ */
7
+ import { TTIProvider, TTIRequest, TTIResponse, TTIErrorCode, ITTIProvider, ModelInfo, RetryOptions, LogLevel } from '../../../types';
8
+ export declare class TTIError extends Error {
9
+ readonly provider: string;
10
+ readonly code: TTIErrorCode;
11
+ readonly cause?: Error | undefined;
12
+ constructor(provider: string, code: TTIErrorCode, message: string, cause?: Error | undefined);
13
+ toString(): string;
14
+ }
15
+ export declare class InvalidConfigError extends TTIError {
16
+ constructor(provider: string, message: string, cause?: Error);
17
+ }
18
+ export declare class QuotaExceededError extends TTIError {
19
+ constructor(provider: string, message?: string, cause?: Error);
20
+ }
21
+ export declare class ProviderUnavailableError extends TTIError {
22
+ constructor(provider: string, message?: string, cause?: Error);
23
+ }
24
+ export declare class GenerationFailedError extends TTIError {
25
+ constructor(provider: string, message: string, cause?: Error);
26
+ }
27
+ export declare class NetworkError extends TTIError {
28
+ constructor(provider: string, message: string, cause?: Error);
29
+ }
30
+ export declare class CapabilityNotSupportedError extends TTIError {
31
+ constructor(provider: string, capability: string, model: string, cause?: Error);
32
+ }
33
+ /**
34
+ * Set the global log level for all TTI providers
35
+ */
36
+ export declare function setLogLevel(level: LogLevel): void;
37
+ /**
38
+ * Get the current global log level
39
+ */
40
+ export declare function getLogLevel(): LogLevel;
41
+ export declare abstract class BaseTTIProvider implements ITTIProvider {
42
+ protected readonly providerName: TTIProvider;
43
+ constructor(providerName: TTIProvider);
44
+ abstract getDisplayName(): string;
45
+ abstract listModels(): ModelInfo[];
46
+ abstract getDefaultModel(): string;
47
+ abstract generate(request: TTIRequest): Promise<TTIResponse>;
48
+ getName(): TTIProvider;
49
+ /**
50
+ * Get model info by ID
51
+ */
52
+ protected getModelInfo(modelId: string): ModelInfo | undefined;
53
+ /**
54
+ * Check if a model supports a specific capability
55
+ */
56
+ protected modelSupportsCapability(modelId: string, capability: keyof ModelInfo['capabilities']): boolean;
57
+ /**
58
+ * Validate that the request is valid
59
+ */
60
+ protected validateRequest(request: TTIRequest): void;
61
+ /**
62
+ * Resolve retry configuration from request
63
+ */
64
+ protected resolveRetryConfig(request: TTIRequest): Required<RetryOptions> | null;
65
+ /**
66
+ * Calculate delay for a specific retry attempt
67
+ */
68
+ protected calculateRetryDelay(attempt: number, config: Required<RetryOptions>): number;
69
+ /**
70
+ * Sleep for a specified duration
71
+ */
72
+ protected sleep(ms: number): Promise<void>;
73
+ /**
74
+ * Execute a generation function with retry logic for rate limits
75
+ */
76
+ protected executeWithRetry<T>(request: TTIRequest, operation: () => Promise<T>, operationName: string): Promise<T>;
77
+ /**
78
+ * Check if an error is a rate limit error (429)
79
+ */
80
+ protected isRateLimitError(error: Error): boolean;
81
+ /**
82
+ * Convert errors to TTIError instances with proper classification
83
+ */
84
+ protected handleError(error: Error, context?: string): TTIError;
85
+ /**
86
+ * Log messages with provider context
87
+ * Respects global log level setting
88
+ */
89
+ protected log(level: 'info' | 'warn' | 'error' | 'debug', message: string, meta?: Record<string, unknown>): void;
90
+ }
91
+ /**
92
+ * Type guard to check if a request includes reference images
93
+ */
94
+ export declare function hasReferenceImages(request: TTIRequest): boolean;
95
+ /**
96
+ * Check if a region is EU-compliant for GDPR purposes
97
+ */
98
+ export declare function isEURegion(region: string): boolean;
@@ -0,0 +1,306 @@
1
+ "use strict";
2
+ /**
3
+ * Base TTI Provider Abstract Class
4
+ *
5
+ * All providers must extend this class and implement the ITTIProvider interface.
6
+ * Provides common functionality for error handling, logging, and validation.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.BaseTTIProvider = exports.CapabilityNotSupportedError = exports.NetworkError = exports.GenerationFailedError = exports.ProviderUnavailableError = exports.QuotaExceededError = exports.InvalidConfigError = exports.TTIError = void 0;
10
+ exports.setLogLevel = setLogLevel;
11
+ exports.getLogLevel = getLogLevel;
12
+ exports.hasReferenceImages = hasReferenceImages;
13
+ exports.isEURegion = isEURegion;
14
+ const types_1 = require("../../../types");
15
+ // ============================================================
16
+ // ERROR CLASSES
17
+ // ============================================================
18
+ class TTIError extends Error {
19
+ constructor(provider, code, message, cause) {
20
+ super(message);
21
+ this.provider = provider;
22
+ this.code = code;
23
+ this.cause = cause;
24
+ this.name = 'TTIError';
25
+ if (Error.captureStackTrace) {
26
+ Error.captureStackTrace(this, TTIError);
27
+ }
28
+ }
29
+ toString() {
30
+ return `[${this.provider}] ${this.code}: ${this.message}${this.cause ? ` (caused by: ${this.cause.message})` : ''}`;
31
+ }
32
+ }
33
+ exports.TTIError = TTIError;
34
+ class InvalidConfigError extends TTIError {
35
+ constructor(provider, message, cause) {
36
+ super(provider, 'INVALID_CONFIG', message, cause);
37
+ this.name = 'InvalidConfigError';
38
+ }
39
+ }
40
+ exports.InvalidConfigError = InvalidConfigError;
41
+ class QuotaExceededError extends TTIError {
42
+ constructor(provider, message, cause) {
43
+ super(provider, 'QUOTA_EXCEEDED', message || 'Provider quota or rate limit exceeded', cause);
44
+ this.name = 'QuotaExceededError';
45
+ }
46
+ }
47
+ exports.QuotaExceededError = QuotaExceededError;
48
+ class ProviderUnavailableError extends TTIError {
49
+ constructor(provider, message, cause) {
50
+ super(provider, 'PROVIDER_UNAVAILABLE', message || 'Provider service is temporarily unavailable', cause);
51
+ this.name = 'ProviderUnavailableError';
52
+ }
53
+ }
54
+ exports.ProviderUnavailableError = ProviderUnavailableError;
55
+ class GenerationFailedError extends TTIError {
56
+ constructor(provider, message, cause) {
57
+ super(provider, 'GENERATION_FAILED', message, cause);
58
+ this.name = 'GenerationFailedError';
59
+ }
60
+ }
61
+ exports.GenerationFailedError = GenerationFailedError;
62
+ class NetworkError extends TTIError {
63
+ constructor(provider, message, cause) {
64
+ super(provider, 'NETWORK_ERROR', message, cause);
65
+ this.name = 'NetworkError';
66
+ }
67
+ }
68
+ exports.NetworkError = NetworkError;
69
+ class CapabilityNotSupportedError extends TTIError {
70
+ constructor(provider, capability, model, cause) {
71
+ super(provider, 'CAPABILITY_NOT_SUPPORTED', `Model '${model}' does not support '${capability}'`, cause);
72
+ this.name = 'CapabilityNotSupportedError';
73
+ }
74
+ }
75
+ exports.CapabilityNotSupportedError = CapabilityNotSupportedError;
76
+ // ============================================================
77
+ // BASE PROVIDER CLASS
78
+ // ============================================================
79
+ /**
80
+ * Global log level for all providers
81
+ * Set via TTI_LOG_LEVEL environment variable or setLogLevel()
82
+ */
83
+ let globalLogLevel = process.env.TTI_LOG_LEVEL || 'info';
84
+ /**
85
+ * Set the global log level for all TTI providers
86
+ */
87
+ function setLogLevel(level) {
88
+ globalLogLevel = level;
89
+ }
90
+ /**
91
+ * Get the current global log level
92
+ */
93
+ function getLogLevel() {
94
+ return globalLogLevel;
95
+ }
96
+ class BaseTTIProvider {
97
+ constructor(providerName) {
98
+ this.providerName = providerName;
99
+ }
100
+ // ============================================================
101
+ // IMPLEMENTED METHODS
102
+ // ============================================================
103
+ getName() {
104
+ return this.providerName;
105
+ }
106
+ /**
107
+ * Get model info by ID
108
+ */
109
+ getModelInfo(modelId) {
110
+ return this.listModels().find((m) => m.id === modelId);
111
+ }
112
+ /**
113
+ * Check if a model supports a specific capability
114
+ */
115
+ modelSupportsCapability(modelId, capability) {
116
+ const model = this.getModelInfo(modelId);
117
+ if (!model)
118
+ return false;
119
+ return model.capabilities[capability];
120
+ }
121
+ /**
122
+ * Validate that the request is valid
123
+ */
124
+ validateRequest(request) {
125
+ if (!request.prompt || request.prompt.trim().length === 0) {
126
+ throw new InvalidConfigError(this.providerName, 'Prompt cannot be empty');
127
+ }
128
+ // If reference images are provided, validate them
129
+ if (request.referenceImages && request.referenceImages.length > 0) {
130
+ const modelId = request.model || this.getDefaultModel();
131
+ // Check if model supports character consistency
132
+ if (!this.modelSupportsCapability(modelId, 'characterConsistency')) {
133
+ throw new CapabilityNotSupportedError(this.providerName, 'characterConsistency', modelId);
134
+ }
135
+ // Validate subject description is provided
136
+ if (!request.subjectDescription || request.subjectDescription.trim().length === 0) {
137
+ throw new InvalidConfigError(this.providerName, 'subjectDescription is required when using referenceImages');
138
+ }
139
+ // Validate reference images have data
140
+ for (let i = 0; i < request.referenceImages.length; i++) {
141
+ const ref = request.referenceImages[i];
142
+ if (!ref.base64 || ref.base64.trim().length === 0) {
143
+ throw new InvalidConfigError(this.providerName, `Reference image at index ${i} has empty base64 data`);
144
+ }
145
+ }
146
+ }
147
+ }
148
+ // ============================================================
149
+ // RETRY LOGIC
150
+ // ============================================================
151
+ /**
152
+ * Resolve retry configuration from request
153
+ */
154
+ resolveRetryConfig(request) {
155
+ const retryOption = request.retry;
156
+ // Explicit disable
157
+ if (retryOption === false) {
158
+ return null;
159
+ }
160
+ // Default (undefined) or explicit true: use defaults
161
+ if (retryOption === undefined || retryOption === true) {
162
+ return { ...types_1.DEFAULT_RETRY_OPTIONS };
163
+ }
164
+ // Custom configuration: merge with defaults
165
+ return {
166
+ maxRetries: retryOption.maxRetries ?? types_1.DEFAULT_RETRY_OPTIONS.maxRetries,
167
+ delayMs: retryOption.delayMs ?? types_1.DEFAULT_RETRY_OPTIONS.delayMs,
168
+ incrementalBackoff: retryOption.incrementalBackoff ?? types_1.DEFAULT_RETRY_OPTIONS.incrementalBackoff,
169
+ };
170
+ }
171
+ /**
172
+ * Calculate delay for a specific retry attempt
173
+ */
174
+ calculateRetryDelay(attempt, config) {
175
+ if (config.incrementalBackoff) {
176
+ // Incremental: 1s, 2s, 3s, ...
177
+ return config.delayMs * attempt;
178
+ }
179
+ // Static: always same delay
180
+ return config.delayMs;
181
+ }
182
+ /**
183
+ * Sleep for a specified duration
184
+ */
185
+ sleep(ms) {
186
+ return new Promise((resolve) => setTimeout(resolve, ms));
187
+ }
188
+ /**
189
+ * Execute a generation function with retry logic for rate limits
190
+ */
191
+ async executeWithRetry(request, operation, operationName) {
192
+ const retryConfig = this.resolveRetryConfig(request);
193
+ // No retry configured
194
+ if (!retryConfig) {
195
+ return operation();
196
+ }
197
+ let lastError = null;
198
+ const maxAttempts = 1 + retryConfig.maxRetries; // initial + retries
199
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
200
+ try {
201
+ return await operation();
202
+ }
203
+ catch (error) {
204
+ lastError = error;
205
+ // Check if this is a rate limit error (429)
206
+ const isRateLimitError = this.isRateLimitError(error);
207
+ // Only retry on rate limit errors
208
+ if (!isRateLimitError) {
209
+ throw error;
210
+ }
211
+ // Check if we have retries left
212
+ if (attempt < maxAttempts) {
213
+ const delay = this.calculateRetryDelay(attempt, retryConfig);
214
+ this.log('warn', `Rate limit hit during ${operationName}. Retry ${attempt}/${retryConfig.maxRetries} in ${delay}ms...`, { attempt, maxRetries: retryConfig.maxRetries, delayMs: delay });
215
+ await this.sleep(delay);
216
+ }
217
+ }
218
+ }
219
+ // All retries exhausted
220
+ throw lastError;
221
+ }
222
+ /**
223
+ * Check if an error is a rate limit error (429)
224
+ */
225
+ isRateLimitError(error) {
226
+ const message = error.message.toLowerCase();
227
+ return (message.includes('429') ||
228
+ message.includes('rate limit') ||
229
+ message.includes('quota exceeded') ||
230
+ message.includes('too many requests') ||
231
+ message.includes('resource exhausted'));
232
+ }
233
+ /**
234
+ * Convert errors to TTIError instances with proper classification
235
+ */
236
+ handleError(error, context) {
237
+ if (error instanceof TTIError) {
238
+ return error;
239
+ }
240
+ const errorMessage = error.message.toLowerCase();
241
+ if (errorMessage.includes('401') || errorMessage.includes('403')) {
242
+ return new InvalidConfigError(this.providerName, `Authentication failed${context ? `: ${context}` : ''}`, error);
243
+ }
244
+ if (errorMessage.includes('429')) {
245
+ return new QuotaExceededError(this.providerName, `Rate limit exceeded${context ? `: ${context}` : ''}`, error);
246
+ }
247
+ if (errorMessage.includes('503') ||
248
+ errorMessage.includes('504') ||
249
+ errorMessage.includes('502')) {
250
+ return new ProviderUnavailableError(this.providerName, `Service temporarily unavailable${context ? `: ${context}` : ''}`, error);
251
+ }
252
+ if (errorMessage.includes('timeout') ||
253
+ errorMessage.includes('econnrefused') ||
254
+ errorMessage.includes('enotfound')) {
255
+ return new NetworkError(this.providerName, `Network error${context ? `: ${context}` : ''}`, error);
256
+ }
257
+ return new GenerationFailedError(this.providerName, `Generation failed${context ? `: ${context}` : ''}: ${error.message}`, error);
258
+ }
259
+ /**
260
+ * Log messages with provider context
261
+ * Respects global log level setting
262
+ */
263
+ log(level, message, meta) {
264
+ // Check if we should log this level
265
+ const currentPriority = types_1.LOG_LEVEL_PRIORITY[level];
266
+ const minPriority = types_1.LOG_LEVEL_PRIORITY[globalLogLevel];
267
+ if (currentPriority < minPriority) {
268
+ return; // Skip logging if below minimum level
269
+ }
270
+ const timestamp = new Date().toISOString();
271
+ const prefix = `[${timestamp}] [${this.providerName.toUpperCase()}] [${level.toUpperCase()}]`;
272
+ if (meta) {
273
+ console[level](prefix, message, meta);
274
+ }
275
+ else {
276
+ console[level](prefix, message);
277
+ }
278
+ }
279
+ }
280
+ exports.BaseTTIProvider = BaseTTIProvider;
281
+ // ============================================================
282
+ // HELPER FUNCTIONS
283
+ // ============================================================
284
+ /**
285
+ * Type guard to check if a request includes reference images
286
+ */
287
+ function hasReferenceImages(request) {
288
+ return (request.referenceImages !== undefined &&
289
+ Array.isArray(request.referenceImages) &&
290
+ request.referenceImages.length > 0);
291
+ }
292
+ /**
293
+ * Check if a region is EU-compliant for GDPR purposes
294
+ */
295
+ function isEURegion(region) {
296
+ const euRegions = [
297
+ 'europe-west1',
298
+ 'europe-west2',
299
+ 'europe-west3',
300
+ 'europe-west4',
301
+ 'europe-west9',
302
+ 'europe-north1',
303
+ 'europe-central2',
304
+ ];
305
+ return euRegions.includes(region);
306
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Eden AI TTI Provider
3
+ *
4
+ * Aggregator service that provides access to multiple AI providers
5
+ * through a unified API.
6
+ *
7
+ * @see https://www.edenai.co/
8
+ * @see https://docs.edenai.co/docs/pricing
9
+ */
10
+ import { TTIRequest, TTIResponse, ModelInfo } from '../../../types';
11
+ import { BaseTTIProvider } from './base-tti-provider';
12
+ interface EdenAIConfig {
13
+ apiKey: string;
14
+ apiUrl?: string;
15
+ }
16
+ export declare class EdenAIProvider extends BaseTTIProvider {
17
+ private config;
18
+ private readonly apiUrl;
19
+ constructor(config?: Partial<EdenAIConfig>);
20
+ getDisplayName(): string;
21
+ listModels(): ModelInfo[];
22
+ getDefaultModel(): string;
23
+ generate(request: TTIRequest): Promise<TTIResponse>;
24
+ private executeGeneration;
25
+ private aspectRatioToSize;
26
+ private processResponse;
27
+ }
28
+ export {};
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ /**
3
+ * Eden AI TTI Provider
4
+ *
5
+ * Aggregator service that provides access to multiple AI providers
6
+ * through a unified API.
7
+ *
8
+ * @see https://www.edenai.co/
9
+ * @see https://docs.edenai.co/docs/pricing
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.EdenAIProvider = void 0;
13
+ const types_1 = require("../../../types");
14
+ const base_tti_provider_1 = require("./base-tti-provider");
15
+ // ============================================================
16
+ // MODEL DEFINITIONS
17
+ // ============================================================
18
+ const EDENAI_MODELS = [
19
+ {
20
+ id: 'openai',
21
+ displayName: 'OpenAI DALL-E',
22
+ capabilities: {
23
+ textToImage: true,
24
+ characterConsistency: false,
25
+ imageEditing: false,
26
+ maxImagesPerRequest: 4,
27
+ },
28
+ pricingUrl: 'https://www.edenai.co/pricing',
29
+ },
30
+ {
31
+ id: 'stabilityai',
32
+ displayName: 'Stability AI',
33
+ capabilities: {
34
+ textToImage: true,
35
+ characterConsistency: false,
36
+ imageEditing: false,
37
+ maxImagesPerRequest: 4,
38
+ },
39
+ pricingUrl: 'https://www.edenai.co/pricing',
40
+ },
41
+ {
42
+ id: 'replicate',
43
+ displayName: 'Replicate',
44
+ capabilities: {
45
+ textToImage: true,
46
+ characterConsistency: false,
47
+ imageEditing: false,
48
+ maxImagesPerRequest: 4,
49
+ },
50
+ pricingUrl: 'https://www.edenai.co/pricing',
51
+ },
52
+ ];
53
+ // ============================================================
54
+ // PROVIDER IMPLEMENTATION
55
+ // ============================================================
56
+ class EdenAIProvider extends base_tti_provider_1.BaseTTIProvider {
57
+ constructor(config) {
58
+ super(types_1.TTIProvider.EDENAI);
59
+ this.config = {
60
+ apiKey: config?.apiKey || process.env.EDENAI_API_KEY || '',
61
+ apiUrl: config?.apiUrl,
62
+ };
63
+ this.apiUrl =
64
+ this.config.apiUrl || 'https://api.edenai.run/v2/image/generation';
65
+ if (!this.config.apiKey) {
66
+ throw new base_tti_provider_1.InvalidConfigError(this.providerName, 'EdenAI API key is required (EDENAI_API_KEY)');
67
+ }
68
+ this.log('info', 'Eden AI Provider initialized');
69
+ }
70
+ // ============================================================
71
+ // ITTIProvider IMPLEMENTATION
72
+ // ============================================================
73
+ getDisplayName() {
74
+ return 'Eden AI';
75
+ }
76
+ listModels() {
77
+ return EDENAI_MODELS;
78
+ }
79
+ getDefaultModel() {
80
+ return 'openai';
81
+ }
82
+ async generate(request) {
83
+ this.validateRequest(request);
84
+ return this.executeWithRetry(request, () => this.executeGeneration(request), 'EdenAI API call');
85
+ }
86
+ async executeGeneration(request) {
87
+ const startTime = Date.now();
88
+ const modelId = request.model || this.getDefaultModel();
89
+ const body = {
90
+ providers: modelId,
91
+ text: request.prompt,
92
+ resolution: request.aspectRatio ? this.aspectRatioToSize(request.aspectRatio) : '1024x1024',
93
+ num_images: request.n || 1,
94
+ };
95
+ this.log('debug', 'Generating image with EdenAI', {
96
+ provider: modelId,
97
+ size: body.resolution,
98
+ });
99
+ try {
100
+ const response = await fetch(this.apiUrl, {
101
+ method: 'POST',
102
+ headers: {
103
+ Authorization: `Bearer ${this.config.apiKey}`,
104
+ 'Content-Type': 'application/json',
105
+ },
106
+ body: JSON.stringify(body),
107
+ });
108
+ if (!response.ok) {
109
+ const errorText = await response.text();
110
+ throw new Error(`EdenAI API error (${response.status}): ${errorText}`);
111
+ }
112
+ const data = (await response.json());
113
+ this.log('debug', 'Raw EdenAI response', { data: JSON.stringify(data) });
114
+ const duration = Date.now() - startTime;
115
+ return this.processResponse(data, modelId, duration);
116
+ }
117
+ catch (error) {
118
+ throw this.handleError(error, 'during EdenAI API call');
119
+ }
120
+ }
121
+ // ============================================================
122
+ // PRIVATE METHODS
123
+ // ============================================================
124
+ aspectRatioToSize(aspectRatio) {
125
+ const mapping = {
126
+ '1:1': '1024x1024',
127
+ '16:9': '1792x1024',
128
+ '9:16': '1024x1792',
129
+ '4:3': '1024x768',
130
+ '3:4': '768x1024',
131
+ };
132
+ return mapping[aspectRatio] || '1024x1024';
133
+ }
134
+ processResponse(data, provider, duration) {
135
+ const responseKeys = Object.keys(data);
136
+ const matchedKey = responseKeys.find((key) => key.startsWith(provider));
137
+ const providerData = matchedKey ? data[matchedKey] : undefined;
138
+ if (!providerData) {
139
+ this.log('error', `Provider data not found for ${provider}. Available keys: ${responseKeys.join(', ')}`);
140
+ throw new base_tti_provider_1.GenerationFailedError(this.providerName, `No data returned for provider: ${provider}`);
141
+ }
142
+ if (providerData.status === 'fail' || providerData.error) {
143
+ throw new base_tti_provider_1.GenerationFailedError(this.providerName, providerData.error || 'Unknown error from provider');
144
+ }
145
+ const items = providerData.items || [];
146
+ const images = [];
147
+ for (const item of items) {
148
+ if (item.image) {
149
+ images.push({ base64: item.image, contentType: 'image/png' });
150
+ }
151
+ else if (item.image_resource_url) {
152
+ images.push({ url: item.image_resource_url });
153
+ }
154
+ }
155
+ if (images.length === 0) {
156
+ throw new base_tti_provider_1.GenerationFailedError(this.providerName, 'No images returned in response');
157
+ }
158
+ const usage = {
159
+ imagesGenerated: images.length,
160
+ modelId: provider,
161
+ };
162
+ return {
163
+ images,
164
+ metadata: {
165
+ provider: this.providerName,
166
+ model: provider,
167
+ duration,
168
+ },
169
+ usage,
170
+ // Eden AI returns actual cost from provider!
171
+ billing: providerData.cost
172
+ ? {
173
+ cost: providerData.cost,
174
+ currency: 'USD',
175
+ source: 'provider',
176
+ }
177
+ : undefined,
178
+ };
179
+ }
180
+ }
181
+ exports.EdenAIProvider = EdenAIProvider;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Google Cloud TTI Provider
3
+ *
4
+ * Unified provider for Google Cloud's image generation services:
5
+ * - Imagen 3 (imagegeneration@006) - High quality text-to-image
6
+ * - Gemini 2.5 Flash Image - Text-to-image with character consistency
7
+ *
8
+ * All requests go through Google Cloud (Vertex AI) with proper DPA.
9
+ * EU-compliant when using EU regions.
10
+ *
11
+ * @see https://cloud.google.com/vertex-ai/generative-ai/pricing
12
+ * @see https://cloud.google.com/terms/data-processing-addendum
13
+ */
14
+ import { TTIRequest, TTIResponse, ModelInfo, GoogleCloudRegion } from '../../../types';
15
+ import { BaseTTIProvider } from './base-tti-provider';
16
+ interface GoogleCloudConfig {
17
+ /** Google Cloud Project ID */
18
+ projectId: string;
19
+ /** Default region for requests */
20
+ region: GoogleCloudRegion;
21
+ /** Path to service account JSON file */
22
+ keyFilename?: string;
23
+ /** Service account credentials object (alternative to keyFilename) */
24
+ credentials?: {
25
+ client_email: string;
26
+ private_key: string;
27
+ project_id?: string;
28
+ };
29
+ }
30
+ export declare class GoogleCloudTTIProvider extends BaseTTIProvider {
31
+ private config;
32
+ private lastUsedRegion;
33
+ private aiplatformClient;
34
+ private genaiClient;
35
+ constructor(config?: Partial<GoogleCloudConfig>);
36
+ getDisplayName(): string;
37
+ listModels(): ModelInfo[];
38
+ getDefaultModel(): string;
39
+ generate(request: TTIRequest): Promise<TTIResponse>;
40
+ /**
41
+ * Get the configured region
42
+ */
43
+ getRegion(): GoogleCloudRegion;
44
+ /**
45
+ * Check if the configured region is hosted in the EU
46
+ */
47
+ isEURegion(): boolean;
48
+ /**
49
+ * Get the effective region for a model, considering availability
50
+ */
51
+ private getEffectiveRegion;
52
+ private generateWithImagen;
53
+ private getAiplatformClient;
54
+ private processImagenResponse;
55
+ private generateWithGemini;
56
+ private getGenaiClient;
57
+ private buildCharacterConsistencyPrompt;
58
+ private processGeminiResponse;
59
+ }
60
+ export {};