From c0e19b8adfc7087732cbf91aed41528c6c7935e2 Mon Sep 17 00:00:00 2001 From: khushi Date: Mon, 30 Jun 2025 10:26:18 +0530 Subject: [PATCH 1/3] Fix: Add user feedback via popup for Groq API fetch failures --- background.js | 141 +++++++++++++++++++++----------------------------- 1 file changed, 59 insertions(+), 82 deletions(-) diff --git a/background.js b/background.js index b60c567..cf4b5bd 100644 --- a/background.js +++ b/background.js @@ -190,54 +190,42 @@ async function translateText(text) { const level = translationSettings?.level || "balanced"; const prompt = getTranslationPrompt(style, level); - try { - const response = await fetch( - "https://api.groq.com/openai/v1/chat/completions", - { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${groqApiKey}`, - }, - body: JSON.stringify({ - messages: [ - { - role: "system", - content: prompt, - }, - { - role: "user", - content: text, - }, - ], - model: "meta-llama/llama-4-scout-17b-16e-instruct", - temperature: 0.7, - max_tokens: 1000, - }), - } - ); - - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error( - errorData.error?.message || `API error: ${response.status}` - ); + const response = await fetch( + "https://api.groq.com/openai/v1/chat/completions", + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${groqApiKey}`, + }, + body: JSON.stringify({ + messages: [ + { role: "system", content: prompt }, + { role: "user", content: text }, + ], + model: "meta-llama/llama-4-scout-17b-16e-instruct", + temperature: 0.7, + max_tokens: 1000, + }), } + ); - const data = await response.json(); - const translatedText = data.choices[0].message.content.trim(); + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error(errorData.error?.message || `API error: ${response.status}`); + } - if (!translatedText) { - throw new Error("Empty translation received"); - } + const data = await response.json(); + const translatedText = data.choices[0].message.content.trim(); - return translatedText; - } catch (error) { - console.error("Translation error:", error); - throw error; + if (!translatedText) { + throw new Error("Empty translation received"); } + + return translatedText; } + // Function to explain text using Groq API async function explainText(text) { const { groqApiKey, translationSettings } = await chrome.storage.local.get([ @@ -251,6 +239,7 @@ async function explainText(text) { const style = translationSettings?.style || "hinglish"; const level = translationSettings?.level || "balanced"; + const prompt = `You are an AI assistant that explains concepts in ${ style === "hindi" ? "Hindi" : "Hinglish" }. @@ -265,54 +254,42 @@ async function explainText(text) { Format your response in a clear, structured way with bullet points or short paragraphs. Only respond with the explanation, no additional text.`; - try { - const response = await fetch( - "https://api.groq.com/openai/v1/chat/completions", - { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${groqApiKey}`, - }, - body: JSON.stringify({ - messages: [ - { - role: "system", - content: prompt, - }, - { - role: "user", - content: text, - }, - ], - model: "meta-llama/llama-4-scout-17b-16e-instruct", - temperature: 0.7, - max_tokens: 1000, - }), - } - ); - - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error( - errorData.error?.message || `API error: ${response.status}` - ); + const response = await fetch( + "https://api.groq.com/openai/v1/chat/completions", + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${groqApiKey}`, + }, + body: JSON.stringify({ + messages: [ + { role: "system", content: prompt }, + { role: "user", content: text }, + ], + model: "meta-llama/llama-4-scout-17b-16e-instruct", + temperature: 0.7, + max_tokens: 1000, + }), } + ); - const data = await response.json(); - const explanation = data.choices[0].message.content.trim(); + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error(errorData.error?.message || `API error: ${response.status}`); + } - if (!explanation) { - throw new Error("Empty explanation received"); - } + const data = await response.json(); + const explanation = data.choices[0].message.content.trim(); - return explanation; - } catch (error) { - console.error("Explanation error:", error); - throw error; + if (!explanation) { + throw new Error("Empty explanation received"); } + + return explanation; } + // Function to show loading popup function showLoadingPopup() { const popup = document.createElement("div"); From 536a1f92aa6667132dac32dc0d05a6ff2018a798 Mon Sep 17 00:00:00 2001 From: khushi Date: Mon, 30 Jun 2025 10:56:35 +0530 Subject: [PATCH 2/3] Fix: Show error popup for Groq API failures --- background.js | 1 - 1 file changed, 1 deletion(-) diff --git a/background.js b/background.js index cf4b5bd..add03b0 100644 --- a/background.js +++ b/background.js @@ -73,7 +73,6 @@ chrome.contextMenus.onClicked.addListener(async (info, tab) => { }); } catch (error) { console.error("Context menu translation error:", error); - // Show error in popup chrome.scripting.executeScript({ target: { tabId: tab.id }, func: showErrorPopup, From 17298df400037c17af814bd3ed6419f90ad073b6 Mon Sep 17 00:00:00 2001 From: khushi Date: Mon, 30 Jun 2025 12:06:10 +0530 Subject: [PATCH 3/3] feat: add retry logic to Groq API calls (Fixes #12) --- background.js | 192 +++++++++++++++++++++++++++++++------------------- 1 file changed, 119 insertions(+), 73 deletions(-) diff --git a/background.js b/background.js index add03b0..29c1686 100644 --- a/background.js +++ b/background.js @@ -175,7 +175,7 @@ function getTranslationPrompt(style, level) { } // Function to translate text using Groq API -async function translateText(text) { +async function translateText(text, retries = 2) { const { groqApiKey, translationSettings } = await chrome.storage.local.get([ "groqApiKey", "translationSettings", @@ -189,44 +189,66 @@ async function translateText(text) { const level = translationSettings?.level || "balanced"; const prompt = getTranslationPrompt(style, level); - const response = await fetch( - "https://api.groq.com/openai/v1/chat/completions", - { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${groqApiKey}`, - }, - body: JSON.stringify({ - messages: [ - { role: "system", content: prompt }, - { role: "user", content: text }, - ], - model: "meta-llama/llama-4-scout-17b-16e-instruct", - temperature: 0.7, - max_tokens: 1000, - }), - } - ); - - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error(errorData.error?.message || `API error: ${response.status}`); - } + const payload = { + messages: [ + { role: "system", content: prompt }, + { role: "user", content: text }, + ], + model: "meta-llama/llama-4-scout-17b-16e-instruct", + temperature: 0.7, + max_tokens: 1000, + }; - const data = await response.json(); - const translatedText = data.choices[0].message.content.trim(); + for (let attempt = 0; attempt <= retries; attempt++) { + try { + const response = await fetch( + "https://api.groq.com/openai/v1/chat/completions", + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${groqApiKey}`, + }, + body: JSON.stringify(payload), + } + ); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + + // Retry only on server (5xx) errors + if (response.status >= 500 && attempt < retries) { + console.warn(`Retrying translateText (attempt ${attempt + 1})`); + continue; + } + + throw new Error( + errorData.error?.message || `API error: ${response.status}` + ); + } + + const data = await response.json(); + const translatedText = data.choices[0].message.content.trim(); + + if (!translatedText) { + throw new Error("Empty translation received"); + } + + return translatedText; - if (!translatedText) { - throw new Error("Empty translation received"); + } catch (error) { + if (attempt === retries) { + throw error; + } + console.warn(`translateText failed on attempt ${attempt + 1}: ${error.message}`); + } } - - return translatedText; } + // Function to explain text using Groq API -async function explainText(text) { +async function explainText(text, retries = 2) { const { groqApiKey, translationSettings } = await chrome.storage.local.get([ "groqApiKey", "translationSettings", @@ -242,53 +264,77 @@ async function explainText(text) { const prompt = `You are an AI assistant that explains concepts in ${ style === "hindi" ? "Hindi" : "Hinglish" }. - Provide a clear and detailed explanation of the given text. - Make it easy to understand and use ${ - level === "moreHindi" - ? "more Hindi words" - : level === "moreEnglish" - ? "more English words" - : "a balanced mix of Hindi and English words" - }. - Format your response in a clear, structured way with bullet points or short paragraphs. - Only respond with the explanation, no additional text.`; - - const response = await fetch( - "https://api.groq.com/openai/v1/chat/completions", - { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${groqApiKey}`, - }, - body: JSON.stringify({ - messages: [ - { role: "system", content: prompt }, - { role: "user", content: text }, - ], - model: "meta-llama/llama-4-scout-17b-16e-instruct", - temperature: 0.7, - max_tokens: 1000, - }), - } - ); - - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error(errorData.error?.message || `API error: ${response.status}`); - } + Provide a clear and detailed explanation of the given text. + Make it easy to understand and use ${ + level === "moreHindi" + ? "more Hindi words" + : level === "moreEnglish" + ? "more English words" + : "a balanced mix of Hindi and English words" + }. + Format your response in a clear, structured way with bullet points or short paragraphs. + Only respond with the explanation, no additional text.`; + + const payload = { + messages: [ + { role: "system", content: prompt }, + { role: "user", content: text }, + ], + model: "meta-llama/llama-4-scout-17b-16e-instruct", + temperature: 0.7, + max_tokens: 1000, + }; - const data = await response.json(); - const explanation = data.choices[0].message.content.trim(); + // Retry loop + for (let attempt = 0; attempt <= retries; attempt++) { + try { + const response = await fetch( + "https://api.groq.com/openai/v1/chat/completions", + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${groqApiKey}`, + }, + body: JSON.stringify(payload), + } + ); + + // If response not OK + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + + // Retry only if server error (5xx) + if (response.status >= 500 && attempt < retries) { + console.warn(`Retrying explainText (attempt ${attempt + 1})`); + continue; + } + + throw new Error(errorData.error?.message || `API error: ${response.status}`); + } + + // Successful response + const data = await response.json(); + const explanation = data.choices[0].message.content.trim(); + + if (!explanation) { + throw new Error("Empty explanation received"); + } + + return explanation; - if (!explanation) { - throw new Error("Empty explanation received"); + } catch (error) { + // Retry on failure, unless it's the last attempt + if (attempt === retries) { + throw error; + } + console.warn(`explainText failed on attempt ${attempt + 1}: ${error.message}`); + } } - - return explanation; } + // Function to show loading popup function showLoadingPopup() { const popup = document.createElement("div");