From 8580148dd5182213dc2a8c643f360cfd6c142890 Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Mon, 26 May 2025 19:18:09 +0000
Subject: [PATCH] feat: Integrate Google Gemini API support
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.
---
src/background/index.mjs | 4 ++
src/config/index.mjs | 13 ++++++
src/popup/sections/GeneralPart.jsx | 13 ++++++
src/services/apis/gemini-api.mjs | 73 ++++++++++++++++++++++++++++++
4 files changed, 103 insertions(+)
create mode 100644 src/services/apis/gemini-api.mjs
diff --git a/src/background/index.mjs b/src/background/index.mjs
index 6fde2d48..bd0f87ee 100644
--- a/src/background/index.mjs
+++ b/src/background/index.mjs
@@ -33,6 +33,7 @@ import {
isUsingClaudeWebModel,
isUsingMoonshotApiModel,
isUsingMoonshotWebModel,
+ isUsingGeminiApiModel,
} from '../config/index.mjs'
import '../_locales/i18n'
import { openUrl } from '../utils/open-url'
@@ -49,6 +50,7 @@ import { generateAnswersWithBardWebApi } from '../services/apis/bard-web.mjs'
import { generateAnswersWithClaudeWebApi } from '../services/apis/claude-web.mjs'
import { generateAnswersWithMoonshotCompletionApi } from '../services/apis/moonshot-api.mjs'
import { generateAnswersWithMoonshotWebApi } from '../services/apis/moonshot-web.mjs'
+import { generateAnswersWithGeminiApi } from '../services/apis/gemini-api.mjs' // Added import
import { isUsingModelName } from '../utils/model-name-convert.mjs'
function setPortProxy(port, proxyTabId) {
@@ -140,6 +142,8 @@ async function executeApi(session, port, config) {
session,
config.moonshotApiKey,
)
+ } else if (isUsingGeminiApiModel(session)) { // Added Gemini condition
+ await generateAnswersWithGeminiApi(port, session.question, session)
} else if (isUsingChatGLMApiModel(session)) {
await generateAnswersWithChatGLMApi(port, session.question, session)
} else if (isUsingOllamaApiModel(session)) {
diff --git a/src/config/index.mjs b/src/config/index.mjs
index 1322216d..dee243bd 100644
--- a/src/config/index.mjs
+++ b/src/config/index.mjs
@@ -88,6 +88,7 @@ export const poeWebModelKeys = [
'poeAiWeb_Llama_2_70b',
]
export const moonshotApiModelKeys = ['moonshot_v1_8k', 'moonshot_v1_32k', 'moonshot_v1_128k']
+export const geminiApiModelKeys = ['geminiApiPro']
export const AlwaysCustomGroups = [
'ollamaApiModelKeys',
@@ -130,6 +131,10 @@ export const ModelGroups = {
value: moonshotApiModelKeys,
desc: 'Kimi.Moonshot (API)',
},
+ geminiApiModelKeys: {
+ value: geminiApiModelKeys,
+ desc: 'Gemini (API)',
+ },
chatglmApiModelKeys: {
value: chatglmApiModelKeys,
desc: 'ChatGLM (API)',
@@ -267,6 +272,8 @@ export const Models = {
value: 'moonshot-v1-128k',
desc: 'Kimi.Moonshot (128k)',
},
+
+ geminiApiPro: { value: 'gemini-pro', desc: 'Gemini (API, Pro)' },
}
for (const modelName in Models) {
@@ -317,6 +324,7 @@ export const defaultConfig = {
claudeApiKey: '',
chatglmApiKey: '',
moonshotApiKey: '',
+ geminiApiKey: '',
customApiKey: '',
@@ -366,6 +374,7 @@ export const defaultConfig = {
'bingFree4',
'moonshotWebFree',
'moonshot_v1_8k',
+ 'geminiApiPro', // Added Gemini API model
'chatglmTurbo',
'customModel',
'azureOpenAi',
@@ -508,6 +517,10 @@ export function isUsingMoonshotApiModel(configOrSession) {
return isInApiModeGroup(moonshotApiModelKeys, configOrSession)
}
+export function isUsingGeminiApiModel(configOrSession) {
+ return isInApiModeGroup(geminiApiModelKeys, configOrSession)
+}
+
export function isUsingChatGLMApiModel(configOrSession) {
return isInApiModeGroup(chatglmApiModelKeys, configOrSession)
}
diff --git a/src/popup/sections/GeneralPart.jsx b/src/popup/sections/GeneralPart.jsx
index 3db270ce..1e791ad0 100644
--- a/src/popup/sections/GeneralPart.jsx
+++ b/src/popup/sections/GeneralPart.jsx
@@ -21,6 +21,7 @@ import {
ThemeMode,
TriggerMode,
isUsingMoonshotApiModel,
+ isUsingGeminiApiModel, // Added import
Models,
} from '../../config/index.mjs'
import Browser from 'webextension-polyfill'
@@ -331,6 +332,18 @@ export function GeneralPart({ config, updateConfig, setTabIndex }) {
)}
)}
+ {isUsingGeminiApiModel(config) && (
+ {
+ const apiKey = e.target.value
+ updateConfig({ geminiApiKey: apiKey })
+ }}
+ />
+ )}
{isUsingSpecialCustomModel(config) && (
({ 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);
+ }
+
+ pushRecord(session, question, answer);
+ // console.debug('Gemini conversation history', { content: session.conversationRecords });
+ port.postMessage({ answer: answer, done: true, session: session });
+
+ } catch (err) {
+ console.error('Error in generateAnswersWithGeminiApi:', err);
+ port.postMessage({ error: err.message || 'Failed to communicate with Gemini API.', done: true, session });
+ }
+}