chatGPTBox icon indicating copy to clipboard operation
chatGPTBox copied to clipboard

feat: Integrate Google Gemini API support

Open canweb221 opened this issue 6 months ago • 4 comments

Adds support for using the Google Gemini API via an API key.

Key changes include:

  • Configuration:

    • Added geminiApiKey to defaultConfig in src/config/index.mjs.
    • Defined geminiApiModelKeys and added 'Gemini (API, Pro)' to Models and ModelGroups.
    • Added 'geminiApiPro' to the default activeApiModes.
  • API Client:

    • Created src/services/apis/gemini-api.mjs with logic to connect to the Gemini API using the configured API key. Includes placeholder for the exact API endpoint and payload structure.
  • User Interface:

    • Added an input field in src/popup/sections/GeneralPart.jsx for you to enter your Gemini API key. This field is conditionally displayed when a Gemini API model is active.
  • Integration:

    • Updated src/background/index.mjs to route requests to the generateAnswersWithGeminiApi client when a Gemini model is selected.

Manual testing with a valid Gemini API key is required to fully verify the functionality.

canweb221 avatar May 26 '25 19:05 canweb221

/review

PeterDaveHello avatar Jun 02 '25 18:06 PeterDaveHello

Qodo Merge was enabled for this repository. To continue using it, please link your Git account with your Qodo account here.

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 No relevant tests
🔒 Security concerns

Sensitive information exposure:
The PR adds a new API key field for Gemini in the UI. While the input is properly set as type="password" to mask the key visually, there should be additional checks to ensure the API key is stored securely and not exposed in logs or network requests (beyond the authenticated API call itself). The current implementation directly includes the API key as a query parameter in line 38, which could expose it in server logs. Consider using an Authorization header instead.

⚡ Recommended focus areas for review

Error Handling

The error handling in the Gemini API client could be improved. The code attempts to extract error messages from the response but may not handle all error scenarios correctly, especially when the response structure is different than expected.

if (!response.ok) {
  const errorData = await response.json().catch(() => ({ message: response.statusText }));
  console.error('Gemini API error:', errorData);
  port.postMessage({ error: `Gemini API error: ${errorData.error?.message || errorData.message || 'Unknown error'}`, done: true, session });
  return;
}
API Implementation

The Gemini API implementation is using placeholder structures that need verification against the actual API documentation. The payload structure and response parsing logic may need adjustments to match Google's Gemini API specifications.

// Construct the request payload
// This is a placeholder structure and needs to be verified against Gemini API documentation
const payload = {
  contents: [{
    parts: [{
      text: question,
    }],
  }],
  // generationConfig: { // Optional: configure temperature, maxOutputTokens, etc.
  //   temperature: config.temperature,
  //   maxOutputTokens: config.maxResponseTokenLength,
  // },
  // safetySettings: [ // Optional: configure safety settings
  //   { category: 'HARM_CATEGORY_HARASSMENT', threshold: 'BLOCK_MEDIUM_AND_ABOVE' },
  //   { category: 'HARM_CATEGORY_HATE_SPEECH', threshold: 'BLOCK_MEDIUM_AND_ABOVE' },
  //   { category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT', threshold: 'BLOCK_MEDIUM_AND_ABOVE' },
  //   { category: 'HARM_CATEGORY_DANGEROUS_CONTENT', threshold: 'BLOCK_MEDIUM_AND_ABOVE' },
  // ],
};

const response = await fetch(`${GEMINI_API_ENDPOINT}?key=${apiKey}`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(payload),
});

if (!response.ok) {
  const errorData = await response.json().catch(() => ({ message: response.statusText }));
  console.error('Gemini API error:', errorData);
  port.postMessage({ error: `Gemini API error: ${errorData.error?.message || errorData.message || 'Unknown error'}`, done: true, session });
  return;
}

const responseData = await response.json();

// Extract the answer from the responseData
// This is a placeholder and needs to be verified against actual Gemini API response structure
// Expected structure: responseData.candidates[0].content.parts[0].text
let answer = 'No response from Gemini API.';
if (responseData.candidates && responseData.candidates[0] && responseData.candidates[0].content && responseData.candidates[0].content.parts && responseData.candidates[0].content.parts[0]) {
  answer = responseData.candidates[0].content.parts[0].text;
} else {
    console.error('Unexpected Gemini API response structure:', responseData);
}
Missing Conversation History

The implementation doesn't appear to handle conversation history properly. Unlike other API implementations, it doesn't seem to send previous messages in the conversation to maintain context.

