Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 116 additions & 94 deletions background.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -176,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",
Expand All @@ -190,56 +189,66 @@ 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,
}),
}
);
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,
};

if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(
errorData.error?.message || `API error: ${response.status}`
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),
}
);
}

const data = await response.json();
const translatedText = data.choices[0].message.content.trim();
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));

if (!translatedText) {
throw new Error("Empty translation received");
}
// Retry only on server (5xx) errors
if (response.status >= 500 && attempt < retries) {
console.warn(`Retrying translateText (attempt ${attempt + 1})`);
continue;
}

return translatedText;
} catch (error) {
console.error("Translation error:", error);
throw error;
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;

} catch (error) {
if (attempt === retries) {
throw error;
}
console.warn(`translateText failed on attempt ${attempt + 1}: ${error.message}`);
}
}
Comment on lines +202 to 245
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Implement exponential backoff and handle network errors.

The retry logic correctly handles 5xx server errors, but could be enhanced for better resilience:

  1. Missing exponential backoff: Rapid successive retries may overwhelm the server or hit rate limits
  2. Network errors not handled: Fetch exceptions (network timeouts, connection issues) don't trigger retries

Consider this enhanced implementation:

  for (let attempt = 0; attempt <= retries; attempt++) {
    try {
+     // Add exponential backoff delay (except for first attempt)
+     if (attempt > 0) {
+       const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000);
+       await new Promise(resolve => setTimeout(resolve, delay));
+     }
+
      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;

    } catch (error) {
+     // Handle network errors (fetch exceptions) for retries
+     const isNetworkError = error.name === 'TypeError' || 
+                           error.message.includes('fetch') ||
+                           error.message.includes('network');
+     
+     if ((isNetworkError || error.message.includes('5')) && attempt < retries) {
+       console.warn(`translateText failed on attempt ${attempt + 1}: ${error.message}`);
+       continue;
+     }
+
      if (attempt === retries) {
        throw error;
      }
-     console.warn(`translateText failed on attempt ${attempt + 1}: ${error.message}`);
    }
  }
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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),
}
);
}
const data = await response.json();
const translatedText = data.choices[0].message.content.trim();
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
if (!translatedText) {
throw new Error("Empty translation received");
}
// Retry only on server (5xx) errors
if (response.status >= 500 && attempt < retries) {
console.warn(`Retrying translateText (attempt ${attempt + 1})`);
continue;
}
return translatedText;
} catch (error) {
console.error("Translation error:", error);
throw error;
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;
} catch (error) {
if (attempt === retries) {
throw error;
}
console.warn(`translateText failed on attempt ${attempt + 1}: ${error.message}`);
}
}
for (let attempt = 0; attempt <= retries; attempt++) {
try {
// Add exponential backoff delay (except for first attempt)
if (attempt > 0) {
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000);
await new Promise(resolve => setTimeout(resolve, delay));
}
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;
} catch (error) {
// Handle network errors (fetch exceptions) for retries
const isNetworkError =
error.name === 'TypeError' ||
error.message.includes('fetch') ||
error.message.includes('network');
if ((isNetworkError || error.message.includes('5')) && attempt < retries) {
console.warn(`translateText failed on attempt ${attempt + 1}: ${error.message}`);
continue;
}
if (attempt === retries) {
throw error;
}
}
}
πŸ€– Prompt for AI Agents
In background.js between lines 202 and 245, the retry logic for the fetch call
lacks exponential backoff and does not handle network errors properly. To fix
this, implement an exponential backoff delay before each retry attempt to space
out retries progressively. Also, ensure that any fetch exceptions (network
errors) are caught and trigger a retry, not just server 5xx responses. Add a
delay function and call it with an increasing timeout before continuing retries,
and wrap the fetch call in a try-catch that retries on both network errors and
server errors.

}



// 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",
Expand All @@ -251,68 +260,81 @@ 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"
}.
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.`;

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,
}),
}
);
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,
};

if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(
errorData.error?.message || `API error: ${response.status}`
// 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),
}
);
}

const data = await response.json();
const explanation = data.choices[0].message.content.trim();
// If response not OK
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));

if (!explanation) {
throw new Error("Empty explanation received");
}
// 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();

return explanation;
} catch (error) {
console.error("Explanation error:", error);
throw error;
if (!explanation) {
throw new Error("Empty explanation received");
}

return explanation;

} 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}`);
}
}
Comment on lines +289 to 333
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Apply the same retry improvements as translateText.

The retry logic correctly handles 5xx server errors but has the same enhancement opportunities as the translateText function:

  1. Missing exponential backoff: Rapid successive retries may overwhelm the server
  2. Network errors not handled: Fetch exceptions don't trigger retries

Apply the same exponential backoff and network error handling improvements as suggested for the translateText function to maintain consistency between both functions.

πŸ€– Prompt for AI Agents
In background.js from lines 289 to 333, the retry logic for the explainText
function lacks exponential backoff and does not handle network errors properly.
To fix this, implement an exponential backoff delay before each retry attempt to
avoid overwhelming the server, and ensure that fetch exceptions (network errors)
trigger retries as well. This will align the retry behavior with the
improvements made in the translateText function for consistency and robustness.

}



// Function to show loading popup
function showLoadingPopup() {
const popup = document.createElement("div");
Expand Down