From c9b45492a55b94d5e60eb283f8f2cc8013aa96b5 Mon Sep 17 00:00:00 2001 From: mattsalt123 <93153126+mattsalt123@users.noreply.github.com> Date: Thu, 29 Feb 2024 15:06:23 +0000 Subject: [PATCH] feat: Add Input Token Limit Config Option (#281) --- README.md | 6 ++-- src/commands/config.ts | 42 ++++++++++++++++++++----- src/engine/openAi.ts | 9 +++--- src/generateCommitMessageFromGitDiff.ts | 15 ++++++--- 4 files changed, 54 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 3302f11..b92f3a7 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,8 @@ Create a `.env` file and add OpenCommit config variables there like this: ```env OCO_OPENAI_API_KEY= -OCO_OPENAI_MAX_TOKENS= +OCO_TOKENS_MAX_INPUT= +OCO_TOKENS_MAX_OUTPUT= OCO_OPENAI_BASE_PATH= OCO_DESCRIPTION= OCO_EMOJI= @@ -329,7 +330,8 @@ jobs: OCO_OPENAI_API_KEY: ${{ secrets.OCO_OPENAI_API_KEY }} # customization - OCO_OPENAI_MAX_TOKENS: 500 + OCO_TOKENS_MAX_INPUT: 4096 + OCO_TOKENS_MAX_OUTPUT: 500 OCO_OPENAI_BASE_PATH: '' OCO_DESCRIPTION: false OCO_EMOJI: false diff --git a/src/commands/config.ts b/src/commands/config.ts index 49c5383..81c27d3 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -15,7 +15,8 @@ dotenv.config(); export enum CONFIG_KEYS { OCO_OPENAI_API_KEY = 'OCO_OPENAI_API_KEY', - OCO_OPENAI_MAX_TOKENS = 'OCO_OPENAI_MAX_TOKENS', + OCO_TOKENS_MAX_INPUT = 'OCO_TOKENS_MAX_INPUT', + OCO_TOKENS_MAX_OUTPUT = 'OCO_TOKENS_MAX_OUTPUT', OCO_OPENAI_BASE_PATH = 'OCO_OPENAI_BASE_PATH', OCO_DESCRIPTION = 'OCO_DESCRIPTION', OCO_EMOJI = 'OCO_EMOJI', @@ -26,13 +27,16 @@ export enum CONFIG_KEYS { OCO_AI_PROVIDER = 'OCO_AI_PROVIDER', } -export const DEFAULT_MODEL_TOKEN_LIMIT = 4096; - export enum CONFIG_MODES { get = 'get', set = 'set' } +export enum DEFAULT_TOKEN_LIMITS { + DEFAULT_MAX_TOKENS_INPUT = 4096, + DEFAULT_MAX_TOKENS_OUTPUT = 500 +} + const validateConfig = ( key: string, condition: any, @@ -75,18 +79,37 @@ export const configValidators = { return value; }, - [CONFIG_KEYS.OCO_OPENAI_MAX_TOKENS](value: any) { + [CONFIG_KEYS.OCO_TOKENS_MAX_INPUT](value: any) { // If the value is a string, convert it to a number. if (typeof value === 'string') { value = parseInt(value); validateConfig( - CONFIG_KEYS.OCO_OPENAI_MAX_TOKENS, + CONFIG_KEYS.OCO_TOKENS_MAX_INPUT, !isNaN(value), 'Must be a number' ); } validateConfig( - CONFIG_KEYS.OCO_OPENAI_MAX_TOKENS, + CONFIG_KEYS.OCO_TOKENS_MAX_INPUT, + value ? typeof value === 'number' : undefined, + 'Must be a number' + ); + + return value; + }, + + [CONFIG_KEYS.OCO_TOKENS_MAX_OUTPUT](value: any) { + // If the value is a string, convert it to a number. + if (typeof value === 'string') { + value = parseInt(value); + validateConfig( + CONFIG_KEYS.OCO_TOKENS_MAX_OUTPUT, + !isNaN(value), + 'Must be a number' + ); + } + validateConfig( + CONFIG_KEYS.OCO_TOKENS_MAX_OUTPUT, value ? typeof value === 'number' : undefined, 'Must be a number' ); @@ -178,8 +201,11 @@ const configPath = pathJoin(homedir(), '.opencommit'); export const getConfig = (): ConfigType | null => { const configFromEnv = { OCO_OPENAI_API_KEY: process.env.OCO_OPENAI_API_KEY, - OCO_OPENAI_MAX_TOKENS: process.env.OCO_OPENAI_MAX_TOKENS - ? Number(process.env.OCO_OPENAI_MAX_TOKENS) + OCO_TOKENS_MAX_INPUT: process.env.OCO_TOKENS_MAX_INPUT + ? Number(process.env.OCO_TOKENS_MAX_INPUT) + : undefined, + OCO_TOKENS_MAX_OUTPUT: process.env.OCO_TOKENS_MAX_OUTPUT + ? Number(process.env.OCO_TOKENS_MAX_OUTPUT) : undefined, OCO_OPENAI_BASE_PATH: process.env.OCO_OPENAI_BASE_PATH, OCO_DESCRIPTION: process.env.OCO_DESCRIPTION === 'true' ? true : false, diff --git a/src/engine/openAi.ts b/src/engine/openAi.ts index dc79082..4148073 100644 --- a/src/engine/openAi.ts +++ b/src/engine/openAi.ts @@ -11,7 +11,7 @@ import { intro, outro } from '@clack/prompts'; import { CONFIG_MODES, - DEFAULT_MODEL_TOKEN_LIMIT, + DEFAULT_TOKEN_LIMITS, getConfig } from '../commands/config'; import { GenerateCommitMessageErrorEnum } from '../generateCommitMessageFromGitDiff'; @@ -20,7 +20,8 @@ import { AiEngine } from './Engine'; const config = getConfig(); -let maxTokens = config?.OCO_OPENAI_MAX_TOKENS; +const MAX_TOKENS_OUTPUT = config?.OCO_TOKENS_MAX_OUTPUT || DEFAULT_TOKEN_LIMITS.DEFAULT_MAX_TOKENS_OUTPUT; +const MAX_TOKENS_INPUT = config?.OCO_TOKENS_MAX_INPUT || DEFAULT_TOKEN_LIMITS.DEFAULT_MAX_TOKENS_INPUT; let basePath = config?.OCO_OPENAI_BASE_PATH; let apiKey = config?.OCO_OPENAI_API_KEY @@ -65,14 +66,14 @@ class OpenAi implements AiEngine { messages, temperature: 0, top_p: 0.1, - max_tokens: maxTokens || 500 + max_tokens: MAX_TOKENS_OUTPUT }; try { const REQUEST_TOKENS = messages .map((msg) => tokenCount(msg.content) + 4) .reduce((a, b) => a + b, 0); - if (REQUEST_TOKENS > DEFAULT_MODEL_TOKEN_LIMIT - maxTokens) { + if (REQUEST_TOKENS > MAX_TOKENS_INPUT - MAX_TOKENS_OUTPUT) { throw new Error(GenerateCommitMessageErrorEnum.tooMuchTokens); } diff --git a/src/generateCommitMessageFromGitDiff.ts b/src/generateCommitMessageFromGitDiff.ts index 1e381c3..fe7bbfb 100644 --- a/src/generateCommitMessageFromGitDiff.ts +++ b/src/generateCommitMessageFromGitDiff.ts @@ -3,13 +3,15 @@ import { ChatCompletionRequestMessageRoleEnum } from 'openai'; -import { DEFAULT_MODEL_TOKEN_LIMIT, getConfig } from './commands/config'; +import { DEFAULT_TOKEN_LIMITS, getConfig } from './commands/config'; import { getMainCommitPrompt } from './prompts'; import { mergeDiffs } from './utils/mergeDiffs'; import { tokenCount } from './utils/tokenCount'; import { getEngine } from './utils/engine'; const config = getConfig(); +const MAX_TOKENS_INPUT = config?.OCO_TOKENS_MAX_INPUT || DEFAULT_TOKEN_LIMITS.DEFAULT_MAX_TOKENS_INPUT; +const MAX_TOKENS_OUTPUT = config?.OCO_TOKENS_MAX_OUTPUT || DEFAULT_TOKEN_LIMITS.DEFAULT_MAX_TOKENS_OUTPUT; const generateCommitMessageChatCompletionPrompt = async ( diff: string @@ -29,7 +31,8 @@ const generateCommitMessageChatCompletionPrompt = async ( export enum GenerateCommitMessageErrorEnum { tooMuchTokens = 'TOO_MUCH_TOKENS', internalError = 'INTERNAL_ERROR', - emptyMessage = 'EMPTY_MESSAGE' + emptyMessage = 'EMPTY_MESSAGE', + outputTokensTooHigh = `Token limit exceeded, OCO_TOKENS_MAX_OUTPUT must not be much higher than the default ${DEFAULT_TOKEN_LIMITS.DEFAULT_MAX_TOKENS_OUTPUT} tokens.` } const ADJUSTMENT_FACTOR = 20; @@ -45,10 +48,10 @@ export const generateCommitMessageByDiff = async ( ).reduce((a, b) => a + b, 0); const MAX_REQUEST_TOKENS = - DEFAULT_MODEL_TOKEN_LIMIT - + MAX_TOKENS_INPUT - ADJUSTMENT_FACTOR - INIT_MESSAGES_PROMPT_LENGTH - - config?.OCO_OPENAI_MAX_TOKENS; + MAX_TOKENS_OUTPUT; if (tokenCount(diff) >= MAX_REQUEST_TOKENS) { const commitMessagePromises = await getCommitMsgsPromisesFromFileDiffs( @@ -124,6 +127,10 @@ function splitDiff(diff: string, maxChangeLength: number) { const splitDiffs = []; let currentDiff = ''; + if (maxChangeLength <= 0) { + throw new Error(GenerateCommitMessageErrorEnum.outputTokensTooHigh); + } + for (let line of lines) { // If a single line exceeds maxChangeLength, split it into multiple lines while (tokenCount(line) > maxChangeLength) {