git-coco 0.7.6 → 0.8.0
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/dist/index.d.ts +119 -63
- package/dist/index.esm.mjs +218 -150
- package/dist/index.js +218 -150
- package/package.json +2 -1
package/dist/index.esm.mjs
CHANGED
|
@@ -15,11 +15,11 @@ import now from 'performance-now';
|
|
|
15
15
|
import prettyMilliseconds from 'pretty-ms';
|
|
16
16
|
import pQueue from 'p-queue';
|
|
17
17
|
import { Document } from 'langchain/document';
|
|
18
|
-
import { HuggingFaceInference } from 'langchain/llms/hf';
|
|
19
18
|
import { loadSummarizationChain, LLMChain } from 'langchain/chains';
|
|
20
|
-
import { OpenAI } from 'langchain/llms/openai';
|
|
21
19
|
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
|
|
22
20
|
import { createTwoFilesPatch } from 'diff';
|
|
21
|
+
import { Ollama } from 'langchain/llms/ollama';
|
|
22
|
+
import { OpenAI } from 'langchain/llms/openai';
|
|
23
23
|
import { minimatch } from 'minimatch';
|
|
24
24
|
import { simpleGit } from 'simple-git';
|
|
25
25
|
import { encoding_for_model } from 'tiktoken';
|
|
@@ -47,33 +47,34 @@ const SUMMARIZE_PROMPT = new PromptTemplate({
|
|
|
47
47
|
inputVariables: inputVariables$2,
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
+
const DEFAULT_IGNORED_FILES = ['package-lock.json'];
|
|
51
|
+
const DEFAULT_IGNORED_EXTENSIONS = ['.map', '.lock'];
|
|
50
52
|
/**
|
|
51
53
|
* Default Config
|
|
52
54
|
*
|
|
53
55
|
* @type {Config}
|
|
54
56
|
*/
|
|
55
57
|
const DEFAULT_CONFIG = {
|
|
56
|
-
service: 'openai
|
|
58
|
+
service: 'openai',
|
|
57
59
|
verbose: false,
|
|
58
60
|
tokenLimit: 1024,
|
|
59
61
|
summarizePrompt: SUMMARIZE_PROMPT.template,
|
|
60
62
|
temperature: 0.4,
|
|
61
63
|
mode: 'stdout',
|
|
62
|
-
ignoredFiles:
|
|
63
|
-
ignoredExtensions:
|
|
64
|
+
ignoredFiles: DEFAULT_IGNORED_FILES,
|
|
65
|
+
ignoredExtensions: DEFAULT_IGNORED_EXTENSIONS,
|
|
64
66
|
defaultBranch: 'main',
|
|
65
67
|
};
|
|
66
68
|
/**
|
|
67
69
|
* Create a named export of all config keys for use in other modules.
|
|
68
70
|
*
|
|
69
|
-
* @see
|
|
71
|
+
* @see Used in `src/lib/config/services/env.ts` to validate all env vars.
|
|
70
72
|
*
|
|
71
73
|
* @type {string[]}
|
|
72
74
|
*/
|
|
73
75
|
const CONFIG_KEYS = Object.keys({
|
|
74
76
|
...DEFAULT_CONFIG,
|
|
75
|
-
|
|
76
|
-
openAIApiKey: '',
|
|
77
|
+
endpoint: '',
|
|
77
78
|
prompt: '',
|
|
78
79
|
});
|
|
79
80
|
const COCO_CONFIG_START_COMMENT = '# -- start coco config --';
|
|
@@ -152,8 +153,9 @@ function loadEnvConfig(config) {
|
|
|
152
153
|
CONFIG_KEYS.forEach((key) => {
|
|
153
154
|
const envVarName = toEnvVarName(key);
|
|
154
155
|
const envValue = parseEnvValue(key, process.env[envVarName]);
|
|
155
|
-
if (envValue === undefined)
|
|
156
|
+
if (envValue === undefined) {
|
|
156
157
|
return;
|
|
158
|
+
}
|
|
157
159
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
158
160
|
// @ts-ignore
|
|
159
161
|
envConfig[key] = envValue;
|
|
@@ -161,27 +163,28 @@ function loadEnvConfig(config) {
|
|
|
161
163
|
return { ...config, ...removeUndefined(envConfig) };
|
|
162
164
|
}
|
|
163
165
|
function parseEnvValue(key, value) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
166
|
+
switch (true) {
|
|
167
|
+
// Handle undefined values
|
|
168
|
+
case value === undefined:
|
|
169
|
+
return undefined;
|
|
170
|
+
// Handle comma separated strings for ignoredFiles and ignoredExtensions arrays
|
|
171
|
+
case (key === 'ignoredFiles' || key === 'ignoredExtensions') &&
|
|
172
|
+
typeof value === 'string' &&
|
|
173
|
+
value.includes(','):
|
|
174
|
+
return value.split(',');
|
|
175
|
+
// Handle boolean values
|
|
176
|
+
case typeof value === 'string' && (value === 'false' || value === 'true'):
|
|
177
|
+
return value === 'true';
|
|
178
|
+
default:
|
|
179
|
+
return value;
|
|
174
180
|
}
|
|
175
|
-
return value;
|
|
176
181
|
}
|
|
177
182
|
function toEnvVarName(key) {
|
|
178
183
|
switch (key) {
|
|
179
184
|
case 'openAIApiKey':
|
|
180
185
|
return 'OPENAI_API_KEY';
|
|
181
|
-
case 'huggingFaceHubApiKey':
|
|
182
|
-
return 'HUGGINGFACE_HUB_API_KEY';
|
|
183
186
|
default:
|
|
184
|
-
return
|
|
187
|
+
return `COCO_${key.replace(/([A-Z])/g, '_$1').toLocaleUpperCase()}`;
|
|
185
188
|
}
|
|
186
189
|
}
|
|
187
190
|
function formatEnvValue(value) {
|
|
@@ -225,13 +228,19 @@ function loadGitConfig(config) {
|
|
|
225
228
|
const gitConfigParsed = ini.parse(gitConfigRaw);
|
|
226
229
|
config = {
|
|
227
230
|
...config,
|
|
228
|
-
service: gitConfigParsed.coco?.
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
231
|
+
service: gitConfigParsed.coco?.service || config.service,
|
|
232
|
+
...(config.service === 'ollama'
|
|
233
|
+
? {
|
|
234
|
+
endpoint: gitConfigParsed.coco?.endpoint || config?.endpoint,
|
|
235
|
+
}
|
|
236
|
+
: {
|
|
237
|
+
openAIApiKey: gitConfigParsed.coco?.openAIApiKey || config?.openAIApiKey,
|
|
238
|
+
}),
|
|
239
|
+
model: gitConfigParsed.coco?.model || config?.model,
|
|
240
|
+
temperature: gitConfigParsed.coco?.temperature || config?.temperature,
|
|
241
|
+
tokenLimit: gitConfigParsed.coco?.tokenLimit || config?.tokenLimit,
|
|
232
242
|
prompt: gitConfigParsed.coco?.prompt || config.prompt,
|
|
233
243
|
mode: gitConfigParsed.coco?.mode || config.mode,
|
|
234
|
-
temperature: gitConfigParsed.coco?.temperature || config.temperature,
|
|
235
244
|
summarizePrompt: gitConfigParsed.coco?.summarizePrompt || config.summarizePrompt,
|
|
236
245
|
ignoredFiles: gitConfigParsed.coco?.ignoredFiles || config.ignoredFiles,
|
|
237
246
|
ignoredExtensions: gitConfigParsed.coco?.ignoredExtensions || config.ignoredExtensions,
|
|
@@ -661,64 +670,16 @@ async function collectDiffs(node, getFileDiff, tokenizer, logger) {
|
|
|
661
670
|
};
|
|
662
671
|
}
|
|
663
672
|
|
|
664
|
-
function getModelAndProviderFromService(service) {
|
|
665
|
-
if (!service) {
|
|
666
|
-
throw new Error(`Missing service`);
|
|
667
|
-
}
|
|
668
|
-
const [provider, model] = service.split(/\/(.*)/s);
|
|
669
|
-
if (!model || !provider) {
|
|
670
|
-
throw new Error(`Invalid service: ${service}`);
|
|
671
|
-
}
|
|
672
|
-
return { provider, model };
|
|
673
|
-
}
|
|
674
|
-
function getModelFromService(service) {
|
|
675
|
-
const { model } = getModelAndProviderFromService(service);
|
|
676
|
-
return model;
|
|
677
|
-
}
|
|
678
673
|
/**
|
|
679
|
-
* Get
|
|
680
|
-
* @param
|
|
681
|
-
* @param configuration
|
|
682
|
-
* @returns LLM Model
|
|
683
|
-
*/
|
|
684
|
-
function getLlm(service, key, fields) {
|
|
685
|
-
const { provider, model } = getModelAndProviderFromService(service);
|
|
686
|
-
if (!model) {
|
|
687
|
-
throw new Error(`Invalid LLM Service: ${service}`);
|
|
688
|
-
}
|
|
689
|
-
switch (provider) {
|
|
690
|
-
case 'huggingface':
|
|
691
|
-
return new HuggingFaceInference({
|
|
692
|
-
model: model,
|
|
693
|
-
apiKey: key,
|
|
694
|
-
maxConcurrency: 4,
|
|
695
|
-
...fields,
|
|
696
|
-
});
|
|
697
|
-
case 'openai':
|
|
698
|
-
default:
|
|
699
|
-
return new OpenAI({
|
|
700
|
-
openAIApiKey: key,
|
|
701
|
-
modelName: model,
|
|
702
|
-
...fields,
|
|
703
|
-
});
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
/**
|
|
707
|
-
* Retrieve appropriate API key based on selected model
|
|
708
|
-
* @param service
|
|
674
|
+
* Get Summarization Chain
|
|
675
|
+
* @param model
|
|
709
676
|
* @param options
|
|
710
677
|
* @returns
|
|
711
678
|
*/
|
|
712
|
-
function
|
|
713
|
-
|
|
714
|
-
switch (provider) {
|
|
715
|
-
case 'huggingface':
|
|
716
|
-
return options.huggingFaceHubApiKey;
|
|
717
|
-
case 'openai':
|
|
718
|
-
default:
|
|
719
|
-
return options.openAIApiKey;
|
|
720
|
-
}
|
|
679
|
+
function getSummarizationChain(model, options = { type: 'map_reduce' }) {
|
|
680
|
+
return loadSummarizationChain(model, options);
|
|
721
681
|
}
|
|
682
|
+
|
|
722
683
|
/**
|
|
723
684
|
* Get Recursive Character Text Splitter
|
|
724
685
|
* @param options
|
|
@@ -727,41 +688,6 @@ function getApiKeyForModel(service, options) {
|
|
|
727
688
|
function getTextSplitter(options = {}) {
|
|
728
689
|
return new RecursiveCharacterTextSplitter(options);
|
|
729
690
|
}
|
|
730
|
-
/**
|
|
731
|
-
* Get Summarization Chain
|
|
732
|
-
* @param model
|
|
733
|
-
* @param options
|
|
734
|
-
* @returns
|
|
735
|
-
*/
|
|
736
|
-
function getSummarizationChain(model, options = { type: 'map_reduce' }) {
|
|
737
|
-
return loadSummarizationChain(model, options);
|
|
738
|
-
}
|
|
739
|
-
function getPrompt({ template, variables, fallback }) {
|
|
740
|
-
if (!template && !fallback)
|
|
741
|
-
throw new Error('Must provide either a template or a fallback');
|
|
742
|
-
return (template
|
|
743
|
-
? new PromptTemplate({
|
|
744
|
-
template,
|
|
745
|
-
inputVariables: variables,
|
|
746
|
-
})
|
|
747
|
-
: fallback);
|
|
748
|
-
}
|
|
749
|
-
/**
|
|
750
|
-
* Verify template string contains all required input variables
|
|
751
|
-
* @param text template string
|
|
752
|
-
* @param inputVariables template variables
|
|
753
|
-
* @returns boolean or error message
|
|
754
|
-
*/
|
|
755
|
-
function validatePromptTemplate(text, inputVariables) {
|
|
756
|
-
if (!text) {
|
|
757
|
-
return 'Prompt template cannot be empty';
|
|
758
|
-
}
|
|
759
|
-
if (!inputVariables.some((entry) => text.includes(entry))) {
|
|
760
|
-
return ('Prompt template must include at least one of the following input variables: ' +
|
|
761
|
-
inputVariables.map((value) => `{${value}}`).join(', '));
|
|
762
|
-
}
|
|
763
|
-
return true;
|
|
764
|
-
}
|
|
765
691
|
|
|
766
692
|
async function parseDefaultFileDiff(nodeFile, commit = '--staged', git) {
|
|
767
693
|
if (commit !== '--staged') {
|
|
@@ -849,14 +775,14 @@ async function fileChangeParser({ changes, commit, options: { tokenizer, git, ll
|
|
|
849
775
|
}
|
|
850
776
|
|
|
851
777
|
const template$1 = `Write informative git commit message, in the imperative, based on the diffs & file changes provided in the "Diff Summary" section.
|
|
852
|
-
Commit Messages must have a short description that is less than 50 characters
|
|
778
|
+
Commit Messages must have a short description that is less than 50 characters and a longer detailed summary no more than 300 characters, the shorter and more concise the better. The detailed summary should be separated from the short description by a blank line. Please follow the guidelines below when writing your commit message:
|
|
853
779
|
|
|
854
|
-
- Typically a hyphen or asterisk is used for the bullet
|
|
855
780
|
- Write concisely using an informal tone
|
|
856
781
|
- DO NOT use phrases like "this commit", "this change", "this function", etc. Instead refer to the function, variable, or class by name
|
|
857
|
-
- DO NOT use phrases like "this commit", "this change", "this function", etc. Instead refer to the function, variable, or class by name
|
|
858
782
|
- DO NOT use specific names or files from the code
|
|
783
|
+
- DO NOT include any diffs or file changes in the commit message
|
|
859
784
|
- Wrap variable, class, function, components, and dependency names in back ticks e.g. \`variable\`
|
|
785
|
+
- ONLY respond with the resulting commit message.
|
|
860
786
|
|
|
861
787
|
"""{summary}"""
|
|
862
788
|
|
|
@@ -867,6 +793,143 @@ const COMMIT_PROMPT = new PromptTemplate({
|
|
|
867
793
|
inputVariables: inputVariables$1,
|
|
868
794
|
});
|
|
869
795
|
|
|
796
|
+
const DEFAULT_OLLAMA_LLM_SERVICE = {
|
|
797
|
+
provider: 'ollama',
|
|
798
|
+
model: 'codellama',
|
|
799
|
+
endpoint: 'http://localhost:11434',
|
|
800
|
+
maxConcurrent: 1,
|
|
801
|
+
tokenLimit: 1024,
|
|
802
|
+
};
|
|
803
|
+
const DEFAULT_OPENAI_LLM_SERVICE = {
|
|
804
|
+
provider: 'openai',
|
|
805
|
+
model: 'gpt-4',
|
|
806
|
+
authentication: {
|
|
807
|
+
type: 'APIKey',
|
|
808
|
+
credentials: {
|
|
809
|
+
apiKey: '',
|
|
810
|
+
},
|
|
811
|
+
},
|
|
812
|
+
tokenLimit: 1024,
|
|
813
|
+
};
|
|
814
|
+
|
|
815
|
+
/**
|
|
816
|
+
* Retrieves the provider and model from the given configuration object.
|
|
817
|
+
* @param config The configuration object.
|
|
818
|
+
* @returns An object containing the provider and model.
|
|
819
|
+
* @throws Error if the configuration is invalid or missing required properties.
|
|
820
|
+
*/
|
|
821
|
+
function getModelAndProviderFromConfig(config) {
|
|
822
|
+
if (!config.service) {
|
|
823
|
+
throw new Error('Invalid service: undefined');
|
|
824
|
+
}
|
|
825
|
+
let result;
|
|
826
|
+
switch (typeof config.service) {
|
|
827
|
+
case 'string':
|
|
828
|
+
result = getDefaultServiceConfigFromAlias(config.service, config?.model);
|
|
829
|
+
break;
|
|
830
|
+
case 'object':
|
|
831
|
+
default:
|
|
832
|
+
result = config.service;
|
|
833
|
+
break;
|
|
834
|
+
}
|
|
835
|
+
const { provider, model } = result;
|
|
836
|
+
if (!model || !provider) {
|
|
837
|
+
throw new Error(`Invalid service: ${config.service}`);
|
|
838
|
+
}
|
|
839
|
+
return { provider, model };
|
|
840
|
+
}
|
|
841
|
+
/**
|
|
842
|
+
* Retrieve appropriate API key based on selected model
|
|
843
|
+
* @param service
|
|
844
|
+
* @param options
|
|
845
|
+
* @returns API Key
|
|
846
|
+
*/
|
|
847
|
+
function getApiKeyForModel(config) {
|
|
848
|
+
const { provider } = getModelAndProviderFromConfig(config);
|
|
849
|
+
switch (provider) {
|
|
850
|
+
case 'openai':
|
|
851
|
+
return config.openAIApiKey || getDefaultServiceApiKey(config);
|
|
852
|
+
default:
|
|
853
|
+
return getDefaultServiceApiKey(config);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* Retrieves the default service API key from the given configuration.
|
|
858
|
+
* @param config The configuration object.
|
|
859
|
+
* @returns The default service API key.
|
|
860
|
+
*/
|
|
861
|
+
function getDefaultServiceApiKey(config) {
|
|
862
|
+
const service = config.service;
|
|
863
|
+
if (service.authentication.type === 'APIKey') {
|
|
864
|
+
return service.authentication.credentials?.apiKey;
|
|
865
|
+
}
|
|
866
|
+
else if (service.authentication.type === 'OAuth') {
|
|
867
|
+
return service.authentication.credentials?.token;
|
|
868
|
+
}
|
|
869
|
+
return '';
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* Retrieves the default service configuration based on the provided alias and optional model.
|
|
873
|
+
* @param alias - The alias of the service.
|
|
874
|
+
* @param model - The optional model to be used.
|
|
875
|
+
* @returns The default service configuration.
|
|
876
|
+
* @throws Error if the alias is invalid or undefined.
|
|
877
|
+
*/
|
|
878
|
+
function getDefaultServiceConfigFromAlias(alias, model) {
|
|
879
|
+
if (!alias) {
|
|
880
|
+
throw new Error('Invalid alias: undefined');
|
|
881
|
+
}
|
|
882
|
+
switch (alias) {
|
|
883
|
+
case 'openai':
|
|
884
|
+
return {
|
|
885
|
+
...DEFAULT_OPENAI_LLM_SERVICE,
|
|
886
|
+
model: model || DEFAULT_OPENAI_LLM_SERVICE.model,
|
|
887
|
+
};
|
|
888
|
+
case 'ollama':
|
|
889
|
+
return {
|
|
890
|
+
...DEFAULT_OLLAMA_LLM_SERVICE,
|
|
891
|
+
model: model || DEFAULT_OLLAMA_LLM_SERVICE.model,
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
/**
|
|
897
|
+
* Get LLM Model Based on Configuration
|
|
898
|
+
*
|
|
899
|
+
* @param fields
|
|
900
|
+
* @param configuration
|
|
901
|
+
* @returns LLM Model
|
|
902
|
+
*/
|
|
903
|
+
function getLlm(provider, model, config) {
|
|
904
|
+
if (!model) {
|
|
905
|
+
throw new Error(`Invalid LLM Service: ${provider}/${model}`);
|
|
906
|
+
}
|
|
907
|
+
switch (provider) {
|
|
908
|
+
case 'ollama':
|
|
909
|
+
return new Ollama({
|
|
910
|
+
baseUrl: DEFAULT_OLLAMA_LLM_SERVICE.endpoint,
|
|
911
|
+
model,
|
|
912
|
+
});
|
|
913
|
+
case 'openai':
|
|
914
|
+
default:
|
|
915
|
+
return new OpenAI({
|
|
916
|
+
openAIApiKey: config.openAIApiKey,
|
|
917
|
+
modelName: model,
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
function getPrompt({ template, variables, fallback }) {
|
|
923
|
+
if (!template && !fallback)
|
|
924
|
+
throw new Error('Must provide either a template or a fallback');
|
|
925
|
+
return (template
|
|
926
|
+
? new PromptTemplate({
|
|
927
|
+
template,
|
|
928
|
+
inputVariables: variables,
|
|
929
|
+
})
|
|
930
|
+
: fallback);
|
|
931
|
+
}
|
|
932
|
+
|
|
870
933
|
function getStatus(file, location = 'index') {
|
|
871
934
|
if ('index' in file && 'working_dir' in file) {
|
|
872
935
|
const statusCode = file[location];
|
|
@@ -921,9 +984,6 @@ function getSummaryText(file, change) {
|
|
|
921
984
|
return `${status}: ${filePath}`;
|
|
922
985
|
}
|
|
923
986
|
|
|
924
|
-
const config = loadConfig();
|
|
925
|
-
const DEFAULT_IGNORED_FILES = config?.ignoredFiles?.length ? config.ignoredFiles : [];
|
|
926
|
-
const DEFAULT_IGNORED_EXTENSIONS = config?.ignoredExtensions?.length ? config.ignoredExtensions : [];
|
|
927
987
|
async function getChanges({ git, options }) {
|
|
928
988
|
const { ignoredFiles = DEFAULT_IGNORED_FILES, ignoredExtensions = DEFAULT_IGNORED_EXTENSIONS } = options || {};
|
|
929
989
|
const staged = [];
|
|
@@ -989,13 +1049,13 @@ async function noResult({ git, logger }) {
|
|
|
989
1049
|
else if (hasUnstaged || hasUntracked) {
|
|
990
1050
|
logger.log('Forget something? No staged changes found... 👻', { color: 'red' });
|
|
991
1051
|
if (hasUnstaged) {
|
|
992
|
-
logger.log('\
|
|
1052
|
+
logger.log('\nChanges not staged for commit:', { color: 'yellow' });
|
|
993
1053
|
logger.verbose(`\t${unstaged.map(({ summary }) => summary).join('\n\t')}`, {
|
|
994
1054
|
color: 'red',
|
|
995
1055
|
});
|
|
996
1056
|
}
|
|
997
1057
|
if (hasUntracked) {
|
|
998
|
-
logger.log('\
|
|
1058
|
+
logger.log('\nUntracked changes:', { color: 'yellow' });
|
|
999
1059
|
logger.verbose(`\t${untracked.map(({ summary }) => summary).join('\n\t')}`, {
|
|
1000
1060
|
color: 'red',
|
|
1001
1061
|
});
|
|
@@ -1069,6 +1129,23 @@ async function getUserReviewDecision({ label, descriptions, enableRetry = true,
|
|
|
1069
1129
|
}));
|
|
1070
1130
|
}
|
|
1071
1131
|
|
|
1132
|
+
/**
|
|
1133
|
+
* Verify template string contains all required input variables
|
|
1134
|
+
* @param text template string
|
|
1135
|
+
* @param inputVariables template variables
|
|
1136
|
+
* @returns boolean or error message
|
|
1137
|
+
*/
|
|
1138
|
+
function validatePromptTemplate(text, inputVariables) {
|
|
1139
|
+
if (!text) {
|
|
1140
|
+
return 'Prompt template cannot be empty';
|
|
1141
|
+
}
|
|
1142
|
+
if (!inputVariables.some((entry) => text.includes(entry))) {
|
|
1143
|
+
return ('Prompt template must include at least one of the following input variables: ' +
|
|
1144
|
+
inputVariables.map((value) => `{${value}}`).join(', '));
|
|
1145
|
+
}
|
|
1146
|
+
return true;
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1072
1149
|
async function editPrompt(options) {
|
|
1073
1150
|
return await editor({
|
|
1074
1151
|
message: 'Edit the prompt',
|
|
@@ -1214,12 +1291,14 @@ const getRepo = () => {
|
|
|
1214
1291
|
const getTikToken = async (modelName) => {
|
|
1215
1292
|
return await encoding_for_model(modelName);
|
|
1216
1293
|
};
|
|
1217
|
-
const getTokenCounter = async (modelName) =>
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1294
|
+
const getTokenCounter = async (modelName) => {
|
|
1295
|
+
return getTikToken(modelName).then((tokenizer) => (text) => {
|
|
1296
|
+
// console.log('Running GetTokenCount', { tokenizer, length: text.length })
|
|
1297
|
+
const tokens = tokenizer.encode(text);
|
|
1298
|
+
// console.log('Tokens', { tokenCount: tokens.length })
|
|
1299
|
+
return tokens.length;
|
|
1300
|
+
});
|
|
1301
|
+
};
|
|
1223
1302
|
|
|
1224
1303
|
async function createCommit(commitMsg, git) {
|
|
1225
1304
|
return await git.commit(commitMsg);
|
|
@@ -1228,17 +1307,14 @@ async function createCommit(commitMsg, git) {
|
|
|
1228
1307
|
const handler$2 = async (argv, logger) => {
|
|
1229
1308
|
const git = getRepo();
|
|
1230
1309
|
const options = loadConfig(argv);
|
|
1231
|
-
const
|
|
1232
|
-
const key = getApiKeyForModel(service, options);
|
|
1233
|
-
const tokenizer = await getTokenCounter(getModelFromService(service));
|
|
1310
|
+
const key = getApiKeyForModel(options);
|
|
1234
1311
|
if (!key) {
|
|
1235
1312
|
logger.log(`No API Key found. 🗝️🚪`, { color: 'red' });
|
|
1236
1313
|
process.exit(1);
|
|
1237
1314
|
}
|
|
1238
|
-
const
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
});
|
|
1315
|
+
const { provider, model } = getModelAndProviderFromConfig(options);
|
|
1316
|
+
const tokenizer = await getTokenCounter(provider === 'openai' ? model : 'gpt-4');
|
|
1317
|
+
const llm = getLlm(provider, model, options);
|
|
1242
1318
|
const INTERACTIVE = isInteractive(options);
|
|
1243
1319
|
if (INTERACTIVE) {
|
|
1244
1320
|
logger.log(LOGO);
|
|
@@ -1304,16 +1380,10 @@ const handler$2 = async (argv, logger) => {
|
|
|
1304
1380
|
* Command line options via yargs
|
|
1305
1381
|
*/
|
|
1306
1382
|
const options$2 = {
|
|
1307
|
-
|
|
1383
|
+
service: { type: 'string', description: 'LLM/Model-Name', choices: ['openai', 'ollama'] },
|
|
1308
1384
|
openAIApiKey: {
|
|
1309
1385
|
type: 'string',
|
|
1310
1386
|
description: 'OpenAI API Key',
|
|
1311
|
-
conflicts: 'huggingFaceHubApiKey',
|
|
1312
|
-
},
|
|
1313
|
-
huggingFaceHubApiKey: {
|
|
1314
|
-
type: 'string',
|
|
1315
|
-
description: 'HuggingFace Hub API Key',
|
|
1316
|
-
conflicts: 'openAIApiKey',
|
|
1317
1387
|
},
|
|
1318
1388
|
tokenLimit: { type: 'number', description: 'Token limit' },
|
|
1319
1389
|
prompt: {
|
|
@@ -1437,15 +1507,13 @@ async function getCommitLogCurrentBranch({ git, logger, comparisonBranch = 'main
|
|
|
1437
1507
|
const handler$1 = async (argv, logger) => {
|
|
1438
1508
|
const options = loadConfig(argv);
|
|
1439
1509
|
const git = getRepo();
|
|
1440
|
-
const key = getApiKeyForModel(options
|
|
1510
|
+
const key = getApiKeyForModel(options);
|
|
1441
1511
|
if (!key) {
|
|
1442
1512
|
logger.log(`No API Key found. 🗝️🚪`, { color: 'red' });
|
|
1443
1513
|
process.exit(1);
|
|
1444
1514
|
}
|
|
1445
|
-
const model =
|
|
1446
|
-
|
|
1447
|
-
maxConcurrency: 10,
|
|
1448
|
-
});
|
|
1515
|
+
const { provider, model } = getModelAndProviderFromConfig(options);
|
|
1516
|
+
const llm = getLlm(provider, model, options);
|
|
1449
1517
|
const INTERACTIVE = isInteractive(options);
|
|
1450
1518
|
if (INTERACTIVE) {
|
|
1451
1519
|
logger.log(LOGO);
|
|
@@ -1477,7 +1545,7 @@ const handler$1 = async (argv, logger) => {
|
|
|
1477
1545
|
fallback: CHANGELOG_PROMPT,
|
|
1478
1546
|
});
|
|
1479
1547
|
return await executeChain({
|
|
1480
|
-
llm
|
|
1548
|
+
llm,
|
|
1481
1549
|
prompt,
|
|
1482
1550
|
variables: { summary: context },
|
|
1483
1551
|
});
|