* 📝 (README.md): add support for custom AI models and update documentation to reflect new environment variable OCO_ AI_PROVIDER (#351)

* feat/add gemini (#349)

* fix: prompt-module/@commitlint (#336)

* docs: spelling fix (#325)

---------

Co-authored-by: tumf <y.takahara@gmail.com>
Co-authored-by: Drew Payment <drew.payment@gmail.com>
Co-authored-by: Takanori Matsumoto <matscube@gmail.com>
Co-authored-by: Kellan Stevens <kellan@kellanstevens.com>
Co-authored-by: JMN09 <jmn09@mail.aub.edu>
Co-authored-by: JMN09 <157629053+JMN09@users.noreply.github.com>
This commit is contained in:
GPT10
2024-08-18 14:55:33 +03:00
committed by GitHub
parent 07f7a05c4b
commit f3371ac1e3
8 changed files with 959 additions and 768 deletions
+436 -381
View File
File diff suppressed because it is too large Load Diff
+424 -372
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "opencommit", "name": "opencommit",
"version": "3.0.17", "version": "3.0.18",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "opencommit", "name": "opencommit",
"version": "3.0.17", "version": "3.0.18",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/core": "^1.10.0", "@actions/core": "^1.10.0",
+4 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "opencommit", "name": "opencommit",
"version": "3.0.17", "version": "3.0.18",
"description": "Auto-generate impressive commits in 1 second. Killing lame commits with AI 🤯🔫", "description": "Auto-generate impressive commits in 1 second. Killing lame commits with AI 🤯🔫",
"keywords": [ "keywords": [
"git", "git",
@@ -98,5 +98,8 @@
"ini": "^3.0.1", "ini": "^3.0.1",
"inquirer": "^9.1.4", "inquirer": "^9.1.4",
"openai": "^3.2.1" "openai": "^3.2.1"
},
"overrides": {
"whatwg-url": "13.0.0"
} }
} }
+46 -11
View File
@@ -33,7 +33,9 @@ export enum CONFIG_KEYS {
OCO_AZURE_ENDPOINT = 'OCO_AZURE_ENDPOINT', OCO_AZURE_ENDPOINT = 'OCO_AZURE_ENDPOINT',
OCO_TEST_MOCK_TYPE = 'OCO_TEST_MOCK_TYPE', OCO_TEST_MOCK_TYPE = 'OCO_TEST_MOCK_TYPE',
OCO_API_URL = 'OCO_API_URL', OCO_API_URL = 'OCO_API_URL',
OCO_OLLAMA_API_URL = 'OCO_OLLAMA_API_URL' OCO_OLLAMA_API_URL = 'OCO_OLLAMA_API_URL',
OCO_FLOWISE_ENDPOINT = 'OCO_FLOWISE_ENDPOINT',
OCO_FLOWISE_API_KEY = 'OCO_FLOWISE_API_KEY'
} }
export enum CONFIG_MODES { export enum CONFIG_MODES {
@@ -130,7 +132,8 @@ export const configValidators = {
config.OCO_ANTHROPIC_API_KEY || config.OCO_ANTHROPIC_API_KEY ||
config.OCO_AI_PROVIDER.startsWith('ollama') || config.OCO_AI_PROVIDER.startsWith('ollama') ||
config.OCO_AZURE_API_KEY || config.OCO_AZURE_API_KEY ||
config.OCO_AI_PROVIDER == 'test', config.OCO_AI_PROVIDER == 'test' ||
config.OCO_AI_PROVIDER == 'flowise',
'You need to provide an OpenAI/Anthropic/Azure API key' 'You need to provide an OpenAI/Anthropic/Azure API key'
); );
validateConfig( validateConfig(
@@ -149,7 +152,8 @@ export const configValidators = {
config.OCO_OPENAI_API_KEY || config.OCO_OPENAI_API_KEY ||
config.OCO_AZURE_API_KEY || config.OCO_AZURE_API_KEY ||
config.OCO_AI_PROVIDER == 'ollama' || config.OCO_AI_PROVIDER == 'ollama' ||
config.OCO_AI_PROVIDER == 'test', config.OCO_AI_PROVIDER == 'test' ||
config.OCO_AI_PROVIDER == 'flowise',
'You need to provide an OpenAI/Anthropic/Azure API key' 'You need to provide an OpenAI/Anthropic/Azure API key'
); );
@@ -175,13 +179,24 @@ export const configValidators = {
value || value ||
config.OCO_OPENAI_API_KEY || config.OCO_OPENAI_API_KEY ||
config.OCO_AI_PROVIDER == 'ollama' || config.OCO_AI_PROVIDER == 'ollama' ||
config.OCO_AI_PROVIDER == 'test', config.OCO_AI_PROVIDER == 'test' ||
config.OCO_AI_PROVIDER == 'flowise',
'You need to provide an OpenAI/Anthropic API key' 'You need to provide an OpenAI/Anthropic API key'
); );
return value; return value;
}, },
[CONFIG_KEYS.OCO_FLOWISE_API_KEY](value: any, config: any = {}) {
validateConfig(
CONFIG_KEYS.OCO_FLOWISE_API_KEY,
value || config.OCO_AI_PROVIDER != 'flowise',
'You need to provide a flowise API key'
);
return value;
},
[CONFIG_KEYS.OCO_DESCRIPTION](value: any) { [CONFIG_KEYS.OCO_DESCRIPTION](value: any) {
validateConfig( validateConfig(
CONFIG_KEYS.OCO_DESCRIPTION, CONFIG_KEYS.OCO_DESCRIPTION,
@@ -268,7 +283,8 @@ export const configValidators = {
].includes(value) || ].includes(value) ||
config.OCO_AI_PROVIDER == 'ollama' || config.OCO_AI_PROVIDER == 'ollama' ||
config.OCO_AI_PROVIDER == 'azure' || config.OCO_AI_PROVIDER == 'azure' ||
config.OCO_AI_PROVIDER == 'test', config.OCO_AI_PROVIDER == 'test' ||
config.OCO_AI_PROVIDER == 'flowise',
`${value} is not supported yet, use:\n\n ${[ `${value} is not supported yet, use:\n\n ${[
...MODEL_LIST.openai, ...MODEL_LIST.openai,
...MODEL_LIST.anthropic, ...MODEL_LIST.anthropic,
@@ -308,9 +324,16 @@ export const configValidators = {
[CONFIG_KEYS.OCO_AI_PROVIDER](value: any) { [CONFIG_KEYS.OCO_AI_PROVIDER](value: any) {
validateConfig( validateConfig(
CONFIG_KEYS.OCO_AI_PROVIDER, CONFIG_KEYS.OCO_AI_PROVIDER,
['', 'openai', 'anthropic', 'gemini', 'azure', 'test'].includes(value) || [
value.startsWith('ollama'), '',
`${value} is not supported yet, use 'ollama', 'anthropic', 'azure', 'gemini' or 'openai' (default)` 'openai',
'anthropic',
'gemini',
'azure',
'test',
'flowise'
].includes(value) || value.startsWith('ollama'),
`${value} is not supported yet, use 'ollama', 'anthropic', 'azure', 'gemini', 'flowise' or 'openai' (default)`
); );
return value; return value;
}, },
@@ -324,6 +347,7 @@ export const configValidators = {
return value; return value;
}, },
[CONFIG_KEYS.OCO_AZURE_ENDPOINT](value: any) { [CONFIG_KEYS.OCO_AZURE_ENDPOINT](value: any) {
validateConfig( validateConfig(
CONFIG_KEYS.OCO_AZURE_ENDPOINT, CONFIG_KEYS.OCO_AZURE_ENDPOINT,
@@ -333,6 +357,17 @@ export const configValidators = {
return value; return value;
}, },
[CONFIG_KEYS.OCO_FLOWISE_ENDPOINT](value: any) {
validateConfig(
CONFIG_KEYS.OCO_FLOWISE_ENDPOINT,
typeof value === 'string' && value.includes(':'),
'Value must be string and should include both I.P. and port number' // Considering the possibility of DNS lookup or feeding the I.P. explicitely, there is no pattern to verify, except a column for the port number
);
return value;
},
[CONFIG_KEYS.OCO_TEST_MOCK_TYPE](value: any) { [CONFIG_KEYS.OCO_TEST_MOCK_TYPE](value: any) {
validateConfig( validateConfig(
CONFIG_KEYS.OCO_TEST_MOCK_TYPE, CONFIG_KEYS.OCO_TEST_MOCK_TYPE,
@@ -361,7 +396,6 @@ export type ConfigType = {
const defaultConfigPath = pathJoin(homedir(), '.opencommit'); const defaultConfigPath = pathJoin(homedir(), '.opencommit');
const defaultEnvPath = pathResolve(process.cwd(), '.env'); const defaultEnvPath = pathResolve(process.cwd(), '.env');
export const getConfig = ({ export const getConfig = ({
configPath = defaultConfigPath, configPath = defaultConfigPath,
envPath = defaultEnvPath envPath = defaultEnvPath
@@ -397,9 +431,10 @@ export const getConfig = ({
process.env.OCO_ONE_LINE_COMMIT === 'true' ? true : false, process.env.OCO_ONE_LINE_COMMIT === 'true' ? true : false,
OCO_AZURE_ENDPOINT: process.env.OCO_AZURE_ENDPOINT || undefined, OCO_AZURE_ENDPOINT: process.env.OCO_AZURE_ENDPOINT || undefined,
OCO_TEST_MOCK_TYPE: process.env.OCO_TEST_MOCK_TYPE || 'commit-message', OCO_TEST_MOCK_TYPE: process.env.OCO_TEST_MOCK_TYPE || 'commit-message',
OCO_OLLAMA_API_URL: process.env.OCO_OLLAMA_API_URL || undefined, OCO_FLOWISE_ENDPOINT: process.env.OCO_FLOWISE_ENDPOINT || ':',
OCO_FLOWISE_API_KEY: process.env.OCO_FLOWISE_API_KEY || undefined,
OCO_OLLAMA_API_URL: process.env.OCO_OLLAMA_API_URL || undefined
}; };
const configExists = existsSync(configPath); const configExists = existsSync(configPath);
if (!configExists) return configFromEnv; if (!configExists) return configFromEnv;
+43
View File
@@ -0,0 +1,43 @@
import axios, { AxiosError } from 'axios';
import { ChatCompletionRequestMessage } from 'openai';
import { AiEngine } from './Engine';
import {
getConfig
} from '../commands/config';
const config = getConfig();
export class FlowiseAi implements AiEngine {
async generateCommitMessage(
messages: Array<ChatCompletionRequestMessage>
): Promise<string | undefined> {
const gitDiff = messages[ messages.length - 1 ]?.content?.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r')
.replace(/\t/g, '\\t');
const url = `http://${config?.OCO_FLOWISE_ENDPOINT}/api/v1/prediction/${config?.OCO_FLOWISE_API_KEY}`;
const payload = {
question : gitDiff,
overrideConfig : {
systemMessagePrompt: messages[0]?.content,
},
history : messages.slice( 1, -1 )
}
try {
const response = await axios.post(url, payload, {
headers: {
'Content-Type': 'application/json'
}
});
const message = response.data;
return message?.text;
} catch (err: any) {
const message = err.response?.data?.error ?? err.message;
throw new Error('local model issues. details: ' + message);
}
}
}
+3
View File
@@ -6,6 +6,7 @@ import { OllamaAi } from '../engine/ollama';
import { AnthropicAi } from '../engine/anthropic' import { AnthropicAi } from '../engine/anthropic'
import { TestAi } from '../engine/testAi'; import { TestAi } from '../engine/testAi';
import { Azure } from '../engine/azure'; import { Azure } from '../engine/azure';
import { FlowiseAi } from '../engine/flowise'
export function getEngine(): AiEngine { export function getEngine(): AiEngine {
const config = getConfig(); const config = getConfig();
@@ -27,6 +28,8 @@ export function getEngine(): AiEngine {
return new Gemini(); return new Gemini();
} else if (provider == 'azure') { } else if (provider == 'azure') {
return new Azure(); return new Azure();
} else if( provider == 'flowise'){
return new FlowiseAi();
} }
//open ai gpt by default //open ai gpt by default
+1 -1
View File
@@ -102,4 +102,4 @@ OCO_ONE_LINE_COMMIT="true"
await envFile.cleanup(); await envFile.cleanup();
}); });
}); });