const payload = {
  contents: [{
    parts: [{
      text: question,
    }],
  }],
  // generationConfig: { // Optional: configure temperature, maxOutputTokens, etc.
  //   temperature: config.temperature,
  //   maxOutputTokens: config.maxResponseTokenLength,
  // },
  // safetySettings: [ // Optional: configure safety settings
  //   { category: 'HARM_CATEGORY_HARASSMENT', threshold: 'BLOCK_MEDIUM_AND_ABOVE' },
  //   { category: 'HARM_CATEGORY_HATE_SPEECH', threshold: 'BLOCK_MEDIUM_AND_ABOVE' },
  //   { category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT', threshold: 'BLOCK_MEDIUM_AND_ABOVE' },
  //   { category: 'HARM_CATEGORY_DANGEROUS_CONTENT', threshold: 'BLOCK_MEDIUM_AND_ABOVE' },
  // ],
};

qodo-merge-pro[bot] avatar Jun 02 '25 18:06 qodo-merge-pro[bot]

/improve

PeterDaveHello avatar Jun 02 '25 18:06 PeterDaveHello

Qodo Merge was enabled for this repository. To continue using it, please link your Git account with your Qodo account here.

PR Code Suggestions ✨

CategorySuggestion                                                                                                                                    Impact
Possible issue
Add conversation history support

Include conversation history in the Gemini API request to maintain context
across messages. The current implementation only sends the current question
without previous context, which will result in the AI not remembering previous
interactions.

src/services/apis/gemini-api.mjs [20-36]

 const payload = {
-  contents: [{
-    parts: [{
-      text: question,
-    }],
-  }],
+  contents: [
+    ...session.conversationRecords.map(record => ({
+      parts: [{ text: record.question }, { text: record.answer }]
+    })),
+    {
+      parts: [{
+        text: question,
+      }],
+    }
+  ],
   // generationConfig: { // Optional: configure temperature, maxOutputTokens, etc.
   //   temperature: config.temperature,
   //   maxResponseTokenLength: config.maxResponseTokenLength,
   // },
   // safetySettings: [ // Optional: configure safety settings
   //   { category: 'HARM_CATEGORY_HARASSMENT', threshold: 'BLOCK_MEDIUM_AND_ABOVE' },
   //   { category: 'HARM_CATEGORY_HATE_SPEECH', threshold: 'BLOCK_MEDIUM_AND_ABOVE' },
   //   { category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT', threshold: 'BLOCK_MEDIUM_AND_ABOVE' },
   //   { category: 'HARM_CATEGORY_DANGEROUS_CONTENT', threshold: 'BLOCK_MEDIUM_AND_ABOVE' },
   // ],
 };
  • [ ] Apply / Chat
Suggestion importance[1-10]: 7

__

Why: This addresses a significant functional issue where the API would lose conversation context between messages. Including session.conversationRecords in the payload is important for maintaining chat continuity.

Medium
Use dynamic model selection

Use the model specified in the session rather than hardcoding 'gemini-pro'. This
ensures the correct model is used when multiple Gemini models are supported in
the future, maintaining consistency with how other APIs are implemented.

src/services/apis/gemini-api.mjs [6]

-const GEMINI_API_ENDPOINT = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent';
+const GEMINI_API_ENDPOINT = `https://generativelanguage.googleapis.com/v1beta/models/${session.modelName}:generateContent`;
  • [ ] Apply / Chat
Suggestion importance[1-10]: 6

__

Why: Good suggestion for future extensibility to support multiple Gemini models. Using session.modelName instead of hardcoding gemini-pro aligns with the architecture of other API implementations in the codebase.

Low
Fix configuration parameters

Uncomment and fix the generationConfig section to properly use the user's
configuration settings. The Gemini API uses maxOutputTokens but the code
incorrectly references maxResponseTokenLength.

src/services/apis/gemini-api.mjs [26-29]

-// generationConfig: { // Optional: configure temperature, maxOutputTokens, etc.
-//   temperature: config.temperature,
-//   maxResponseTokenLength: config.maxResponseTokenLength,
-// },
+generationConfig: {
+  temperature: config.temperature,
+  maxOutputTokens: config.maxTokens || 4096,
+},
  • [ ] Apply / Chat
Suggestion importance[1-10]: 5

__

Why: Correctly identifies that maxOutputTokens is the proper Gemini API parameter name, but assumes config.temperature and config.maxTokens exist when they're not defined in the PR's defaultConfig.

Low
  • [ ] More

qodo-merge-pro[bot] avatar Jun 02 '25 18:06 qodo-merge-pro[bot]