Files
iddaai-fe/src/lib/utils/ai-helper.ts
T
fahricansecer 5c8619b282
Deploy Iddaai Frontend / build-and-deploy (push) Failing after 34s
gg
2026-05-10 22:59:27 +03:00

246 lines
7.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { getPricing } from "@/lib/services/pricing-service";
import { GoogleGenAI, Modality } from "@google/genai";
/**
* Merkezi yapay zeka istemci örneği
* Tüm yapay zeka istekleri bu örneği kullanmalıdır
*/
export const ai = new GoogleGenAI({
vertexai: true,
apiKey: process.env.GOOGLE_API_KEY,
});
// Kalite artırıcı anahtar kelimeler
const QUALITY_BOOSTERS = [
"highly detailed",
"8k resolution",
"professional photography",
"studio lighting",
"sharp focus",
"cinematic composition",
"vibrant colors",
"masterpiece",
"masterpiece",
];
/**
* Yapay Zeka Model yapılandırmaları
*/
export const AI_MODELS = {
FLASH_LITE: "gemini-2.5-flash-lite",
FLASH: "gemini-2.5-flash",
FLASH_IMAGE: "gemini-2.5-flash-image",
} as const;
// ——————————————————————————————————————
// Type definitions for Gemini API responses
// ——————————————————————————————————————
/** Token usage metadata from Gemini API */
interface UsageMetadata {
promptTokenCount?: number;
candidatesTokenCount?: number;
totalTokenCount?: number;
}
/** Gemini content part (text or inline data) */
interface ContentPart {
text?: string;
inlineData?: { mimeType: string; data: string };
}
/** Generation config passed to Gemini */
interface GenerationConfig {
responseModalities?: Modality[];
temperature?: number;
maxOutputTokens?: number;
[key: string]: unknown;
}
/** Prompt content — can be a string, structured content array, or object */
type PromptContent =
| string
| Array<{ role: string; parts: ContentPart[] }>
| Record<string, unknown>;
/**
* Yapay zeka ile içerik oluştur
* Tüm yapay zeka içerik oluşturma işlemleri için merkezi fonksiyon
*
* @param model - Kullanılacak yapay zeka modeli
* @param prompt - Gönderilecek istem veya içerikler
* @param config - İsteğe bağlı yapılandırma
* @returns Yapay zeka yanıtı
*/
export async function generateAIContent(
model: string,
prompt: PromptContent,
config?: GenerationConfig,
) {
try {
const response = await ai.models.generateContent({
model,
contents: prompt,
...(config && { config }),
});
return response;
} catch (error) {
console.error("AI generation error:", error);
throw error;
}
}
/**
* Yapay zeka ile metin içeriği oluştur
* @param model - Kullanılacak yapay zeka modeli
* @param prompt - Metin istemi
* @returns Oluşturulan metin
*/
export async function generateText(
model: string,
prompt: string,
): Promise<{ text: string; usage?: UsageMetadata }> {
const response = await generateAIContent(model, prompt);
return {
text: (response.text || "").trim(),
usage: response.usageMetadata as UsageMetadata | undefined,
};
}
/**
* Görüntü girdisi ile içerik oluştur
* @param model - Kullanılacak yapay zeka modeli
* @param imageBase64 - Base64 kodlanmış görüntü
* @param textPrompt - Metin istemi
* @returns Yapay zeka yanıtı
*/
export async function generateWithImage(
model: string,
imageBase64: string,
textPrompt: string,
) {
const response = await generateAIContent(model, [
{
role: "user",
parts: [
{ inlineData: { mimeType: "image/jpeg", data: imageBase64 } },
{ text: textPrompt },
],
},
]);
return {
text: (response.text || "").trim(),
usage: response.usageMetadata as UsageMetadata | undefined,
};
}
/**
* İstemden görüntü oluştur
* @param prompt - Görüntü oluşturma için metin istemi
* @param imageBase64 - Düzenleme için isteğe bağlı base64 görüntü
* @param suggestions - Görsel iyileştirme önerileri (Nano Banana için)
* @param model - Kullanılacak model (varsayılan: FLASH_IMAGE)
* @param aspectRatio - Hedef en boy oranı (örn: "16:9", "1:1")
* @returns Oluşturulan görüntü URL'si (base64)
*/
export async function generateImage(
prompt: string,
imageBase64?: string,
suggestions?: string[],
model: string = AI_MODELS.FLASH_IMAGE,
aspectRatio?: string,
): Promise<{ imageUrl: string | null; usage?: UsageMetadata }> {
let parts: Array<
{ text: string } | { inlineData: { mimeType: string; data: string } }
> = [];
// En boy oranı talimatı oluştur
const ratioInstruction = aspectRatio
? `\n\nTarget Aspect Ratio: ${aspectRatio}\nEnsure the image strictly follows the ${aspectRatio} aspect ratio format.`
: "";
if (imageBase64) {
// Görüntü düzenleme modu
const cleanBase64 = imageBase64.replace(
/^data:image\/(png|jpeg|webp|jpg);base64,/,
"",
);
// Eğer Nano Banana modeli ve öneriler varsa, prompt'u zenginleştir
let finalPrompt = prompt;
if (model === "nano-banana") {
let improvementInstructions = "";
if (suggestions && suggestions.length > 0) {
improvementInstructions = `\n\nApply these specific improvements based on platform analysis:\n${suggestions.map((s) => `- ${s}`).join("\n")}`;
}
finalPrompt = `${prompt}${improvementInstructions}\n\nStyle & Quality Instructions:\nRender in ${QUALITY_BOOSTERS.join(", ")}.\n\nCRITICAL OBJECTIVE: The result MUST achieve a perfect 10/10 score.\n- Clarity: 10/10 (Ultra-sharp, no blur)\n- Professionalism: 10/10 (High-end commercial look)\n- Engagement: 10/10 (Eye-catching contrast and lighting)\n- Platform Fit: 10/10 (Perfect aspect ratio and framing)\nEnsure the image is visually stunning and flawless.`;
}
parts.push({ inlineData: { mimeType: "image/png", data: cleanBase64 } });
parts.push({ text: finalPrompt + ratioInstruction });
} else {
parts.push({ text: prompt + ratioInstruction });
}
console.log("[AI-Helper] generateImage calling Gemini model", {
model: AI_MODELS.FLASH_IMAGE,
});
try {
const response = await ai.models.generateContent({
model,
contents: [{ role: "user", parts }],
config: {
responseModalities: [Modality.IMAGE],
},
});
const candidate = response.candidates?.[0];
const imagePart = candidate?.content?.parts?.find((p) =>
p.inlineData?.mimeType?.startsWith("image/"),
);
if (imagePart?.inlineData?.data) {
return {
imageUrl: `data:${imagePart.inlineData.mimeType};base64,${imagePart.inlineData.data}`,
usage: response.usageMetadata as UsageMetadata | undefined,
};
}
console.warn("No image part found in response");
return {
imageUrl: null,
usage: response.usageMetadata as UsageMetadata | undefined,
};
} catch (error) {
console.error("generateImage error:", error);
return { imageUrl: null };
}
}
/**
* Token kullanımına dayalı Yapay Zeka Maliyetini hesapla
* Giriş Maliyeti = (Giriş Tokenları / 1.000.000) * Giriş Fiyatı
* Çıkış Maliyeti = (Çıkış Tokenları / 1.000.000) * Çıkış Fiyatı
* @param usage - Yapay zeka yanıtından kullanım meta verileri
* @returns Para birimi cinsinden toplam maliyet
*/
export async function calculateAICost(
usage: UsageMetadata | null | undefined,
): Promise<number> {
if (!usage) return 0;
const pricing = await getPricing();
const inputTokens = usage.promptTokenCount || 0;
const outputTokens = usage.candidatesTokenCount || 0;
const inputCost = (inputTokens / 1_000_000) * pricing.inputCost;
const outputCost = (outputTokens / 1_000_000) * pricing.outputCost;
return inputCost + outputCost;
}