feat: log generation requests
This commit is contained in:
parent
13e8824000
commit
367f1d1946
|
|
@ -18,3 +18,6 @@ UPLOADS_DIR=./uploads/temp
|
||||||
|
|
||||||
# Logging Configuration
|
# Logging Configuration
|
||||||
LOG_LEVEL=info
|
LOG_LEVEL=info
|
||||||
|
|
||||||
|
# Text-to-Image Logging (optional)
|
||||||
|
# TTI_LOG=./apps/api-service/logs/tti.log
|
||||||
|
|
@ -9,6 +9,7 @@ import {
|
||||||
GeminiParams,
|
GeminiParams,
|
||||||
} from "../types/api";
|
} from "../types/api";
|
||||||
import { StorageFactory } from "./StorageFactory";
|
import { StorageFactory } from "./StorageFactory";
|
||||||
|
import { TTILogger, TTILogEntry } from "./TTILogger";
|
||||||
|
|
||||||
export class ImageGenService {
|
export class ImageGenService {
|
||||||
private ai: GoogleGenAI;
|
private ai: GoogleGenAI;
|
||||||
|
|
@ -39,7 +40,12 @@ export class ImageGenService {
|
||||||
let generatedData: GeneratedImageData;
|
let generatedData: GeneratedImageData;
|
||||||
let geminiParams: GeminiParams;
|
let geminiParams: GeminiParams;
|
||||||
try {
|
try {
|
||||||
const aiResult = await this.generateImageWithAI(prompt, referenceImages);
|
const aiResult = await this.generateImageWithAI(
|
||||||
|
prompt,
|
||||||
|
referenceImages,
|
||||||
|
finalOrgId,
|
||||||
|
finalProjectId,
|
||||||
|
);
|
||||||
generatedData = aiResult.generatedData;
|
generatedData = aiResult.generatedData;
|
||||||
geminiParams = aiResult.geminiParams;
|
geminiParams = aiResult.geminiParams;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -114,8 +120,13 @@ export class ImageGenService {
|
||||||
*/
|
*/
|
||||||
private async generateImageWithAI(
|
private async generateImageWithAI(
|
||||||
prompt: string,
|
prompt: string,
|
||||||
referenceImages?: ReferenceImage[],
|
referenceImages: ReferenceImage[] | undefined,
|
||||||
): Promise<{ generatedData: GeneratedImageData; geminiParams: GeminiParams }> {
|
orgId: string,
|
||||||
|
projectId: string,
|
||||||
|
): Promise<{
|
||||||
|
generatedData: GeneratedImageData;
|
||||||
|
geminiParams: GeminiParams;
|
||||||
|
}> {
|
||||||
const contentParts: any[] = [];
|
const contentParts: any[] = [];
|
||||||
|
|
||||||
// Add reference images if provided
|
// Add reference images if provided
|
||||||
|
|
@ -135,6 +146,8 @@ export class ImageGenService {
|
||||||
text: prompt,
|
text: prompt,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// CRITICAL: Calculate exact values before SDK call
|
||||||
|
// These exact objects will be passed to both SDK and logger
|
||||||
const contents = [
|
const contents = [
|
||||||
{
|
{
|
||||||
role: "user" as const,
|
role: "user" as const,
|
||||||
|
|
@ -155,7 +168,29 @@ export class ImageGenService {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Log TTI request BEFORE SDK call - using exact same values
|
||||||
|
const ttiLogger = TTILogger.getInstance();
|
||||||
|
const logEntry: TTILogEntry = {
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
orgId,
|
||||||
|
projectId,
|
||||||
|
prompt,
|
||||||
|
model: this.primaryModel,
|
||||||
|
config,
|
||||||
|
...(referenceImages &&
|
||||||
|
referenceImages.length > 0 && {
|
||||||
|
referenceImages: referenceImages.map((img) => ({
|
||||||
|
mimetype: img.mimetype,
|
||||||
|
size: img.buffer.length,
|
||||||
|
originalname: img.originalname,
|
||||||
|
})),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
ttiLogger.log(logEntry);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Use the EXACT same config and contents objects calculated above
|
||||||
const response = await this.ai.models.generateContent({
|
const response = await this.ai.models.generateContent({
|
||||||
model: this.primaryModel,
|
model: this.primaryModel,
|
||||||
config,
|
config,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
import { writeFileSync, appendFileSync, existsSync, mkdirSync } from "fs";
|
||||||
|
import { dirname } from "path";
|
||||||
|
|
||||||
|
export interface TTILogEntry {
|
||||||
|
timestamp: string;
|
||||||
|
orgId: string;
|
||||||
|
projectId: string;
|
||||||
|
prompt: string;
|
||||||
|
referenceImages?: Array<{
|
||||||
|
mimetype: string;
|
||||||
|
size: number;
|
||||||
|
originalname: string;
|
||||||
|
}>;
|
||||||
|
model: string;
|
||||||
|
config: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TTILogger {
|
||||||
|
private static instance: TTILogger | null = null;
|
||||||
|
private logFilePath: string | null = null;
|
||||||
|
private isEnabled: boolean = false;
|
||||||
|
|
||||||
|
private constructor() {
|
||||||
|
const ttiLogPath = process.env["TTI_LOG"];
|
||||||
|
|
||||||
|
if (ttiLogPath) {
|
||||||
|
this.logFilePath = ttiLogPath;
|
||||||
|
this.isEnabled = true;
|
||||||
|
this.initializeLogFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static getInstance(): TTILogger {
|
||||||
|
if (!TTILogger.instance) {
|
||||||
|
TTILogger.instance = new TTILogger();
|
||||||
|
}
|
||||||
|
return TTILogger.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private initializeLogFile(): void {
|
||||||
|
if (!this.logFilePath) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Ensure directory exists
|
||||||
|
const dir = dirname(this.logFilePath);
|
||||||
|
if (!existsSync(dir)) {
|
||||||
|
mkdirSync(dir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset/clear the log file on service start
|
||||||
|
writeFileSync(this.logFilePath, "# Text-to-Image Generation Log\n\n", {
|
||||||
|
encoding: "utf-8",
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`[TTILogger] Log file initialized: ${this.logFilePath}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`[TTILogger] Failed to initialize log file:`, error);
|
||||||
|
this.isEnabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log(entry: TTILogEntry): void {
|
||||||
|
if (!this.isEnabled || !this.logFilePath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const logEntry = this.formatLogEntry(entry);
|
||||||
|
appendFileSync(this.logFilePath, logEntry, { encoding: "utf-8" });
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`[TTILogger] Failed to write log entry:`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private formatLogEntry(entry: TTILogEntry): string {
|
||||||
|
const { timestamp, orgId, projectId, prompt, referenceImages, model, config } = entry;
|
||||||
|
|
||||||
|
// Format date from ISO timestamp
|
||||||
|
const date = new Date(timestamp);
|
||||||
|
const formattedDate = date.toISOString().replace("T", " ").slice(0, 19);
|
||||||
|
|
||||||
|
let logText = `## ${formattedDate}\n`;
|
||||||
|
logText += `${orgId}/${projectId}\n\n`;
|
||||||
|
logText += `**Prompt:** ${prompt}\n\n`;
|
||||||
|
|
||||||
|
if (referenceImages && referenceImages.length > 0) {
|
||||||
|
logText += `**Reference Images:** ${referenceImages.length} image${referenceImages.length > 1 ? "s" : ""}\n`;
|
||||||
|
for (const img of referenceImages) {
|
||||||
|
const sizeMB = (img.size / (1024 * 1024)).toFixed(2);
|
||||||
|
logText += `- ${img.originalname} (${img.mimetype}, ${sizeMB} MB)\n`;
|
||||||
|
}
|
||||||
|
logText += "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
logText += `**Model:** ${model}\n`;
|
||||||
|
logText += `**Config:** ${JSON.stringify(config)}\n\n`;
|
||||||
|
logText += `---\n\n`;
|
||||||
|
|
||||||
|
return logText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -42,6 +42,7 @@ services:
|
||||||
- LOG_LEVEL=${LOG_LEVEL}
|
- LOG_LEVEL=${LOG_LEVEL}
|
||||||
- PORT=${PORT}
|
- PORT=${PORT}
|
||||||
- CORS_ORIGIN=${CORS_ORIGIN}
|
- CORS_ORIGIN=${CORS_ORIGIN}
|
||||||
|
- TTI_LOG=${TTI_LOG}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
postgres:
|
postgres:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue