@loonylabs/tti-middleware 1.0.0 → 1.1.1
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 +90 -6
- package/dist/middleware/services/tti/assets/placeholder-image.d.ts +6 -0
- package/dist/middleware/services/tti/assets/placeholder-image.js +9 -0
- package/dist/middleware/services/tti/index.d.ts +1 -0
- package/dist/middleware/services/tti/index.js +1 -0
- package/dist/middleware/services/tti/providers/base-tti-provider.d.ts +27 -1
- package/dist/middleware/services/tti/providers/base-tti-provider.js +78 -3
- package/dist/middleware/services/tti/providers/edenai-provider.d.ts +1 -1
- package/dist/middleware/services/tti/providers/edenai-provider.js +2 -2
- package/dist/middleware/services/tti/providers/google-cloud-provider.d.ts +1 -1
- package/dist/middleware/services/tti/providers/google-cloud-provider.js +48 -15
- package/dist/middleware/services/tti/providers/ionos-provider.d.ts +1 -1
- package/dist/middleware/services/tti/providers/ionos-provider.js +2 -2
- package/dist/middleware/services/tti/utils/debug-tti.utils.d.ts +150 -0
- package/dist/middleware/services/tti/utils/debug-tti.utils.js +418 -0
- package/dist/middleware/services/tti/utils/index.d.ts +1 -0
- package/dist/middleware/services/tti/utils/index.js +17 -0
- package/dist/middleware/types/index.d.ts +10 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
- **Retry Logic**: Automatic retry for rate limits (429) with configurable backoff
|
|
49
49
|
- **TypeScript First**: Full type safety with comprehensive interfaces
|
|
50
50
|
- **Logging Control**: Configurable log levels via environment or API
|
|
51
|
+
- **Debug Logging**: Markdown file logging for debugging prompts and responses
|
|
51
52
|
- **Error Handling**: Typed error classes for precise error handling
|
|
52
53
|
|
|
53
54
|
## Quick Start
|
|
@@ -103,7 +104,7 @@ const character = await service.generate({
|
|
|
103
104
|
model: 'gemini-flash-image', // Only this model supports character consistency!
|
|
104
105
|
});
|
|
105
106
|
|
|
106
|
-
// 2. Generate new scenes with the same character
|
|
107
|
+
// 2. Generate new scenes with the same character (Structured Mode)
|
|
107
108
|
const scene = await service.generate({
|
|
108
109
|
prompt: 'dancing happily in the rain, jumping in puddles',
|
|
109
110
|
model: 'gemini-flash-image',
|
|
@@ -113,6 +114,17 @@ const scene = await service.generate({
|
|
|
113
114
|
}],
|
|
114
115
|
subjectDescription: 'cute cartoon bear with red hat and blue scarf',
|
|
115
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
|
+
});
|
|
116
128
|
```
|
|
117
129
|
|
|
118
130
|
**Important:** Character consistency is only supported by `gemini-flash-image` model!
|
|
@@ -225,7 +237,11 @@ IONOS_API_URL=https://api.ionos.cloud/ai/v1
|
|
|
225
237
|
|
|
226
238
|
## Character Consistency
|
|
227
239
|
|
|
228
|
-
Generate consistent characters across multiple images - perfect for children's book illustrations
|
|
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:
|
|
229
245
|
|
|
230
246
|
```typescript
|
|
231
247
|
// Step 1: Create a character
|
|
@@ -242,15 +258,47 @@ for (const scene of scenes) {
|
|
|
242
258
|
prompt: scene,
|
|
243
259
|
model: 'gemini-flash-image',
|
|
244
260
|
referenceImages: [{ base64: bear.images[0].base64!, mimeType: 'image/png' }],
|
|
245
|
-
subjectDescription: 'cute cartoon bear with red hat',
|
|
261
|
+
subjectDescription: 'cute cartoon bear with red hat', // Required in structured mode
|
|
246
262
|
});
|
|
247
263
|
// Save result...
|
|
248
264
|
}
|
|
249
265
|
```
|
|
250
266
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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)
|
|
254
302
|
|
|
255
303
|
## GDPR / Compliance
|
|
256
304
|
|
|
@@ -405,6 +453,42 @@ Available levels: `debug`, `info`, `warn`, `error`, `silent`
|
|
|
405
453
|
|
|
406
454
|
</details>
|
|
407
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
|
+
|
|
408
492
|
<details>
|
|
409
493
|
<summary><strong>Error Handling</strong></summary>
|
|
410
494
|
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Placeholder image for dry mode responses.
|
|
3
|
+
* A white 1024x1024 PNG image encoded as base64.
|
|
4
|
+
*/
|
|
5
|
+
export declare const DRY_MODE_PLACEHOLDER_IMAGE = "iVBORw0KGgoAAAANSUhEUgAABAAAAAQACAYAAAB/HSuDAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAABkaSURBVHhe7dgxAQAwDICw+je9/ZVQkhMJzAMAAADOmx0AAACAewwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIOADWOa8bmfAA9cAAAAASUVORK5CYII=";
|
|
6
|
+
export declare const DRY_MODE_PLACEHOLDER_MIME_TYPE = "image/png";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Placeholder image for dry mode responses.
|
|
4
|
+
* A white 1024x1024 PNG image encoded as base64.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.DRY_MODE_PLACEHOLDER_MIME_TYPE = exports.DRY_MODE_PLACEHOLDER_IMAGE = void 0;
|
|
8
|
+
exports.DRY_MODE_PLACEHOLDER_IMAGE = 'iVBORw0KGgoAAAANSUhEUgAABAAAAAQACAYAAAB/HSuDAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAABkaSURBVHhe7dgxAQAwDICw+je9/ZVQkhMJzAMAAADOmx0AAACAewwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIMAAAAAAgAADAAAAAAIMAAAAAAgwAAAAACDAAAAAAIAAAwAAAAACDAAAAAAIMAAAAAAgwAAAAACAAAMAAAAAAgwAAAAACDAAAAAAIOADWOa8bmfAA9cAAAAASUVORK5CYII=';
|
|
9
|
+
exports.DRY_MODE_PLACEHOLDER_MIME_TYPE = 'image/png';
|
|
@@ -16,3 +16,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./tti.service"), exports);
|
|
18
18
|
__exportStar(require("./providers"), exports);
|
|
19
|
+
__exportStar(require("./utils"), exports);
|
|
@@ -44,8 +44,34 @@ export declare abstract class BaseTTIProvider implements ITTIProvider {
|
|
|
44
44
|
abstract getDisplayName(): string;
|
|
45
45
|
abstract listModels(): ModelInfo[];
|
|
46
46
|
abstract getDefaultModel(): string;
|
|
47
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Provider-specific generation implementation.
|
|
49
|
+
* Called by generate() after validation and dry mode checks.
|
|
50
|
+
* Subclasses should implement the actual API call logic here.
|
|
51
|
+
*/
|
|
52
|
+
protected abstract doGenerate(request: TTIRequest): Promise<TTIResponse>;
|
|
48
53
|
getName(): TTIProvider;
|
|
54
|
+
/**
|
|
55
|
+
* Generate images from a request.
|
|
56
|
+
* This is the main entry point that handles:
|
|
57
|
+
* - Request validation
|
|
58
|
+
* - Dry mode (skip API call, return mock response with logging)
|
|
59
|
+
* - Delegation to provider-specific doGenerate()
|
|
60
|
+
*
|
|
61
|
+
* Note: Normal mode logging is handled by each provider in doGenerate()
|
|
62
|
+
* to support provider-specific metadata (e.g., region for Google Cloud).
|
|
63
|
+
*/
|
|
64
|
+
generate(request: TTIRequest): Promise<TTIResponse>;
|
|
65
|
+
/**
|
|
66
|
+
* Handle dry mode: log request and return mock response without API call.
|
|
67
|
+
* Useful for development and debugging without incurring API costs.
|
|
68
|
+
*/
|
|
69
|
+
protected handleDryMode(request: TTIRequest): Promise<TTIResponse>;
|
|
70
|
+
/**
|
|
71
|
+
* Create a mock response for dry mode.
|
|
72
|
+
* Returns placeholder images (white 1024x1024 PNG) with metadata.
|
|
73
|
+
*/
|
|
74
|
+
protected createDryModeResponse(request: TTIRequest, modelId: string): TTIResponse;
|
|
49
75
|
/**
|
|
50
76
|
* Get model info by ID
|
|
51
77
|
*/
|
|
@@ -12,6 +12,8 @@ exports.getLogLevel = getLogLevel;
|
|
|
12
12
|
exports.hasReferenceImages = hasReferenceImages;
|
|
13
13
|
exports.isEURegion = isEURegion;
|
|
14
14
|
const types_1 = require("../../../types");
|
|
15
|
+
const debug_tti_utils_1 = require("../utils/debug-tti.utils");
|
|
16
|
+
const placeholder_image_1 = require("../assets/placeholder-image");
|
|
15
17
|
// ============================================================
|
|
16
18
|
// ERROR CLASSES
|
|
17
19
|
// ============================================================
|
|
@@ -103,6 +105,74 @@ class BaseTTIProvider {
|
|
|
103
105
|
getName() {
|
|
104
106
|
return this.providerName;
|
|
105
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Generate images from a request.
|
|
110
|
+
* This is the main entry point that handles:
|
|
111
|
+
* - Request validation
|
|
112
|
+
* - Dry mode (skip API call, return mock response with logging)
|
|
113
|
+
* - Delegation to provider-specific doGenerate()
|
|
114
|
+
*
|
|
115
|
+
* Note: Normal mode logging is handled by each provider in doGenerate()
|
|
116
|
+
* to support provider-specific metadata (e.g., region for Google Cloud).
|
|
117
|
+
*/
|
|
118
|
+
async generate(request) {
|
|
119
|
+
// 1. Validate the request
|
|
120
|
+
this.validateRequest(request);
|
|
121
|
+
// 2. Handle dry mode - skip API call, return mock response
|
|
122
|
+
if (request.dry) {
|
|
123
|
+
return this.handleDryMode(request);
|
|
124
|
+
}
|
|
125
|
+
// 3. Execute actual generation via provider-specific implementation
|
|
126
|
+
// Provider handles its own logging with provider-specific metadata
|
|
127
|
+
return this.doGenerate(request);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Handle dry mode: log request and return mock response without API call.
|
|
131
|
+
* Useful for development and debugging without incurring API costs.
|
|
132
|
+
*/
|
|
133
|
+
async handleDryMode(request) {
|
|
134
|
+
const modelId = request.model || this.getDefaultModel();
|
|
135
|
+
this.log('info', 'Dry mode enabled - skipping API call', {
|
|
136
|
+
model: modelId,
|
|
137
|
+
provider: this.providerName,
|
|
138
|
+
});
|
|
139
|
+
// Create debug info for logging (if enabled)
|
|
140
|
+
let debugInfo = null;
|
|
141
|
+
if (debug_tti_utils_1.TTIDebugger.isEnabled) {
|
|
142
|
+
debugInfo = debug_tti_utils_1.TTIDebugger.createDebugInfo(request, this.providerName, modelId);
|
|
143
|
+
await debug_tti_utils_1.TTIDebugger.logRequest(debugInfo);
|
|
144
|
+
}
|
|
145
|
+
const dryResponse = this.createDryModeResponse(request, modelId);
|
|
146
|
+
if (debugInfo) {
|
|
147
|
+
debugInfo = debug_tti_utils_1.TTIDebugger.updateWithResponse(debugInfo, dryResponse);
|
|
148
|
+
await debug_tti_utils_1.TTIDebugger.logResponse(debugInfo);
|
|
149
|
+
}
|
|
150
|
+
return dryResponse;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Create a mock response for dry mode.
|
|
154
|
+
* Returns placeholder images (white 1024x1024 PNG) with metadata.
|
|
155
|
+
*/
|
|
156
|
+
createDryModeResponse(request, modelId) {
|
|
157
|
+
const imageCount = request.n || 1;
|
|
158
|
+
// Generate the requested number of placeholder images
|
|
159
|
+
const images = Array.from({ length: imageCount }, () => ({
|
|
160
|
+
base64: placeholder_image_1.DRY_MODE_PLACEHOLDER_IMAGE,
|
|
161
|
+
contentType: placeholder_image_1.DRY_MODE_PLACEHOLDER_MIME_TYPE,
|
|
162
|
+
}));
|
|
163
|
+
return {
|
|
164
|
+
images,
|
|
165
|
+
metadata: {
|
|
166
|
+
provider: this.providerName,
|
|
167
|
+
model: modelId,
|
|
168
|
+
duration: 0,
|
|
169
|
+
},
|
|
170
|
+
usage: {
|
|
171
|
+
imagesGenerated: imageCount,
|
|
172
|
+
modelId: modelId,
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
106
176
|
/**
|
|
107
177
|
* Get model info by ID
|
|
108
178
|
*/
|
|
@@ -133,9 +203,14 @@ class BaseTTIProvider {
|
|
|
133
203
|
throw new CapabilityNotSupportedError(this.providerName, 'characterConsistency', modelId);
|
|
134
204
|
}
|
|
135
205
|
// Validate subject description is provided
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
206
|
+
// RELAXED: We now allow missing subjectDescription to support raw multimodal prompting
|
|
207
|
+
// where the user references images directly in the prompt (e.g., "Image 1 is X...").
|
|
208
|
+
// if (!request.subjectDescription || request.subjectDescription.trim().length === 0) {
|
|
209
|
+
// throw new InvalidConfigError(
|
|
210
|
+
// this.providerName,
|
|
211
|
+
// 'subjectDescription is required when using referenceImages'
|
|
212
|
+
// );
|
|
213
|
+
// }
|
|
139
214
|
// Validate reference images have data
|
|
140
215
|
for (let i = 0; i < request.referenceImages.length; i++) {
|
|
141
216
|
const ref = request.referenceImages[i];
|
|
@@ -20,7 +20,7 @@ export declare class EdenAIProvider extends BaseTTIProvider {
|
|
|
20
20
|
getDisplayName(): string;
|
|
21
21
|
listModels(): ModelInfo[];
|
|
22
22
|
getDefaultModel(): string;
|
|
23
|
-
|
|
23
|
+
protected doGenerate(request: TTIRequest): Promise<TTIResponse>;
|
|
24
24
|
private executeGeneration;
|
|
25
25
|
private aspectRatioToSize;
|
|
26
26
|
private processResponse;
|
|
@@ -79,8 +79,8 @@ class EdenAIProvider extends base_tti_provider_1.BaseTTIProvider {
|
|
|
79
79
|
getDefaultModel() {
|
|
80
80
|
return 'openai';
|
|
81
81
|
}
|
|
82
|
-
async
|
|
83
|
-
|
|
82
|
+
async doGenerate(request) {
|
|
83
|
+
// Note: validateRequest() is called by BaseTTIProvider.generate()
|
|
84
84
|
return this.executeWithRetry(request, () => this.executeGeneration(request), 'EdenAI API call');
|
|
85
85
|
}
|
|
86
86
|
async executeGeneration(request) {
|
|
@@ -36,7 +36,7 @@ export declare class GoogleCloudTTIProvider extends BaseTTIProvider {
|
|
|
36
36
|
getDisplayName(): string;
|
|
37
37
|
listModels(): ModelInfo[];
|
|
38
38
|
getDefaultModel(): string;
|
|
39
|
-
|
|
39
|
+
protected doGenerate(request: TTIRequest): Promise<TTIResponse>;
|
|
40
40
|
/**
|
|
41
41
|
* Get the configured region
|
|
42
42
|
*/
|
|
@@ -49,6 +49,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
49
49
|
exports.GoogleCloudTTIProvider = void 0;
|
|
50
50
|
const types_1 = require("../../../types");
|
|
51
51
|
const base_tti_provider_1 = require("./base-tti-provider");
|
|
52
|
+
const debug_tti_utils_1 = require("../utils/debug-tti.utils");
|
|
52
53
|
// ============================================================
|
|
53
54
|
// MODEL DEFINITIONS
|
|
54
55
|
// ============================================================
|
|
@@ -146,8 +147,8 @@ class GoogleCloudTTIProvider extends base_tti_provider_1.BaseTTIProvider {
|
|
|
146
147
|
// Default to Gemini Flash Image as it supports character consistency
|
|
147
148
|
return 'gemini-flash-image';
|
|
148
149
|
}
|
|
149
|
-
async
|
|
150
|
-
|
|
150
|
+
async doGenerate(request) {
|
|
151
|
+
// Note: validateRequest() is called by BaseTTIProvider.generate()
|
|
151
152
|
const modelId = request.model || this.getDefaultModel();
|
|
152
153
|
const modelInfo = this.getModelInfo(modelId);
|
|
153
154
|
if (!modelInfo) {
|
|
@@ -157,19 +158,44 @@ class GoogleCloudTTIProvider extends base_tti_provider_1.BaseTTIProvider {
|
|
|
157
158
|
}
|
|
158
159
|
// Validate region availability
|
|
159
160
|
const effectiveRegion = this.getEffectiveRegion(modelId);
|
|
161
|
+
// Create debug info for logging
|
|
162
|
+
let debugInfo = null;
|
|
163
|
+
if (debug_tti_utils_1.TTIDebugger.isEnabled) {
|
|
164
|
+
debugInfo = debug_tti_utils_1.TTIDebugger.createDebugInfo(request, this.providerName, modelId, { region: effectiveRegion });
|
|
165
|
+
await debug_tti_utils_1.TTIDebugger.logRequest(debugInfo);
|
|
166
|
+
}
|
|
160
167
|
this.log('debug', 'Generating image', {
|
|
161
168
|
model: modelId,
|
|
162
169
|
region: effectiveRegion,
|
|
163
170
|
hasReferenceImages: (0, base_tti_provider_1.hasReferenceImages)(request),
|
|
164
171
|
});
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
172
|
+
try {
|
|
173
|
+
// Route to appropriate implementation with retry support
|
|
174
|
+
let response;
|
|
175
|
+
switch (modelId) {
|
|
176
|
+
case 'imagen-3':
|
|
177
|
+
response = await this.executeWithRetry(request, () => this.generateWithImagen(request, effectiveRegion), 'Imagen API call');
|
|
178
|
+
break;
|
|
179
|
+
case 'gemini-flash-image':
|
|
180
|
+
response = await this.executeWithRetry(request, () => this.generateWithGemini(request, effectiveRegion), 'Gemini API call');
|
|
181
|
+
break;
|
|
182
|
+
default:
|
|
183
|
+
throw new base_tti_provider_1.InvalidConfigError(this.providerName, `Unknown model: ${modelId}`);
|
|
184
|
+
}
|
|
185
|
+
// Log successful response
|
|
186
|
+
if (debugInfo) {
|
|
187
|
+
debugInfo = debug_tti_utils_1.TTIDebugger.updateWithResponse(debugInfo, response);
|
|
188
|
+
await debug_tti_utils_1.TTIDebugger.logResponse(debugInfo);
|
|
189
|
+
}
|
|
190
|
+
return response;
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
// Log error
|
|
194
|
+
if (debugInfo) {
|
|
195
|
+
debugInfo = debug_tti_utils_1.TTIDebugger.updateWithError(debugInfo, error);
|
|
196
|
+
await debug_tti_utils_1.TTIDebugger.logError(debugInfo);
|
|
197
|
+
}
|
|
198
|
+
throw error;
|
|
173
199
|
}
|
|
174
200
|
}
|
|
175
201
|
// ============================================================
|
|
@@ -347,9 +373,16 @@ class GoogleCloudTTIProvider extends base_tti_provider_1.BaseTTIProvider {
|
|
|
347
373
|
},
|
|
348
374
|
});
|
|
349
375
|
}
|
|
350
|
-
// Build character consistency prompt
|
|
351
|
-
|
|
352
|
-
|
|
376
|
+
// Build character consistency prompt if subject description is provided
|
|
377
|
+
if (request.subjectDescription) {
|
|
378
|
+
const fullPrompt = this.buildCharacterConsistencyPrompt(request.prompt, request.subjectDescription, request.referenceImages.length);
|
|
379
|
+
parts.push({ text: fullPrompt });
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
// No subject description - treat as raw multimodal prompt
|
|
383
|
+
// This allows "image 1", "image 2" style prompting
|
|
384
|
+
parts.push({ text: request.prompt });
|
|
385
|
+
}
|
|
353
386
|
}
|
|
354
387
|
else {
|
|
355
388
|
parts.push({ text: request.prompt });
|
|
@@ -416,9 +449,9 @@ class GoogleCloudTTIProvider extends base_tti_provider_1.BaseTTIProvider {
|
|
|
416
449
|
}
|
|
417
450
|
buildCharacterConsistencyPrompt(userPrompt, subjectDescription, referenceCount) {
|
|
418
451
|
const referenceText = referenceCount === 1 ? 'the reference image' : `the ${referenceCount} reference images`;
|
|
419
|
-
return `Using ${referenceText} as a reference for the
|
|
452
|
+
return `Using ${referenceText} as a reference for the subject "${subjectDescription}", generate a new image where: ${userPrompt}
|
|
420
453
|
|
|
421
|
-
IMPORTANT: Maintain exact visual consistency with the
|
|
454
|
+
IMPORTANT: Maintain exact visual consistency with the subject in the reference - same style, colors, proportions, and distinctive features. The subject should be immediately recognizable as the same one from the reference.`;
|
|
422
455
|
}
|
|
423
456
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
424
457
|
processGeminiResponse(response, duration) {
|
|
@@ -18,7 +18,7 @@ export declare class IonosProvider extends BaseTTIProvider {
|
|
|
18
18
|
getDisplayName(): string;
|
|
19
19
|
listModels(): ModelInfo[];
|
|
20
20
|
getDefaultModel(): string;
|
|
21
|
-
|
|
21
|
+
protected doGenerate(request: TTIRequest): Promise<TTIResponse>;
|
|
22
22
|
private executeGeneration;
|
|
23
23
|
private aspectRatioToSize;
|
|
24
24
|
private processResponse;
|
|
@@ -55,8 +55,8 @@ class IonosProvider extends base_tti_provider_1.BaseTTIProvider {
|
|
|
55
55
|
getDefaultModel() {
|
|
56
56
|
return 'default';
|
|
57
57
|
}
|
|
58
|
-
async
|
|
59
|
-
|
|
58
|
+
async doGenerate(request) {
|
|
59
|
+
// Note: validateRequest() is called by BaseTTIProvider.generate()
|
|
60
60
|
return this.executeWithRetry(request, () => this.executeGeneration(request), 'IONOS API call');
|
|
61
61
|
}
|
|
62
62
|
async executeGeneration(request) {
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TTI Debugger Utility
|
|
3
|
+
*
|
|
4
|
+
* Provides markdown-based logging for TTI requests, similar to LLM middleware.
|
|
5
|
+
* Logs prompts, subject descriptions, reference image metadata, and responses.
|
|
6
|
+
*
|
|
7
|
+
* Enable via:
|
|
8
|
+
* - Environment variable: DEBUG_TTI_REQUESTS=true
|
|
9
|
+
* - Or programmatically: TTIDebugger.setEnabled(true)
|
|
10
|
+
*
|
|
11
|
+
* Configure log directory:
|
|
12
|
+
* - Environment variable: TTI_DEBUG_LOG_DIR=/path/to/logs
|
|
13
|
+
* - Or programmatically: TTIDebugger.setLogsDir('/path/to/logs')
|
|
14
|
+
* - Default: process.cwd()/logs/tti/requests/
|
|
15
|
+
*/
|
|
16
|
+
import { TTIRequest, TTIResponse } from '../../../types';
|
|
17
|
+
/**
|
|
18
|
+
* Debug information for a TTI request
|
|
19
|
+
*/
|
|
20
|
+
export interface TTIDebugInfo {
|
|
21
|
+
requestTimestamp: Date;
|
|
22
|
+
responseTimestamp?: Date;
|
|
23
|
+
provider: string;
|
|
24
|
+
model: string;
|
|
25
|
+
region?: string;
|
|
26
|
+
prompt: string;
|
|
27
|
+
subjectDescription?: string;
|
|
28
|
+
referenceImageCount: number;
|
|
29
|
+
referenceImageMimeTypes?: string[];
|
|
30
|
+
aspectRatio?: string;
|
|
31
|
+
providerOptions?: Record<string, unknown>;
|
|
32
|
+
useCase?: string;
|
|
33
|
+
sessionId?: string;
|
|
34
|
+
bookId?: string;
|
|
35
|
+
characterId?: string;
|
|
36
|
+
sectionId?: string;
|
|
37
|
+
response?: {
|
|
38
|
+
imageCount: number;
|
|
39
|
+
imageMimeTypes: string[];
|
|
40
|
+
duration: number;
|
|
41
|
+
};
|
|
42
|
+
rawRequest?: TTIRequest;
|
|
43
|
+
rawResponse?: TTIResponse;
|
|
44
|
+
error?: {
|
|
45
|
+
message: string;
|
|
46
|
+
code?: string;
|
|
47
|
+
details?: unknown;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Configuration options for TTIDebugger
|
|
52
|
+
*/
|
|
53
|
+
export interface TTIDebuggerConfig {
|
|
54
|
+
/** Enable/disable logging (default: from DEBUG_TTI_REQUESTS env) */
|
|
55
|
+
enabled?: boolean;
|
|
56
|
+
/** Directory for log files (default: process.cwd()/logs/tti/requests) */
|
|
57
|
+
logsDir?: string;
|
|
58
|
+
/** Include raw base64 image data in logs (default: false - too large) */
|
|
59
|
+
includeBase64?: boolean;
|
|
60
|
+
/** Log to console as well (default: false) */
|
|
61
|
+
consoleLog?: boolean;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Static debugger class for TTI request/response logging
|
|
65
|
+
*/
|
|
66
|
+
export declare class TTIDebugger {
|
|
67
|
+
private static _enabled;
|
|
68
|
+
private static _logsDir;
|
|
69
|
+
private static _includeBase64;
|
|
70
|
+
private static _consoleLog;
|
|
71
|
+
/**
|
|
72
|
+
* Check if debugging is enabled
|
|
73
|
+
*/
|
|
74
|
+
static get isEnabled(): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Enable or disable debugging
|
|
77
|
+
*/
|
|
78
|
+
static setEnabled(enabled: boolean): void;
|
|
79
|
+
/**
|
|
80
|
+
* Get the current logs directory
|
|
81
|
+
*/
|
|
82
|
+
static getLogsDir(): string;
|
|
83
|
+
/**
|
|
84
|
+
* Set the logs directory
|
|
85
|
+
*/
|
|
86
|
+
static setLogsDir(dir: string): void;
|
|
87
|
+
/**
|
|
88
|
+
* Configure the debugger
|
|
89
|
+
*/
|
|
90
|
+
static configure(config: TTIDebuggerConfig): void;
|
|
91
|
+
/**
|
|
92
|
+
* Log a TTI request (call before generation)
|
|
93
|
+
*/
|
|
94
|
+
static logRequest(debugInfo: TTIDebugInfo): Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* Log a TTI response (call after generation)
|
|
97
|
+
*/
|
|
98
|
+
static logResponse(debugInfo: TTIDebugInfo): Promise<void>;
|
|
99
|
+
/**
|
|
100
|
+
* Log an error
|
|
101
|
+
*/
|
|
102
|
+
static logError(debugInfo: TTIDebugInfo): Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* Save debug info to a markdown file
|
|
105
|
+
*/
|
|
106
|
+
static saveToMarkdown(debugInfo: TTIDebugInfo): Promise<string>;
|
|
107
|
+
/**
|
|
108
|
+
* Generate a filename for the log file
|
|
109
|
+
*/
|
|
110
|
+
private static generateFilename;
|
|
111
|
+
/**
|
|
112
|
+
* Format debug info as markdown
|
|
113
|
+
*/
|
|
114
|
+
private static formatMarkdown;
|
|
115
|
+
/**
|
|
116
|
+
* Sanitize request by removing/truncating base64 data
|
|
117
|
+
*/
|
|
118
|
+
private static sanitizeRequest;
|
|
119
|
+
/**
|
|
120
|
+
* Sanitize response by removing/truncating base64 data
|
|
121
|
+
*/
|
|
122
|
+
private static sanitizeResponse;
|
|
123
|
+
/**
|
|
124
|
+
* Ensure the logs directory exists
|
|
125
|
+
*/
|
|
126
|
+
private static ensureLogsDirectory;
|
|
127
|
+
/**
|
|
128
|
+
* Create debug info from a request (helper for providers)
|
|
129
|
+
*/
|
|
130
|
+
static createDebugInfo(request: TTIRequest, provider: string, model: string, options?: {
|
|
131
|
+
region?: string;
|
|
132
|
+
useCase?: string;
|
|
133
|
+
sessionId?: string;
|
|
134
|
+
bookId?: string;
|
|
135
|
+
characterId?: string;
|
|
136
|
+
sectionId?: string;
|
|
137
|
+
}): TTIDebugInfo;
|
|
138
|
+
/**
|
|
139
|
+
* Update debug info with response data (helper for providers)
|
|
140
|
+
*/
|
|
141
|
+
static updateWithResponse(debugInfo: TTIDebugInfo, response: TTIResponse): TTIDebugInfo;
|
|
142
|
+
/**
|
|
143
|
+
* Update debug info with error data (helper for providers)
|
|
144
|
+
*/
|
|
145
|
+
static updateWithError(debugInfo: TTIDebugInfo, error: Error & {
|
|
146
|
+
code?: string;
|
|
147
|
+
cause?: unknown;
|
|
148
|
+
}): TTIDebugInfo;
|
|
149
|
+
}
|
|
150
|
+
export default TTIDebugger;
|
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* TTI Debugger Utility
|
|
4
|
+
*
|
|
5
|
+
* Provides markdown-based logging for TTI requests, similar to LLM middleware.
|
|
6
|
+
* Logs prompts, subject descriptions, reference image metadata, and responses.
|
|
7
|
+
*
|
|
8
|
+
* Enable via:
|
|
9
|
+
* - Environment variable: DEBUG_TTI_REQUESTS=true
|
|
10
|
+
* - Or programmatically: TTIDebugger.setEnabled(true)
|
|
11
|
+
*
|
|
12
|
+
* Configure log directory:
|
|
13
|
+
* - Environment variable: TTI_DEBUG_LOG_DIR=/path/to/logs
|
|
14
|
+
* - Or programmatically: TTIDebugger.setLogsDir('/path/to/logs')
|
|
15
|
+
* - Default: process.cwd()/logs/tti/requests/
|
|
16
|
+
*/
|
|
17
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
20
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
21
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
22
|
+
}
|
|
23
|
+
Object.defineProperty(o, k2, desc);
|
|
24
|
+
}) : (function(o, m, k, k2) {
|
|
25
|
+
if (k2 === undefined) k2 = k;
|
|
26
|
+
o[k2] = m[k];
|
|
27
|
+
}));
|
|
28
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
29
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
30
|
+
}) : function(o, v) {
|
|
31
|
+
o["default"] = v;
|
|
32
|
+
});
|
|
33
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
34
|
+
var ownKeys = function(o) {
|
|
35
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
36
|
+
var ar = [];
|
|
37
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
38
|
+
return ar;
|
|
39
|
+
};
|
|
40
|
+
return ownKeys(o);
|
|
41
|
+
};
|
|
42
|
+
return function (mod) {
|
|
43
|
+
if (mod && mod.__esModule) return mod;
|
|
44
|
+
var result = {};
|
|
45
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
46
|
+
__setModuleDefault(result, mod);
|
|
47
|
+
return result;
|
|
48
|
+
};
|
|
49
|
+
})();
|
|
50
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
|
+
exports.TTIDebugger = void 0;
|
|
52
|
+
const fs = __importStar(require("fs"));
|
|
53
|
+
const path = __importStar(require("path"));
|
|
54
|
+
// ============================================================
|
|
55
|
+
// DEBUGGER CLASS
|
|
56
|
+
// ============================================================
|
|
57
|
+
/**
|
|
58
|
+
* Static debugger class for TTI request/response logging
|
|
59
|
+
*/
|
|
60
|
+
class TTIDebugger {
|
|
61
|
+
// ============================================================
|
|
62
|
+
// CONFIGURATION
|
|
63
|
+
// ============================================================
|
|
64
|
+
/**
|
|
65
|
+
* Check if debugging is enabled
|
|
66
|
+
*/
|
|
67
|
+
static get isEnabled() {
|
|
68
|
+
return this._enabled;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Enable or disable debugging
|
|
72
|
+
*/
|
|
73
|
+
static setEnabled(enabled) {
|
|
74
|
+
this._enabled = enabled;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get the current logs directory
|
|
78
|
+
*/
|
|
79
|
+
static getLogsDir() {
|
|
80
|
+
return this._logsDir;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Set the logs directory
|
|
84
|
+
*/
|
|
85
|
+
static setLogsDir(dir) {
|
|
86
|
+
this._logsDir = dir;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Configure the debugger
|
|
90
|
+
*/
|
|
91
|
+
static configure(config) {
|
|
92
|
+
if (config.enabled !== undefined) {
|
|
93
|
+
this._enabled = config.enabled;
|
|
94
|
+
}
|
|
95
|
+
if (config.logsDir !== undefined) {
|
|
96
|
+
this._logsDir = config.logsDir;
|
|
97
|
+
}
|
|
98
|
+
if (config.includeBase64 !== undefined) {
|
|
99
|
+
this._includeBase64 = config.includeBase64;
|
|
100
|
+
}
|
|
101
|
+
if (config.consoleLog !== undefined) {
|
|
102
|
+
this._consoleLog = config.consoleLog;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// ============================================================
|
|
106
|
+
// LOGGING METHODS
|
|
107
|
+
// ============================================================
|
|
108
|
+
/**
|
|
109
|
+
* Log a TTI request (call before generation)
|
|
110
|
+
*/
|
|
111
|
+
static async logRequest(debugInfo) {
|
|
112
|
+
if (!this._enabled)
|
|
113
|
+
return;
|
|
114
|
+
if (this._consoleLog) {
|
|
115
|
+
console.log('[TTI Debug] Request:', {
|
|
116
|
+
provider: debugInfo.provider,
|
|
117
|
+
model: debugInfo.model,
|
|
118
|
+
useCase: debugInfo.useCase,
|
|
119
|
+
prompt: debugInfo.prompt.substring(0, 100) + '...',
|
|
120
|
+
subjectDescription: debugInfo.subjectDescription,
|
|
121
|
+
referenceImageCount: debugInfo.referenceImageCount,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Log a TTI response (call after generation)
|
|
127
|
+
*/
|
|
128
|
+
static async logResponse(debugInfo) {
|
|
129
|
+
if (!this._enabled)
|
|
130
|
+
return;
|
|
131
|
+
try {
|
|
132
|
+
await this.saveToMarkdown(debugInfo);
|
|
133
|
+
if (this._consoleLog) {
|
|
134
|
+
console.log('[TTI Debug] Response saved:', {
|
|
135
|
+
provider: debugInfo.provider,
|
|
136
|
+
model: debugInfo.model,
|
|
137
|
+
duration: debugInfo.response?.duration,
|
|
138
|
+
imageCount: debugInfo.response?.imageCount,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
console.error('[TTI Debug] Failed to save log:', error);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Log an error
|
|
148
|
+
*/
|
|
149
|
+
static async logError(debugInfo) {
|
|
150
|
+
if (!this._enabled)
|
|
151
|
+
return;
|
|
152
|
+
try {
|
|
153
|
+
await this.saveToMarkdown(debugInfo);
|
|
154
|
+
if (this._consoleLog) {
|
|
155
|
+
console.error('[TTI Debug] Error:', debugInfo.error);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
console.error('[TTI Debug] Failed to save error log:', error);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// ============================================================
|
|
163
|
+
// MARKDOWN GENERATION
|
|
164
|
+
// ============================================================
|
|
165
|
+
/**
|
|
166
|
+
* Save debug info to a markdown file
|
|
167
|
+
*/
|
|
168
|
+
static async saveToMarkdown(debugInfo) {
|
|
169
|
+
this.ensureLogsDirectory();
|
|
170
|
+
const filename = this.generateFilename(debugInfo);
|
|
171
|
+
const filepath = path.join(this._logsDir, filename);
|
|
172
|
+
const content = this.formatMarkdown(debugInfo);
|
|
173
|
+
await fs.promises.writeFile(filepath, content, 'utf-8');
|
|
174
|
+
return filepath;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Generate a filename for the log file
|
|
178
|
+
*/
|
|
179
|
+
static generateFilename(debugInfo) {
|
|
180
|
+
const timestamp = debugInfo.requestTimestamp
|
|
181
|
+
.toISOString()
|
|
182
|
+
.replace(/[:.]/g, '-');
|
|
183
|
+
const useCasePart = debugInfo.useCase
|
|
184
|
+
? `_${debugInfo.useCase.toLowerCase().replace(/[^a-z0-9]/g, '-')}`
|
|
185
|
+
: '';
|
|
186
|
+
const identifierPart = debugInfo.characterId
|
|
187
|
+
? `_char-${debugInfo.characterId.substring(0, 8)}`
|
|
188
|
+
: debugInfo.sectionId
|
|
189
|
+
? `_sec-${debugInfo.sectionId.substring(0, 8)}`
|
|
190
|
+
: '';
|
|
191
|
+
return `${timestamp}${useCasePart}${identifierPart}.md`;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Format debug info as markdown
|
|
195
|
+
*/
|
|
196
|
+
static formatMarkdown(debugInfo) {
|
|
197
|
+
const sections = [];
|
|
198
|
+
// Header
|
|
199
|
+
sections.push('# TTI Request & Response Log\n');
|
|
200
|
+
// Provider Information
|
|
201
|
+
sections.push('## Provider Information\n');
|
|
202
|
+
sections.push(`- **Provider**: ${debugInfo.provider}`);
|
|
203
|
+
sections.push(`- **Model**: ${debugInfo.model}`);
|
|
204
|
+
if (debugInfo.region) {
|
|
205
|
+
sections.push(`- **Region**: ${debugInfo.region}`);
|
|
206
|
+
}
|
|
207
|
+
sections.push('');
|
|
208
|
+
// Request Information
|
|
209
|
+
sections.push('## Request Information\n');
|
|
210
|
+
sections.push(`- **Request Timestamp**: ${debugInfo.requestTimestamp.toISOString()}`);
|
|
211
|
+
if (debugInfo.responseTimestamp) {
|
|
212
|
+
sections.push(`- **Response Timestamp**: ${debugInfo.responseTimestamp.toISOString()}`);
|
|
213
|
+
}
|
|
214
|
+
if (debugInfo.useCase) {
|
|
215
|
+
sections.push(`- **Use Case**: ${debugInfo.useCase}`);
|
|
216
|
+
}
|
|
217
|
+
if (debugInfo.sessionId) {
|
|
218
|
+
sections.push(`- **Session ID**: ${debugInfo.sessionId}`);
|
|
219
|
+
}
|
|
220
|
+
if (debugInfo.bookId) {
|
|
221
|
+
sections.push(`- **Book ID**: ${debugInfo.bookId}`);
|
|
222
|
+
}
|
|
223
|
+
if (debugInfo.characterId) {
|
|
224
|
+
sections.push(`- **Character ID**: ${debugInfo.characterId}`);
|
|
225
|
+
}
|
|
226
|
+
if (debugInfo.sectionId) {
|
|
227
|
+
sections.push(`- **Section ID**: ${debugInfo.sectionId}`);
|
|
228
|
+
}
|
|
229
|
+
if (debugInfo.aspectRatio) {
|
|
230
|
+
sections.push(`- **Aspect Ratio**: ${debugInfo.aspectRatio}`);
|
|
231
|
+
}
|
|
232
|
+
sections.push('');
|
|
233
|
+
// Reference Images
|
|
234
|
+
sections.push('## Reference Images\n');
|
|
235
|
+
sections.push(`- **Count**: ${debugInfo.referenceImageCount}`);
|
|
236
|
+
if (debugInfo.referenceImageMimeTypes &&
|
|
237
|
+
debugInfo.referenceImageMimeTypes.length > 0) {
|
|
238
|
+
sections.push(`- **MIME Types**: ${debugInfo.referenceImageMimeTypes.join(', ')}`);
|
|
239
|
+
}
|
|
240
|
+
sections.push('');
|
|
241
|
+
// Subject Description (important for character consistency debugging)
|
|
242
|
+
sections.push('## Subject Description\n');
|
|
243
|
+
if (debugInfo.subjectDescription) {
|
|
244
|
+
sections.push('```');
|
|
245
|
+
sections.push(debugInfo.subjectDescription);
|
|
246
|
+
sections.push('```');
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
sections.push('*No subject description provided (raw multimodal mode)*');
|
|
250
|
+
}
|
|
251
|
+
sections.push('');
|
|
252
|
+
// Prompt (the most important part for debugging)
|
|
253
|
+
sections.push('## Prompt\n');
|
|
254
|
+
sections.push('```');
|
|
255
|
+
sections.push(debugInfo.prompt);
|
|
256
|
+
sections.push('```');
|
|
257
|
+
sections.push('');
|
|
258
|
+
// Provider Options
|
|
259
|
+
if (debugInfo.providerOptions &&
|
|
260
|
+
Object.keys(debugInfo.providerOptions).length > 0) {
|
|
261
|
+
sections.push('## Provider Options\n');
|
|
262
|
+
sections.push('```json');
|
|
263
|
+
sections.push(JSON.stringify(debugInfo.providerOptions, null, 2));
|
|
264
|
+
sections.push('```');
|
|
265
|
+
sections.push('');
|
|
266
|
+
}
|
|
267
|
+
// Response
|
|
268
|
+
if (debugInfo.response) {
|
|
269
|
+
sections.push('## Response\n');
|
|
270
|
+
sections.push(`- **Image Count**: ${debugInfo.response.imageCount}`);
|
|
271
|
+
sections.push(`- **MIME Types**: ${debugInfo.response.imageMimeTypes.join(', ')}`);
|
|
272
|
+
sections.push(`- **Duration**: ${debugInfo.response.duration}ms`);
|
|
273
|
+
sections.push('');
|
|
274
|
+
}
|
|
275
|
+
// Raw Request Data (without base64)
|
|
276
|
+
if (debugInfo.rawRequest) {
|
|
277
|
+
sections.push('## Raw Request Data\n');
|
|
278
|
+
sections.push('```json');
|
|
279
|
+
const sanitizedRequest = this.sanitizeRequest(debugInfo.rawRequest);
|
|
280
|
+
sections.push(JSON.stringify(sanitizedRequest, null, 2));
|
|
281
|
+
sections.push('```');
|
|
282
|
+
sections.push('');
|
|
283
|
+
}
|
|
284
|
+
// Raw Response Data (without base64)
|
|
285
|
+
if (debugInfo.rawResponse) {
|
|
286
|
+
sections.push('## Raw Response Data\n');
|
|
287
|
+
sections.push('```json');
|
|
288
|
+
const sanitizedResponse = this.sanitizeResponse(debugInfo.rawResponse);
|
|
289
|
+
sections.push(JSON.stringify(sanitizedResponse, null, 2));
|
|
290
|
+
sections.push('```');
|
|
291
|
+
sections.push('');
|
|
292
|
+
}
|
|
293
|
+
// Error
|
|
294
|
+
if (debugInfo.error) {
|
|
295
|
+
sections.push('## Error\n');
|
|
296
|
+
sections.push(`- **Message**: ${debugInfo.error.message}`);
|
|
297
|
+
if (debugInfo.error.code) {
|
|
298
|
+
sections.push(`- **Code**: ${debugInfo.error.code}`);
|
|
299
|
+
}
|
|
300
|
+
if (debugInfo.error.details) {
|
|
301
|
+
sections.push('- **Details**:');
|
|
302
|
+
sections.push('```json');
|
|
303
|
+
sections.push(JSON.stringify(debugInfo.error.details, null, 2));
|
|
304
|
+
sections.push('```');
|
|
305
|
+
}
|
|
306
|
+
sections.push('');
|
|
307
|
+
}
|
|
308
|
+
// Footer
|
|
309
|
+
sections.push('---');
|
|
310
|
+
sections.push(`*Generated on ${new Date().toISOString()}*`);
|
|
311
|
+
return sections.join('\n');
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Sanitize request by removing/truncating base64 data
|
|
315
|
+
*/
|
|
316
|
+
static sanitizeRequest(request) {
|
|
317
|
+
const sanitized = { ...request };
|
|
318
|
+
if (request.referenceImages && !this._includeBase64) {
|
|
319
|
+
sanitized.referenceImages = request.referenceImages.map((ref, index) => ({
|
|
320
|
+
index,
|
|
321
|
+
mimeType: ref.mimeType || 'unknown',
|
|
322
|
+
base64Length: ref.base64?.length || 0,
|
|
323
|
+
base64Preview: ref.base64 ? `${ref.base64.substring(0, 50)}...` : null,
|
|
324
|
+
}));
|
|
325
|
+
}
|
|
326
|
+
return sanitized;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Sanitize response by removing/truncating base64 data
|
|
330
|
+
*/
|
|
331
|
+
static sanitizeResponse(response) {
|
|
332
|
+
const sanitized = { ...response };
|
|
333
|
+
if (response.images && !this._includeBase64) {
|
|
334
|
+
sanitized.images = response.images.map((img, index) => ({
|
|
335
|
+
index,
|
|
336
|
+
contentType: img.contentType || 'unknown',
|
|
337
|
+
hasUrl: !!img.url,
|
|
338
|
+
base64Length: img.base64?.length || 0,
|
|
339
|
+
base64Preview: img.base64 ? `${img.base64.substring(0, 50)}...` : null,
|
|
340
|
+
}));
|
|
341
|
+
}
|
|
342
|
+
return sanitized;
|
|
343
|
+
}
|
|
344
|
+
// ============================================================
|
|
345
|
+
// HELPER METHODS
|
|
346
|
+
// ============================================================
|
|
347
|
+
/**
|
|
348
|
+
* Ensure the logs directory exists
|
|
349
|
+
*/
|
|
350
|
+
static ensureLogsDirectory() {
|
|
351
|
+
if (!fs.existsSync(this._logsDir)) {
|
|
352
|
+
fs.mkdirSync(this._logsDir, { recursive: true });
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Create debug info from a request (helper for providers)
|
|
357
|
+
*/
|
|
358
|
+
static createDebugInfo(request, provider, model, options) {
|
|
359
|
+
return {
|
|
360
|
+
requestTimestamp: new Date(),
|
|
361
|
+
provider,
|
|
362
|
+
model,
|
|
363
|
+
region: options?.region,
|
|
364
|
+
prompt: request.prompt,
|
|
365
|
+
subjectDescription: request.subjectDescription,
|
|
366
|
+
referenceImageCount: request.referenceImages?.length || 0,
|
|
367
|
+
referenceImageMimeTypes: request.referenceImages?.map((ref) => ref.mimeType || 'unknown'),
|
|
368
|
+
aspectRatio: request.aspectRatio,
|
|
369
|
+
providerOptions: request.providerOptions,
|
|
370
|
+
useCase: options?.useCase,
|
|
371
|
+
sessionId: options?.sessionId,
|
|
372
|
+
bookId: options?.bookId,
|
|
373
|
+
characterId: options?.characterId,
|
|
374
|
+
sectionId: options?.sectionId,
|
|
375
|
+
rawRequest: request,
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Update debug info with response data (helper for providers)
|
|
380
|
+
*/
|
|
381
|
+
static updateWithResponse(debugInfo, response) {
|
|
382
|
+
return {
|
|
383
|
+
...debugInfo,
|
|
384
|
+
responseTimestamp: new Date(),
|
|
385
|
+
response: {
|
|
386
|
+
imageCount: response.images.length,
|
|
387
|
+
imageMimeTypes: response.images.map((img) => img.contentType || 'unknown'),
|
|
388
|
+
duration: response.metadata.duration,
|
|
389
|
+
},
|
|
390
|
+
rawResponse: response,
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Update debug info with error data (helper for providers)
|
|
395
|
+
*/
|
|
396
|
+
static updateWithError(debugInfo, error) {
|
|
397
|
+
return {
|
|
398
|
+
...debugInfo,
|
|
399
|
+
responseTimestamp: new Date(),
|
|
400
|
+
error: {
|
|
401
|
+
message: error.message,
|
|
402
|
+
code: error.code,
|
|
403
|
+
details: error.cause,
|
|
404
|
+
},
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
exports.TTIDebugger = TTIDebugger;
|
|
409
|
+
TTIDebugger._enabled = process.env.DEBUG_TTI_REQUESTS === 'true' ||
|
|
410
|
+
process.env.NODE_ENV === 'development';
|
|
411
|
+
TTIDebugger._logsDir = process.env.TTI_DEBUG_LOG_DIR ||
|
|
412
|
+
path.join(process.cwd(), 'logs', 'tti', 'requests');
|
|
413
|
+
TTIDebugger._includeBase64 = false;
|
|
414
|
+
TTIDebugger._consoleLog = false;
|
|
415
|
+
// ============================================================
|
|
416
|
+
// EXPORTS
|
|
417
|
+
// ============================================================
|
|
418
|
+
exports.default = TTIDebugger;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './debug-tti.utils';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./debug-tti.utils"), exports);
|
|
@@ -93,6 +93,16 @@ export interface TTIRequest {
|
|
|
93
93
|
* Default: true (retry enabled with defaults)
|
|
94
94
|
*/
|
|
95
95
|
retry?: boolean | RetryOptions;
|
|
96
|
+
/**
|
|
97
|
+
* Dry mode - validate and log request without making actual API calls.
|
|
98
|
+
* Useful for development and debugging without incurring API costs.
|
|
99
|
+
* When enabled:
|
|
100
|
+
* - Request is validated as normal
|
|
101
|
+
* - Request is logged via TTIDebugger (if enabled)
|
|
102
|
+
* - A mock response is returned (no actual image generation)
|
|
103
|
+
* Default: false
|
|
104
|
+
*/
|
|
105
|
+
dry?: boolean;
|
|
96
106
|
}
|
|
97
107
|
/**
|
|
98
108
|
* Generated image
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loonylabs/tti-middleware",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Provider-agnostic Text-to-Image middleware with GDPR compliance. Supports Google Cloud (Imagen, Gemini), Eden AI, and IONOS.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|