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; /** * 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 { 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; }