git-aicommit 6.0.0 → 7.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,7 +9,7 @@ Tired of writing commit messages? Let the computer do it for you!
9
9
  ## Installation
10
10
 
11
11
  ```bash
12
- npm install -g git-aicommit
12
+ bun add -g git-aicommit
13
13
  ```
14
14
 
15
15
  ## Configuration
@@ -55,7 +55,7 @@ export default {
55
55
  ],
56
56
  diffFilter: 'ACMRTUXB',
57
57
  completionPromptParams: {
58
- model: "gpt-3.5-turbo",
58
+ model: "gpt-4o-mini",
59
59
  temperature: 0.0,
60
60
  maxTokens: 1000,
61
61
  }
@@ -83,6 +83,4 @@ alias gai='git-aicommit'
83
83
  gai
84
84
  ```
85
85
 
86
- It that simple!
87
-
88
-
86
+ It's that simple!
package/autocommit.js CHANGED
@@ -1,16 +1,10 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
2
 
3
3
  import { execSync, spawn } from "child_process";
4
4
  import rc from 'rc';
5
- import {
6
- ChatPromptTemplate,
7
- HumanMessagePromptTemplate,
8
- PromptTemplate,
9
- SystemMessagePromptTemplate
10
- } from "langchain/prompts";
11
5
  import defaultConfig from './config.js';
12
- import {ChatOpenAI} from "langchain/chat_models/openai";
13
- import {getModelContextSize} from "./count_tokens.js";
6
+ import { OpenAI } from "openai";
7
+ import {calculateMaxTokens, getModelContextSize} from "./count_tokens.js";
14
8
 
15
9
  const config = rc(
16
10
  'git-aicommit',
@@ -22,26 +16,26 @@ const config = rc(
22
16
  );
23
17
 
24
18
  try {
25
- execSync(
26
- 'git rev-parse --is-inside-work-tree',
27
- {encoding: 'utf8', stdio: 'ignore'}
28
- );
19
+ execSync(
20
+ 'git rev-parse --is-inside-work-tree',
21
+ {encoding: 'utf8', stdio: 'ignore'}
22
+ );
29
23
  } catch (e) {
30
- console.error("This is not a git repository");
31
- process.exit(1);
24
+ console.error("This is not a git repository");
25
+ process.exit(1);
32
26
  }
33
27
 
34
28
  if (!config.openAiKey && !config.azureOpenAiKey) {
35
- console.error("Please set OPENAI_API_KEY or AZURE_OPENAI_API_KEY");
36
- process.exit(1);
29
+ console.error("Please set OPENAI_API_KEY or AZURE_OPENAI_API_KEY");
30
+ process.exit(1);
37
31
  }
38
32
 
39
33
  // if any settings related to AZURE are set, if there are items that are not set, will error.
40
34
  if (config.azureOpenAiKey && !(
41
35
  config.azureOpenAiInstanceName && config.azureOpenAiDeploymentName && config.azureOpenAiVersion
42
36
  )){
43
- console.error("Please set AZURE_OPENAI_API_KEY, AZURE_OPENAI_API_INSTANCE_NAME, AZURE_OPENAI_API_DEPLOYMENT_NAME, AZURE_OPENAI_API_VERSION when Azure OpenAI Service.");
44
- process.exit(1);
37
+ console.error("Please set AZURE_OPENAI_API_KEY, AZURE_OPENAI_API_INSTANCE_NAME, AZURE_OPENAI_API_DEPLOYMENT_NAME, AZURE_OPENAI_API_VERSION when Azure OpenAI Service.");
38
+ process.exit(1);
45
39
  }
46
40
 
47
41
  const excludeFromDiff = config.excludeFromDiff || [];
@@ -50,8 +44,8 @@ const diffCommand = `git diff --staged \
50
44
  --no-ext-diff \
51
45
  --diff-filter=${diffFilter} \
52
46
  -- ${excludeFromDiff.map(
53
- (pattern) => `':(exclude)${pattern}'`
54
- ).join(' ')}
47
+ (pattern) => `':(exclude)${pattern}'`
48
+ ).join(' ')}
55
49
  `;
56
50
 
57
51
  let diff = execSync(diffCommand, {encoding: 'utf8'});
@@ -61,97 +55,47 @@ if (!diff) {
61
55
  process.exit(1);
62
56
  }
63
57
 
64
- const openai = new ChatOpenAI({
65
- modelName: config.modelName,
66
- openAIApiKey: config.openAiKey,
67
- azureOpenAIApiKey: config.azureOpenAiKey,
68
- azureOpenAIApiInstanceName: config.azureOpenAiInstanceName,
69
- azureOpenAIApiDeploymentName: config.azureOpenAiDeploymentName,
70
- azureOpenAIApiVersion: config.azureOpenAiVersion,
71
- temperature: config.temperature,
72
- maxTokens: config.maxTokens,
58
+ const openai = new OpenAI({
59
+ apiKey: config.openAiKey,
60
+ baseURL: config.azureOpenAiKey ? `https://${config.azureOpenAiInstanceName}.openai.azure.com/openai/deployments/${config.azureOpenAiDeploymentName}` : undefined,
61
+ defaultHeaders: config.azureOpenAiKey ? { 'api-key': config.azureOpenAiKey } : undefined
73
62
  });
