Skip to content

Commit 7ba5bbd

Browse files
authored
Merge pull request #22 from dot-mario/main
✨ feat(config): add gemini support as ai provider
2 parents 1a5f8e8 + bfc658a commit 7ba5bbd

7 files changed

Lines changed: 229 additions & 35 deletions

File tree

README.md

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Use Azure/OpenAI API to review Git changes, generate conventional commit message
2727

2828
## ✨ Features
2929

30-
- 🤯 Support generating commit messages based on git diffs using ChatGPT / Azure API.
30+
- 🤯 Support generating commit messages based on git diffs using ChatGPT / Azure API and Gemini API.
3131
- 🗺️ Support multi-language commit messages.
3232
- 😜 Support adding Gitmoji.
3333
- 🛠️ Support custom system prompt.
@@ -59,15 +59,19 @@ Use Azure/OpenAI API to review Git changes, generate conventional commit message
5959
6060
In the VSCode settings, locate the "ai-commit" configuration options and configure them as needed:
6161

62-
| Configuration | Type | Default | Required | Notes |
63-
| :----------------- | :----: | :-----: | :------: | :-----------------------------------------------------------------------------------------------------: |
64-
| OPENAI_API_KEY | string | None | Yes | [OpenAI token](https://platform.openai.com/account/api-keys) |
65-
| OPENAI_BASE_URL | string | None | No | If using Azure, use: https://{resource}.openai.azure.com/openai/deployments/{model} |
66-
| OPENAI_MODEL | string | gpt-4o | Yes | OpenAI MODEL,you can select a model from the list by running the `Show Available OpenAI Models` command |
67-
| AZURE_API_VERSION | string | None | No | AZURE_API_VERSION |
68-
| AI_COMMIT_LANGUAGE | string | en | Yes | Supports 19 languages |
69-
| SYSTEM_PROMPT | string | None | No | Custom system prompt |
70-
| OPENAI_TEMPERATURE | number | 0.7 | No | Controls randomness in the output. Range: 0-2. Lower values: more focused, Higher values: more creative |
62+
| Configuration | Type | Default | Required | Notes |
63+
| :----------------- | :----: | :------------------: | :------: | :----------------------------------------------------------------------------------------------------------------: |
64+
| AI_PROVIDER | string | openai | Yes | Select AI Provider: `openai` or `gemini`. |
65+
| OPENAI_API_KEY | string | None | Yes | Required when `AI Provider` is set to `OpenAI`. [OpenAI token](https://platform.openai.com/account/api-keys) |
66+
| OPENAI_BASE_URL | string | None | No | If using Azure, use: https://{resource}.openai.azure.com/openai/deployments/{model} |
67+
| OPENAI_MODEL | string | gpt-4o | Yes | OpenAI MODEL, you can select a model from the list by running the `Show Available OpenAI Models` command |
68+
| AZURE_API_VERSION | string | None | No | AZURE_API_VERSION |
69+
| OPENAI_TEMPERATURE | number | 0.7 | No | Controls randomness in the output. Range: 0-2. Lower values: more focused, Higher values: more creative |
70+
| GEMINI_API_KEY | string | None | Yes | Required when `AI Provider` is set to `Gemini`. [Gemini API key](https://makersuite.google.com/app/apikey) |
71+
| GEMINI_MODEL | string | gemini-2.0-flash-001 | Yes | Gemini MODEL. Currently, model selection is limited to configuration. |
72+
| GEMINI_TEMPERATURE | number | 0.7 | No | Controls randomness in the output. Range: 0-2 for Gemini. Lower values: more focused, Higher values: more creative |
73+
| AI_COMMIT_LANGUAGE | string | en | Yes | Supports 19 languages |
74+
| SYSTEM_PROMPT | string | None | No | Custom system prompt |
7175

7276
## ⌨️ Local Development
7377

package-lock.json

Lines changed: 12 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,32 @@
124124
"minimum": 0,
125125
"maximum": 2,
126126
"description": "OpenAI temperature setting (0-2). Higher values make output more random, lower values more deterministic."
127+
},
128+
"ai-commit.AI_PROVIDER": {
129+
"type": "string",
130+
"default": "openai",
131+
"description": "AI Provider to use (OpenAI or Gemini)",
132+
"enum": [
133+
"openai",
134+
"gemini"
135+
]
136+
},
137+
"ai-commit.GEMINI_API_KEY": {
138+
"type": "string",
139+
"default": "",
140+
"description": "Gemini API Key"
141+
},
142+
"ai-commit.GEMINI_MODEL": {
143+
"type": "string",
144+
"default": "gemini-2.0-flash-001",
145+
"description": "Gemini Model to use"
146+
},
147+
"ai-commit.GEMINI_TEMPERATURE": {
148+
"type": "number",
149+
"default": 0.7,
150+
"minimum": 0,
151+
"maximum": 2,
152+
"description": "Gemini temperature setting (0-2). Controls randomness."
127153
}
128154
},
129155
"title": "AI Commit"
@@ -143,9 +169,9 @@
143169
"compile": "webpack",
144170
"compile-tests": "tsc -p . --outDir out",
145171
"lint": "eslint src --ext ts",
146-
"package": "npm vsce package --no-dependencies",
172+
"package": "vsce package --no-dependencies",
147173
"pretest": "npm run compile-tests && npm run compile && npm run lint",
148-
"publish": "npm vsce publish --no-dependencies",
174+
"publish": "vsce publish --no-dependencies",
149175
"test": "node ./out/test/runTest.js",
150176
"vscode:prepublish": "npm run build",
151177
"watch": "webpack --watch",
@@ -169,9 +195,10 @@
169195
"webpack-cli": "^5.0.1"
170196
},
171197
"dependencies": {
198+
"@google/generative-ai": "^0.21.0",
199+
"fs-extra": "^11.0.4",
172200
"openai": "^4.14.2",
173-
"simple-git": "^3.17.0",
174-
"fs-extra": "^11.0.4"
201+
"simple-git": "^3.17.0"
175202
},
176203
"resolutions": {
177204
"@types/node": "16.x"

src/commands.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export class CommandManager {
1919
// Show available OpenAI models
2020
this.registerCommand('ai-commit.showAvailableModels', async () => {
2121
const configManager = ConfigurationManager.getInstance();
22-
const models = await configManager.getAvailableModels();
22+
const models = await configManager.getAvailableOpenAIModels();
2323
const selected = await vscode.window.showQuickPick(models, {
2424
placeHolder: 'Please select a model'
2525
});
@@ -29,6 +29,27 @@ export class CommandManager {
2929
await config.update('OPENAI_MODEL', selected, vscode.ConfigurationTarget.Global);
3030
}
3131
});
32+
33+
/**
34+
* @deprecated
35+
* This function is deprecated because Gemini API does not currently support listing models via API.
36+
*
37+
* Show available Gemini models
38+
*/
39+
/*
40+
this.registerCommand('ai-commit.showAvailableGeminiModels', async () => {
41+
const configManager = ConfigurationManager.getInstance();
42+
const models = await configManager.getAvailableGeminiModels(); // Use the updated function
43+
const selected = await vscode.window.showQuickPick(models, {
44+
placeHolder: 'Please select a Gemini model'
45+
});
46+
47+
if (selected) {
48+
const config = vscode.workspace.getConfiguration('ai-commit');
49+
await config.update('GEMINI_MODEL', selected, vscode.ConfigurationTarget.Global);
50+
}
51+
});
52+
*/
3253
}
3354

3455
private registerCommand(command: string, handler: (...args: any[]) => any) {

src/config.ts

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as vscode from 'vscode';
22
import { createOpenAIApi } from './openai-utils';
3+
import { createGeminiAPIClient } from './gemini-utils';
34

45
/**
56
* Configuration keys used in the AI commit extension.
@@ -19,7 +20,12 @@ export enum ConfigKeys {
1920
AZURE_API_VERSION = 'AZURE_API_VERSION',
2021
AI_COMMIT_LANGUAGE = 'AI_COMMIT_LANGUAGE',
2122
SYSTEM_PROMPT = 'AI_COMMIT_SYSTEM_PROMPT',
22-
OPENAI_TEMPERATURE = 'OPENAI_TEMPERATURE'
23+
OPENAI_TEMPERATURE = 'OPENAI_TEMPERATURE',
24+
25+
GEMINI_API_KEY = 'GEMINI_API_KEY',
26+
GEMINI_MODEL = 'GEMINI_MODEL',
27+
GEMINI_TEMPERATURE = 'GEMINI_TEMPERATURE',
28+
AI_PROVIDER = 'AI_PROVIDER',
2329
}
2430

2531
/**
@@ -39,7 +45,7 @@ export class ConfigurationManager {
3945

4046
if (event.affectsConfiguration('ai-commit.OPENAI_BASE_URL') ||
4147
event.affectsConfiguration('ai-commit.OPENAI_API_KEY')) {
42-
this.updateModelList();
48+
this.updateOpenAIModelList();
4349
}
4450
}
4551
});
@@ -67,13 +73,13 @@ export class ConfigurationManager {
6773
/**
6874
* Updates the list of available OpenAI models.
6975
*/
70-
private async updateModelList() {
76+
private async updateOpenAIModelList() {
7177
try {
7278
const openai = createOpenAIApi();
7379
const models = await openai.models.list();
7480

7581
// Save available models to extension state
76-
await this.context.globalState.update('availableModels', models.data.map(model => model.id));
82+
await this.context.globalState.update('availableOpenAIModels', models.data.map(model => model.id));
7783

7884
// Get the current selected model
7985
const config = vscode.workspace.getConfiguration('ai-commit');
@@ -91,12 +97,60 @@ export class ConfigurationManager {
9197

9298
/**
9399
* Retrieves the list of available OpenAI models.
94-
* @returns {Promise<string[]>} The list of available models.
100+
* @returns {Promise<string[]>} The list of available OpenAI models.
95101
*/
96-
public async getAvailableModels(): Promise<string[]> {
97-
if (!this.context.globalState.get<string[]>('availableModels')) {
98-
await this.updateModelList();
102+
public async getAvailableOpenAIModels(): Promise<string[]> {
103+
if (!this.context.globalState.get<string[]>('availableOpenAIModels')) {
104+
await this.updateOpenAIModelList();
99105
}
100-
return this.context.globalState.get<string[]>('availableModels', []);
106+
return this.context.globalState.get<string[]>('availableOpenAIModels', []);
101107
}
108+
109+
/**
110+
* @deprecated
111+
* This function is deprecated because Gemini API does not currently support listing models via API.
112+
* We have to wait for this feature to be updated to the gemini library at some point, or find another way.
113+
*
114+
* Updates the list of available Gemini models.
115+
*/
116+
/*
117+
private async updateGeminiModelList() {
118+
try {
119+
const geminiAPI = createGeminiAPIClient();
120+
const modelListResponse = await geminiAPI.listModels(); // Gemini API does not currently have a function to get a list of models
121+
const availableModels = modelListResponse.models.map(model => model.name);
122+
123+
// Save available Gemini models to extension global state
124+
await this.context.globalState.update('availableGeminiModels', availableModels);
125+
126+
// Get the currently selected Gemini model
127+
const config = vscode.workspace.getConfiguration('ai-commit');
128+
const currentModel = config.get<string>('GEMINI_MODEL');
129+
130+
// If the current selected Gemini model is not in the available list, set it to a default value
131+
if (currentModel && !availableModels.includes(currentModel)) {
132+
await config.update('GEMINI_MODEL', 'gemini-2.0-flash-001', vscode.ConfigurationTarget.Global);
133+
}
134+
135+
} catch (error) {
136+
console.error('Failed to fetch Gemini models:', error);
137+
}
138+
}
139+
*/
140+
141+
/**
142+
* @deprecated
143+
* This function is deprecated because Gemini API does not currently support listing models via API.
144+
*
145+
* Retrieves the list of available Gemini models.
146+
* @returns {Promise<string[]>} The list of available Gemini models.
147+
*/
148+
/*
149+
public async getAvailableGeminiModels(): Promise<string[]> {
150+
if (!this.context.globalState.get<string[]>('availableGeminiModels')) {
151+
await this.updateGeminiModelList();
152+
}
153+
return this.context.globalState.get<string[]>('availableGeminiModels', []);
154+
}
155+
*/
102156
}

src/gemini-utils.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { GoogleGenerativeAI } from "@google/generative-ai";
2+
import { ConfigKeys, ConfigurationManager } from './config';
3+
4+
/**
5+
* Creates and returns a Gemini API configuration object.
6+
* @returns {Object} - The Gemini API configuration object.
7+
* @throws {Error} - Throws an error if the API key is missing or empty.
8+
*/
9+
function getGeminiConfig() {
10+
const configManager = ConfigurationManager.getInstance();
11+
const apiKey = configManager.getConfig<string>(ConfigKeys.GEMINI_API_KEY);
12+
13+
if (!apiKey) {
14+
throw new Error('The GEMINI_API_KEY environment variable is missing or empty.');
15+
}
16+
17+
const config: {
18+
apiKey: string;
19+
} = {
20+
apiKey
21+
};
22+
23+
return config;
24+
}
25+
26+
/**
27+
* Creates and returns a Gemini API instance.
28+
* @returns {GoogleGenerativeAI} - The Gemini API instance.
29+
*/
30+
export function createGeminiAPIClient() {
31+
const config = getGeminiConfig();
32+
return new GoogleGenerativeAI(config.apiKey);
33+
}
34+
35+
/**
36+
* Sends a chat completion request to the Gemini API.
37+
* @param {any[]} messages - The messages to send to the API.
38+
* @returns {Promise<string>} - A promise that resolves to the API response.
39+
*/
40+
export async function GeminiAPI(messages: any[]) {
41+
try {
42+
const gemini = createGeminiAPIClient();
43+
const configManager = ConfigurationManager.getInstance();
44+
const modelName = configManager.getConfig<string>(ConfigKeys.GEMINI_MODEL);
45+
const temperature = configManager.getConfig<number>(ConfigKeys.GEMINI_TEMPERATURE, 0.7);
46+
47+
const model = gemini.getGenerativeModel({ model: modelName });
48+
const chat = model.startChat({
49+
generationConfig: {
50+
temperature: temperature,
51+
},
52+
});
53+
54+
const result = await chat.sendMessage(messages.map(msg => msg.content));
55+
const response = result.response;
56+
const text = response.text();
57+
58+
return text;
59+
60+
} catch (error) {
61+
console.error('Gemini API call failed:', error);
62+
throw error;
63+
}
64+
}

0 commit comments

Comments
 (0)