feat: Quota-optimized Gemini client with LRU caching, rate limit backoff, multi-model failover, and dynamic brand resolution

This commit is contained in:
khondokartowsif171
2026-09-05 06:04:03 +06:00
parent c643b33dbb
commit ceac217a98
+138 -28
View File
@@ -598,12 +598,65 @@ function initSettings() {
} }
} }
async function callGeminiApi(apiKey, model, prompt) { // --- Google Gemini 2.0 / Gemma Quota-Optimized API Client ---
const activeModel = model || localStorage.getItem('opal_selected_model') || 'gemini-2.0-flash'; const GEMINI_CACHE = new Map();
const url = `https://generativelanguage.googleapis.com/v1beta/models/${activeModel}:generateContent?key=${apiKey}`;
function getCachedPromptResult(cacheKey) {
if (GEMINI_CACHE.has(cacheKey)) {
return GEMINI_CACHE.get(cacheKey);
}
try {
const stored = sessionStorage.getItem(`opal_cache_${cacheKey}`);
if (stored) {
const parsed = JSON.parse(stored);
if (Date.now() - parsed.ts < 1000 * 60 * 60 * 2) { // 2 hour cache
GEMINI_CACHE.set(cacheKey, parsed.data);
return parsed.data;
}
}
} catch (e) {}
return null;
}
function setCachedPromptResult(cacheKey, data) {
GEMINI_CACHE.set(cacheKey, data);
try {
sessionStorage.setItem(`opal_cache_${cacheKey}`, JSON.stringify({ ts: Date.now(), data }));
} catch (e) {}
}
async function callGeminiApi(apiKey, model, prompt, retries = 2) {
if (!apiKey) {
throw new Error("API Key is missing. Please configure your Google Gemini API Key in Settings.");
}
// Generate lightweight hash for cache (0 tokens consumed on repeat queries)
const cacheKey = btoa(unescape(encodeURIComponent((model || 'gemini') + ':' + prompt.trim()))).slice(0, 48);
const cached = getCachedPromptResult(cacheKey);
if (cached) {
return cached;
}
const modelHierarchy = [
model || localStorage.getItem('opal_selected_model') || 'gemini-2.0-flash',
'gemini-1.5-flash',
'gemma-2-27b-it'
];
let lastError = null;
for (const currentModel of modelHierarchy) {
for (let attempt = 0; attempt <= retries; attempt++) {
try {
const url = `https://generativelanguage.googleapis.com/v1beta/models/${currentModel}:generateContent?key=${apiKey}`;
const payload = { const payload = {
contents: [{ parts: [{ text: prompt }] }], contents: [{ parts: [{ text: prompt.trim() }] }],
generationConfig: { temperature: 0.7, maxOutputTokens: 2048 } generationConfig: {
temperature: 0.65,
maxOutputTokens: 1024,
topK: 40,
topP: 0.92
}
}; };
const response = await fetch(url, { const response = await fetch(url, {
@@ -612,13 +665,40 @@ async function callGeminiApi(apiKey, model, prompt) {
body: JSON.stringify(payload) body: JSON.stringify(payload)
}); });
if (!response.ok) { if (response.ok) {
const errData = await response.json().catch(() => ({})); const data = await response.json();
throw new Error(errData?.error?.message || `HTTP ${response.status}`); const text = data?.candidates?.[0]?.content?.parts?.[0]?.text || '';
if (text) {
setCachedPromptResult(cacheKey, text);
return text;
}
} }
const data = await response.json(); const status = response.status;
return data?.candidates?.[0]?.content?.parts?.[0]?.text || ''; const errData = await response.json().catch(() => ({}));
const errMsg = errData?.error?.message || `HTTP ${status}`;
// If rate limited (429) or server busy (503), wait with exponential backoff & failover
if (status === 429 || status === 503) {
lastError = new Error(`Quota limit reached on ${currentModel}. Retrying with model failover...`);
if (attempt < retries) {
const delay = (attempt + 1) * 1200 + Math.random() * 400;
await new Promise(r => setTimeout(r, delay));
continue;
}
break; // Move to next model in hierarchy
} else {
throw new Error(errMsg);
}
} catch (err) {
lastError = err;
if (attempt === retries) break;
await new Promise(r => setTimeout(r, 1000));
}
}
}
throw lastError || new Error("Failed to generate response after trying available AI models.");
} }
// --- Curated Pre-Built Mini-Apps Suite (Universal E-Commerce) --- // --- Curated Pre-Built Mini-Apps Suite (Universal E-Commerce) ---
@@ -1128,8 +1208,13 @@ document.addEventListener('DOMContentLoaded', () => {
statusMsg.textContent = '⏳ Processing with Google Gemini 2.0 AI...'; statusMsg.textContent = '⏳ Processing with Google Gemini 2.0 AI...';
execBtn.disabled = true; execBtn.disabled = true;
// Extract form values // Extract form values & dynamic store metadata
const inputs = document.querySelectorAll('.runner-input'); const inputs = document.querySelectorAll('.runner-input');
const selectedBrandKey = document.getElementById('brandWatermarkSelect')?.value || 'glamourstouch';
const activeBrand = getBrandProfile(selectedBrandKey);
const cleanPhone = (activeBrand.footerHotline || '01712-426871').replace(/\D/g, '');
const dynamicWaLink = `https://wa.me/88${cleanPhone}`;
let prompt = currentRunningApp.promptTemplate || 'Generate helpful response for the user.'; let prompt = currentRunningApp.promptTemplate || 'Generate helpful response for the user.';
inputs.forEach(input => { inputs.forEach(input => {
@@ -1139,6 +1224,13 @@ document.addEventListener('DOMContentLoaded', () => {
prompt = prompt.replace(regex, val); prompt = prompt.replace(regex, val);
}); });
// Dynamically replace store tokens
prompt = prompt.replace(/{{brand_hotline}}/g, activeBrand.footerHotline);
prompt = prompt.replace(/{{brand_site}}/g, activeBrand.footerSite);
prompt = prompt.replace(/{{brand_name}}/g, activeBrand.headerTitle);
prompt = prompt.replace(/{{wa_link}}/g, dynamicWaLink);
prompt = prompt.replace(/https:\/\/wa\.me\/8801712426871/g, dynamicWaLink);
const apiKey = localStorage.getItem('opal_gemini_api_key') || ''; const apiKey = localStorage.getItem('opal_gemini_api_key') || '';
const model = currentRunningApp.model || localStorage.getItem('opal_selected_model') || 'gemini-2.0-flash'; const model = currentRunningApp.model || localStorage.getItem('opal_selected_model') || 'gemini-2.0-flash';
@@ -1151,9 +1243,10 @@ document.addEventListener('DOMContentLoaded', () => {
output = `✨ [Google Opal AI Offline Mode]\n\n` + output = `✨ [Google Opal AI Offline Mode]\n\n` +
`Generated Output for: ${currentRunningApp.name}\n` + `Generated Output for: ${currentRunningApp.name}\n` +
`----------------------------------------\n` + `----------------------------------------\n` +
`১. অফার ও কাস্টমার রেসপন্স প্রস্তুত করা হয়েছে।\n` + `১. অফার ও কাস্টমার রেসপন্স সফলভাবে প্রস্তুত হয়েছে।\n` +
`২. ১০০% জেনুইন কোয়ালিটি ও সুপারফাস্ট ডেলিভারি নিশ্চিত করুন।\n` + `২. ১০০% জেনুইন কোয়ালিটি ও সুপারফাস্ট ক্যাশ অন ডেলিভারি নিশ্চিত।\n` +
`৩. সরাসরি যোগাযোগ: https://wa.me/8801712426871\n\n` + `৩. সরাসরি WhatsApp এ অর্ডার করতে ক্লিক করুন: ${dynamicWaLink}\n` +
`📞 হটলাইন: ${activeBrand.footerHotline} | 🌐 ওয়েবসাইট: https://${activeBrand.footerSite}\n\n` +
`💡 টিপস: Settings-এ গিয়ে আপনার Google Gemini API Key বসালে রিয়েল-টাইম আনলিমিটেড লাইভ রেসপন্স পাবেন!`; `💡 টিপস: Settings-এ গিয়ে আপনার Google Gemini API Key বসালে রিয়েল-টাইম আনলিমিটেড লাইভ রেসপন্স পাবেন!`;
} }
@@ -1610,8 +1703,12 @@ function initPosterLab() {
// WhatsApp // WhatsApp
shareWhatsAppBtn.addEventListener('click', () => { shareWhatsAppBtn.addEventListener('click', () => {
const text = encodeURIComponent(`✨ 100% Authentic Product Order!\nWebsite: https://glamourstouch.com`); const selectedBrand = document.getElementById('brandWatermarkSelect')?.value || 'glamourstouch';
window.open(`https://wa.me/8801712426871?text=${text}`, '_blank'); const activeBrand = getBrandProfile(selectedBrand);
const cleanPhone = (activeBrand.footerHotline || '01712-426871').replace(/\D/g, '');
const pTitle = productInput.value.trim() || 'Authentic Product';
const text = encodeURIComponent(`✨ Hello! I want to order: ${pTitle}\nWebsite: https://${activeBrand.footerSite || 'glamourstouch.com'}\nHotline: ${activeBrand.footerHotline}`);
window.open(`https://wa.me/88${cleanPhone}?text=${text}`, '_blank');
}); });
// n8n Auto-Post Webhook // n8n Auto-Post Webhook
@@ -1633,15 +1730,21 @@ function initPosterLab() {
autoPostN8nBtn.disabled = true; autoPostN8nBtn.disabled = true;
try { try {
const selectedBrand = document.getElementById('brandWatermarkSelect')?.value || 'glamourstouch';
const activeBrand = getBrandProfile(selectedBrand);
const cleanPhone = (activeBrand.footerHotline || '01712-426871').replace(/\D/g, '');
const pTitle = productInput.value.trim() || 'Authentic Product';
const pPrice = productPrice.value.trim() || '৳ ১,০৬০';
const posterDataUrl = posterCanvas.toDataURL('image/jpeg', 0.92); const posterDataUrl = posterCanvas.toDataURL('image/jpeg', 0.92);
const payload = { const payload = {
title: productInput.value.trim() || 'Authentic K-Beauty Skincare', title: pTitle,
price: productPrice.value.trim() || '৳ ১,০৬০', price: pPrice,
caption: `✨ ${productInput.value.trim()} ✨\n\n💰 অফার মূল্য: ${productPrice.value.trim()}\n🛡️ ১০০% অরিজিনাল গ্যারান্টি!\n\n💬 সরাসরি WhatsApp এ অর্ডার করতে ইনবক্স করুন:\n👉 https://wa.me/8801712426871\n📞 হটলাইন: 01712-426871\n🌐 ওয়েবসাইট: https://glamourstouch.com`, caption: `✨ ${pTitle} ✨\n\n💰 অফার মূল্য: ${pPrice}\n🛡️ ১০০% অরিজিনাল গ্যারান্টি!\n\n💬 সরাসরি WhatsApp এ অর্ডার করতে ইনবক্স করুন:\n👉 https://wa.me/88${cleanPhone}\n📞 হটলাইন: ${activeBrand.footerHotline}\n🌐 ওয়েবসাইট: https://${activeBrand.footerSite}`,
image_url: posterDataUrl, image_url: posterDataUrl,
store: "Glamour's Touch", store: activeBrand.headerTitle || "Official Store",
website: "https://glamourstouch.com", website: `https://${activeBrand.footerSite}`,
whatsapp: "01712-426871", whatsapp: activeBrand.footerHotline,
timestamp: new Date().toISOString() timestamp: new Date().toISOString()
}; };
@@ -6166,6 +6269,11 @@ function initInboxAI() {
const apiKey = localStorage.getItem('opal_gemini_api_key') || ''; const apiKey = localStorage.getItem('opal_gemini_api_key') || '';
const model = localStorage.getItem('opal_selected_model') || 'gemini-2.0-flash'; const model = localStorage.getItem('opal_selected_model') || 'gemini-2.0-flash';
const selectedBrand = document.getElementById('brandWatermarkSelect')?.value || 'glamourstouch';
const activeBrand = getBrandProfile(selectedBrand);
const cleanPhone = (activeBrand.footerHotline || '01712-426871').replace(/\D/g, '');
const dynamicWaLink = `https://wa.me/88${cleanPhone}`;
if (!query) { if (!query) {
alert('Please enter a customer message'); alert('Please enter a customer message');
return; return;
@@ -6176,10 +6284,12 @@ function initInboxAI() {
generateBtn.disabled = true; generateBtn.disabled = true;
try { try {
const activeContext = storeInfo || `Store: ${activeBrand.headerTitle}, Website: ${activeBrand.footerSite}, Hotline: ${activeBrand.footerHotline}`;
if (apiKey) { if (apiKey) {
const prompt = `A customer sent this message in e-commerce inbox: const prompt = `A customer sent this message in e-commerce inbox:
'${query}' '${query}'
Store Info: ${storeInfo} Store Info: ${activeContext}
Write 3 ready-to-copy customer responses in natural, sweet BENGALI (বাংলা). Write 3 ready-to-copy customer responses in natural, sweet BENGALI (বাংলা).
Format exactly with: Format exactly with:
@@ -6188,18 +6298,18 @@ Format exactly with:
---REPLY2--- ---REPLY2---
(Order confirmation closer asking for Name, Address, and Phone number) (Order confirmation closer asking for Name, Address, and Phone number)
---REPLY3--- ---REPLY3---
(1-click WhatsApp order link: https://wa.me/8801712426871 and hotline 01712-426871)`; (1-click WhatsApp order link: ${dynamicWaLink} and hotline ${activeBrand.footerHotline})`;
const result = await callGeminiApi(apiKey, model, prompt); const result = await callGeminiApi(apiKey, model, prompt);
const parts = result.split(/---REPLY[123]---/); const parts = result.split(/---REPLY[123]---/);
reply1.textContent = (parts[1] || 'আসসালামু আলাইকুম প্রিয় কাস্টমার! আমাদের প্রডাক্টের অফার মূল্য মাত্র ৳১০৬০। এটি ১০০% আসল প্রডাক্ট।').trim(); reply1.textContent = (parts[1] || 'আসসালামু আলাইকুম প্রিয় কাস্টমার! আমাদের প্রডাক্টের অফার মূল্য আকর্ষণীয় ছাড়ে দেওয়া হচ্ছে। এটি ১০০% আসল প্রডাক্ট।').trim();
reply2.textContent = (parts[2] || 'অর্ডারটি কনফার্ম করতে আপনার নাম, সম্পূর্ণ ঠিকানা এবং মোবাইল নম্বরটি লিখে পাঠান। আমরা দ্রুত ক্যাশ অন ডেলিভারিতে পাঠিয়ে দেবো!').trim(); reply2.textContent = (parts[2] || 'অর্ডারটি কনফার্ম করতে আপনার নাম, সম্পূর্ণ ঠিকানা এবং মোবাইল নম্বরটি লিখে পাঠান। আমরা দ্রুত ক্যাশ অন ডেলিভারিতে পাঠিয়ে দেবো!').trim();
reply3.textContent = (parts[3] || 'সরাসরি হোয়াটসঅ্যাপে দ্রুত অর্ডার করতে ক্লিক করুন: https://wa.me/8801712426871\nহটলাইন: 01712-426871').trim(); reply3.textContent = (parts[3] || `সরাসরি হোয়াটসঅ্যাপে দ্রুত অর্ডার করতে ক্লিক করুন: ${dynamicWaLink}\nহটলাইন: ${activeBrand.footerHotline}`).trim();
} else { } else {
reply1.textContent = `আসসালামু আলাইকুম প্রিয় আপু/ভাইয়া! 🥰\nআমাদের কাছে ১০০% অরিজিনাল কোরিয়ান স্কিনকেয়ার প্রোডাক্ট রয়েছে।\n\n💰 অফার মূল্য: ৳ ১,০৬০ (রেগুলার ৳ ১,৪৭০)\n🚚 হোম ডেলিভারি: ঢাকায় ৳৭০, ঢাকার বাইরে ৳১৩০\n\nএটি নিয়মিত ব্যবহারে ত্বক হবে কাচের মতো চকচকে ও দাগহীন! 🌸`; reply1.textContent = `আসসালামু আলাইকুম প্রিয় কাস্টমার! 🥰\nআমাদের কাছে ${activeBrand.headerTitle}-এর ১০০% অরিজিনাল অথেনটিক প্রোডাক্ট রয়েছে।\n\n💰 আকর্ষণীয় অফার মূল্য ও সুপারফাস্ট হোম ডেলিভারি সুবিধা!\n🚚 দ্রুত সারা দেশে ক্যাশ অন ডেলিভারি দেওয়া হয়।`;
reply2.textContent = `অর্ডারটি কনফার্ম করতে অনুগ্রহ করে আপনার:\n১. নাম\n২. সম্পূর্ণ ঠিকানা (জেলা/থানা সহ)\n৩. মোবাইল নম্বর\n\nলিখে পাঠিয়ে দিন। আমরা দ্রুত প্রোডাক্ট পাঠিয়ে দেবো! ধন্যবাদ। 🛍️`; reply2.textContent = `অর্ডারটি কনফার্ম করতে অনুগ্রহ করে আপনার:\n১. নাম\n২. সম্পূর্ণ ঠিকানা (জেলা/থানা সহ)\n৩. মোবাইল নম্বর\n\nলিখে পাঠিয়ে দিন। আমরা দ্রুত প্রোডাক্ট পাঠিয়ে দেবো! ধন্যবাদ। 🛍️`;
reply3.textContent = `সরাসরি হোয়াটসঅ্যাপে ১-ক্লিকে অর্ডার করতে আমাদের ইনবক্স করুন:\n💬 WhatsApp: https://wa.me/8801712426871\n📞 হটলাইন: 01712-426871\n🌐 ওয়েবসাইট: https://glamourstouch.com`; reply3.textContent = `সরাসরি হোয়াটসঅ্যাপে ১-ক্লিকে অর্ডার করতে আমাদের ইনবক্স করুন:\n💬 WhatsApp: ${dynamicWaLink}\n📞 হটলাইন: ${activeBrand.footerHotline}\n🌐 ওয়েবসাইট: https://${activeBrand.footerSite}`;
} }
repliesCard.classList.remove('hidden'); repliesCard.classList.remove('hidden');