74
63
 
75
- const systemMessagePromptTemplate = SystemMessagePromptTemplate.fromTemplate(
76
- config.systemMessagePromptTemplate
77
- );
64
+ async function getChatCompletion(messages) {
65
+ const response = await openai.chat.completions.create({
66
+ model: config.modelName || 'gpt-4o-mini',
67
+ messages: messages,
68
+ temperature: config.temperature,
69
+ max_tokens: config.maxTokens,
70
+ });
78
71
 
79
- const humanPromptTemplate = HumanMessagePromptTemplate.fromTemplate(
80
- config.humanPromptTemplate
81
- );
72
+ return response.choices[0].message.content.trim();
73
+ }
74
+
75
+ const systemMessage = { role: "system", content: config.systemMessagePromptTemplate };
76
+ const userMessage = { role: "user", content: config.humanPromptTemplate.replace("{diff}", diff).replace("{language}", config.language) };
82
77
 
83
- const chatPrompt = ChatPromptTemplate.fromPromptMessages([
84
- systemMessagePromptTemplate,
85
- humanPromptTemplate,
86
- ]);
78
+ const chatMessages = [systemMessage, userMessage];
87
79
 
88
- const chatMessages = await chatPrompt.formatMessages({
89
- diff: diff,
90
- language: config.language,
80
+ const tokenCount = await calculateMaxTokens({
81
+ prompt: diff,
82
+ modelName: config.modelName || 'gpt-4o-mini'
91
83
  });
92
84
 
93
- const tokenCount = (await openai.getNumTokensFromMessages(chatMessages)).totalCount
94
- const contextSize = getModelContextSize(config.modelName)
85
+ const contextSize = getModelContextSize(config.modelName || 'gpt-4o-mini');
95
86
 
96
87
  if (tokenCount > contextSize) {
97
- console.log('Diff is too long. Splitting into multiple requests.')
98
- // TODO: split smarter
99
- const filenameRegex = /^a\/(.+?)\s+b\/(.+?)/;
100
- const diffByFiles = diff
101
- .split('diff ' + '--git ') // Wierd string concat in order to avoid splitting on this line when using autocommit in this repo :)
102
- .filter((fileDiff) => fileDiff.length > 0)
103
- .map((fileDiff) => {
104
- const match = fileDiff.match(filenameRegex);
105
- const filename = match ? match[1] : 'Unknown file';
106
-
107
- const content = fileDiff
108
- .replaceAll(filename, '')
109
- .replaceAll('a/ b/\n', '')
110
-
111
- return chatPrompt
112
- .formatMessages({
113
- diff: content,
114
- language: config.language,
115
- })
116
- .then((prompt) => {
117
- return openai.call(prompt)
118
- .then((res) => {
119
- return {
120
- filename: filename,
121
- changes: res.text.trim(),
122
- }
123
- })
124
- .catch((e) => {
125
- console.error(`Error during OpenAI request: ${e.message}`);
126
- process.exit(1);
127
- });
128
- });
129
- });
130
-
131
- // wait for all promises to resolve
132
- const mergeChanges = await Promise.all(diffByFiles);
133
-
134
- diff = mergeChanges
135
- .map((fileDiff) => {
136
- return `diff --git ${fileDiff.filename}\n${fileDiff.changes}`
137
-
138
- })
139
- .join('\n\n')
140
- }
141
-
142
- const prompt = await chatPrompt.formatMessages({
143
- diff: diff,
144
- language: config.language,
145
- })
88
+ console.log('Diff is too long. Please lower the amount of changes in the commit or switch to a model with bigger context size');
146
89
 
147
- const res = await openai.call(prompt)
148
- .catch((e) => {
149
- console.error(`Error during OpenAI request: ${e.message}`);
150
- process.exit(1);
151
- });
90
+ process.exit(1);
91
+ }
152
92
 
153
- const commitMessage = res.text.trim();
93
+ const messages = [
94
+ { role: "system", content: config.systemMessagePromptTemplate },
95
+ { role: "user", content: config.humanPromptTemplate.replace("{diff}", diff).replace("{language}", config.language) }
96
+ ];
154
97
 
98
+ const commitMessage = await getChatCompletion(messages);
155
99
 
156
100
  if (!config.autocommit) {
157
101
  console.log(`Autocommit is disabled. Here is the message:\n ${commitMessage}`);
package/config.js CHANGED
@@ -8,7 +8,7 @@ export default {
8
8
  openCommitTextEditor: false,
9
9
  language: 'english',
10
10
  systemMessagePromptTemplate: '' +
11
- 'You are expert AI, your job is to write clear and concise Git commit messages.' +
11
+ 'You are expert software developer, your job is to write clear and concise Git commit messages. ' +
12
12
  'Your responsibility is to ensure that these messages accurately describe the changes made in each commit,' +
13
13
  'follow established guidelines. Provide a clear history of changes to the codebase.' +
14
14
  'Write 1-2 sentences. Output only the commit message without comments or other text.',
@@ -16,13 +16,13 @@ export default {
16
16
  'Read the following git diff for a multiple files and ' +
17
17
  'write 1-2 sentences commit message in {language}' +
18
18
  'without mentioning lines or files.' +
19
- 'Explain why these changes were made (summarize the reasoning):\n' +
19
+ 'If the reason behind the changed can be deducted from the changed, provide this reason:\n' +
20
20
  '{diff}',
21
21
  excludeFromDiff: [
22
22
  '*.lock', '*.lockb', '*-lock.json', '*-lock.yaml'
23
23
  ],
24
24
  diffFilter: 'ACMRTUXB',
25
- modelName: "gpt-3.5-turbo-16k",
25
+ modelName: "gpt-4o-mini",
26
26
  temperature: 0.0,
27
27
  maxTokens: 2000,
28
28
  }
package/count_tokens.js CHANGED
@@ -1,27 +1,27 @@
1
- // langchain/dist/base_language/count_tokens.js
2
1
  export const getModelNameForTiktoken = (modelName) => {
3
2
  if (modelName.startsWith("gpt-3.5-turbo-16k")) {
4
3
  return "gpt-3.5-turbo-16k";
5
4
  }
5
+
6
6
  if (modelName.startsWith("gpt-3.5-turbo-")) {
7
7
  return "gpt-3.5-turbo";
8
8
  }
9
+
9
10
  if (modelName.startsWith("gpt-4-32k-")) {
10
11
  return "gpt-4-32k";
11
12
  }
13
+
12
14
  if (modelName.startsWith("gpt-4-")) {
13
15
  return "gpt-4";
14
16
  }
15
- return modelName;
16
- };
17
- export const getEmbeddingContextSize = (modelName) => {
18
- switch (modelName) {
19
- case "text-embedding-ada-002":
20
- return 8191;
21
- default:
22
- return 2046;
17
+
18
+ if (modelName.startsWith("gpt-4o-")) {
19
+ return "gpt-4o";
23
20
  }
21
+ return modelName;
24
22
  };
23
+
24
+
25
25
  export const getModelContextSize = (modelName) => {
26
26
  switch (getModelNameForTiktoken(modelName)) {
27
27
  case "gpt-3.5-turbo-16k":
@@ -32,33 +32,24 @@ export const getModelContextSize = (modelName) => {
32
32
  return 32768;
33
33
  case "gpt-4":
34
34
  return 8192;
35
- case "text-davinci-003":
36
- return 4097;
37
- case "text-curie-001":
38
- return 2048;
39
- case "text-babbage-001":
40
- return 2048;
41
- case "text-ada-001":
42
- return 2048;
43
- case "code-davinci-002":
44
- return 8000;
45
- case "code-cushman-001":
46
- return 2048;
35
+ case "gpt-4o":
36
+ return 128000;
47
37
  default:
48
- return 4097;
38
+ return 4096;
49
39
  }
50
40
  };
41
+
51
42
  export const importTiktoken = async () => {
52
43
  try {
53
44
  const { encoding_for_model } = await import("@dqbd/tiktoken");
54
45
  return { encoding_for_model };
55
- }
56
- catch (error) {
46
+ } catch (error) {
57
47
  console.log(error);
58
48
  return { encoding_for_model: null };
59
49
  }
60
50
  };
61
- export const calculateMaxTokens = async ({ prompt, modelName, }) => {
51
+
52
+ export const calculateMaxTokens = async ({ prompt, modelName }) => {
62
53
  const { encoding_for_model } = await importTiktoken();
63
54
  // fallback to approximate calculation if tiktoken is not available
64
55
  let numTokens = Math.ceil(prompt.length / 4);
@@ -69,8 +60,7 @@ export const calculateMaxTokens = async ({ prompt, modelName, }) => {
69
60
  numTokens = tokenized.length;
70
61
  encoding.free();
71
62
  }
72
- }
73
- catch (error) {
63
+ } catch (error) {
74
64
  console.warn("Failed to calculate number of tokens with tiktoken, falling back to approximate count", error);
75
65
  }
76
66
  const maxTokens = getModelContextSize(modelName);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-aicommit",
3
- "version": "6.0.0",
3
+ "version": "7.0.1",
4
4
  "description": "Generates auto commit messages with OpenAI GPT3 model",
5
5
  "main": "autocommit.js",
6
6
  "repository": "https://github.com/shanginn/autocommit",
@@ -8,10 +8,9 @@
8
8
  "license": "MIT",
9
9
  "type": "module",
10
10
  "dependencies": {
11
- "langchain": "^0.0.75",
12
- "openai": "^3.3.0",
11
+ "openai": "^4.52.7",
13
12
  "rc": "^1.2.8",
14
- "tiktoken": "^1.0.8"
13
+ "tiktoken": "^1.0.15"
15
14
  },
16
15
  "preferGlobal": true,
17
16
  "bin": {