From 45aed936b1632ee09e0c7b10939483b62d79ed23 Mon Sep 17 00:00:00 2001 From: frauniki Date: Sun, 15 Jun 2025 17:29:12 +0900 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20clean=20up=20c?= =?UTF-8?q?ode=20formatting=20and=20improve=20readability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix inconsistent indentation across multiple engine files - Remove trailing whitespace and add missing newlines - Improve code formatting in prompt generation functions - Break long lines for better readability - Standardize spacing and brackets placement --- src/engine/azure.ts | 2 +- src/engine/groq.ts | 2 +- src/engine/mistral.ts | 12 ++-- src/engine/mlx.ts | 72 ++++++++++----------- src/engine/ollama.ts | 8 +-- src/engine/openAi.ts | 8 +-- src/generateCommitMessageFromGitDiff.ts | 9 ++- src/modules/commitlint/prompts.ts | 85 ++++++++++++++----------- src/modules/commitlint/utils.ts | 2 +- src/prompts.ts | 28 ++++---- src/utils/removeContentTags.ts | 18 ++++-- 11 files changed, 134 insertions(+), 112 deletions(-) diff --git a/src/engine/azure.ts b/src/engine/azure.ts index acbf0d3..72c8307 100644 --- a/src/engine/azure.ts +++ b/src/engine/azure.ts @@ -53,7 +53,7 @@ export class AzureEngine implements AiEngine { if (message?.content === null) { return undefined; } - + let content = message?.content; return removeContentTags(content, 'think'); } catch (error) { diff --git a/src/engine/groq.ts b/src/engine/groq.ts index baa6410..bed882d 100644 --- a/src/engine/groq.ts +++ b/src/engine/groq.ts @@ -7,4 +7,4 @@ export class GroqEngine extends OpenAiEngine { config.baseURL = 'https://api.groq.com/openai/v1'; super(config); } -} \ No newline at end of file +} diff --git a/src/engine/mistral.ts b/src/engine/mistral.ts index 8a7e932..5f97d82 100644 --- a/src/engine/mistral.ts +++ b/src/engine/mistral.ts @@ -23,7 +23,10 @@ export class MistralAiEngine implements AiEngine { if (!config.baseURL) { this.client = new Mistral({ apiKey: config.apiKey }); } else { - this.client = new Mistral({ apiKey: config.apiKey, serverURL: config.baseURL }); + this.client = new Mistral({ + apiKey: config.apiKey, + serverURL: config.baseURL + }); } } @@ -50,13 +53,12 @@ export class MistralAiEngine implements AiEngine { const completion = await this.client.chat.complete(params); - if (!completion.choices) - throw Error('No completion choice available.') - + if (!completion.choices) throw Error('No completion choice available.'); + const message = completion.choices[0].message; if (!message || !message.content) - throw Error('No completion choice available.') + throw Error('No completion choice available.'); let content = message.content as string; return removeContentTags(content, 'think'); diff --git a/src/engine/mlx.ts b/src/engine/mlx.ts index f83a19a..70e60b7 100644 --- a/src/engine/mlx.ts +++ b/src/engine/mlx.ts @@ -6,42 +6,42 @@ import { AiEngine, AiEngineConfig } from './Engine'; interface MLXConfig extends AiEngineConfig {} export class MLXEngine implements AiEngine { - config: MLXConfig; - client: AxiosInstance; + config: MLXConfig; + client: AxiosInstance; - constructor(config) { - this.config = config; - this.client = axios.create({ - url: config.baseURL - ? `${config.baseURL}/${config.apiKey}` - : 'http://localhost:8080/v1/chat/completions', - headers: { 'Content-Type': 'application/json' } - }); + constructor(config) { + this.config = config; + this.client = axios.create({ + url: config.baseURL + ? `${config.baseURL}/${config.apiKey}` + : 'http://localhost:8080/v1/chat/completions', + headers: { 'Content-Type': 'application/json' } + }); + } + + async generateCommitMessage( + messages: Array + ): Promise { + const params = { + messages, + temperature: 0, + top_p: 0.1, + repetition_penalty: 1.5, + stream: false + }; + try { + const response = await this.client.post( + this.client.getUri(this.config), + params + ); + + const choices = response.data.choices; + const message = choices[0].message; + let content = message?.content; + return removeContentTags(content, 'think'); + } catch (err: any) { + const message = err.response?.data?.error ?? err.message; + throw new Error(`MLX provider error: ${message}`); } - - async generateCommitMessage( - messages: Array): - Promise { - const params = { - messages, - temperature: 0, - top_p: 0.1, - repetition_penalty: 1.5, - stream: false - }; - try { - const response = await this.client.post( - this.client.getUri(this.config), - params - ); - - const choices = response.data.choices; - const message = choices[0].message; - let content = message?.content; - return removeContentTags(content, 'think'); - } catch (err: any) { - const message = err.response?.data?.error ?? err.message; - throw new Error(`MLX provider error: ${message}`); - } - } + } } diff --git a/src/engine/ollama.ts b/src/engine/ollama.ts index 7d0355b..3ddaf16 100644 --- a/src/engine/ollama.ts +++ b/src/engine/ollama.ts @@ -11,13 +11,13 @@ export class OllamaEngine implements AiEngine { constructor(config) { this.config = config; - + // Combine base headers with custom headers - const headers = { + const headers = { 'Content-Type': 'application/json', - ...config.customHeaders + ...config.customHeaders }; - + this.client = axios.create({ url: config.baseURL ? `${config.baseURL}/${config.apiKey}` diff --git a/src/engine/openAi.ts b/src/engine/openAi.ts index 22a9b37..fc56617 100644 --- a/src/engine/openAi.ts +++ b/src/engine/openAi.ts @@ -18,18 +18,18 @@ export class OpenAiEngine implements AiEngine { const clientOptions: OpenAI.ClientOptions = { apiKey: config.apiKey }; - + if (config.baseURL) { clientOptions.baseURL = config.baseURL; } - + if (config.customHeaders) { const headers = parseCustomHeaders(config.customHeaders); if (Object.keys(headers).length > 0) { clientOptions.defaultHeaders = headers; } } - + this.client = new OpenAI(clientOptions); } @@ -54,7 +54,7 @@ export class OpenAiEngine implements AiEngine { this.config.maxTokensInput - this.config.maxTokensOutput ) throw new Error(GenerateCommitMessageErrorEnum.tooMuchTokens); - + const completion = await this.client.chat.completions.create(params); const message = completion.choices[0].message; diff --git a/src/generateCommitMessageFromGitDiff.ts b/src/generateCommitMessageFromGitDiff.ts index 546de68..4c4d038 100644 --- a/src/generateCommitMessageFromGitDiff.ts +++ b/src/generateCommitMessageFromGitDiff.ts @@ -14,7 +14,10 @@ const generateCommitMessageChatCompletionPrompt = async ( fullGitMojiSpec: boolean, context: string ): Promise> => { - const INIT_MESSAGES_PROMPT = await getMainCommitPrompt(fullGitMojiSpec, context); + const INIT_MESSAGES_PROMPT = await getMainCommitPrompt( + fullGitMojiSpec, + context + ); const chatContextAsCompletionRequest = [...INIT_MESSAGES_PROMPT]; @@ -38,7 +41,7 @@ const ADJUSTMENT_FACTOR = 20; export const generateCommitMessageByDiff = async ( diff: string, fullGitMojiSpec: boolean = false, - context: string = "" + context: string = '' ): Promise => { try { const INIT_MESSAGES_PROMPT = await getMainCommitPrompt( @@ -75,7 +78,7 @@ export const generateCommitMessageByDiff = async ( const messages = await generateCommitMessageChatCompletionPrompt( diff, fullGitMojiSpec, - context, + context ); const engine = getEngine(); diff --git a/src/modules/commitlint/prompts.ts b/src/modules/commitlint/prompts.ts index 8dd81c5..127838f 100644 --- a/src/modules/commitlint/prompts.ts +++ b/src/modules/commitlint/prompts.ts @@ -56,10 +56,11 @@ const llmReadableRules: { blankline: (key, applicable) => `There should ${applicable} be a blank line at the beginning of the ${key}.`, caseRule: (key, applicable, value: string | Array) => - `The ${key} should ${applicable} be in ${Array.isArray(value) - ? `one of the following case: + `The ${key} should ${applicable} be in ${ + Array.isArray(value) + ? `one of the following case: - ${value.join('\n - ')}.` - : `${value} case.` + : `${value} case.` }`, emptyRule: (key, applicable) => `The ${key} should ${applicable} be empty.`, enumRule: (key, applicable, value: string | Array) => @@ -67,17 +68,18 @@ const llmReadableRules: { - ${Array.isArray(value) ? value.join('\n - ') : value}.`, enumTypeRule: (key, applicable, value: string | Array, prompt) => `The ${key} should ${applicable} be one of the following values: - - ${Array.isArray(value) + - ${ + Array.isArray(value) ? value - .map((v) => { - const description = getTypeRuleExtraDescription(v, prompt); - if (description) { - return `${v} (${description})`; - } else return v; - }) - .join('\n - ') + .map((v) => { + const description = getTypeRuleExtraDescription(v, prompt); + if (description) { + return `${v} (${description})`; + } else return v; + }) + .join('\n - ') : value - }.`, + }.`, fullStopRule: (key, applicable, value: string) => `The ${key} should ${applicable} end with '${value}'.`, maxLengthRule: (key, applicable, value: string) => @@ -214,16 +216,20 @@ const STRUCTURE_OF_COMMIT = config.OCO_OMIT_SCOPE const GEN_COMMITLINT_CONSISTENCY_PROMPT = ( prompts: string[] ): OpenAI.Chat.Completions.ChatCompletionMessageParam[] => [ - { - role: 'system', - content: `${IDENTITY} Your mission is to create clean and comprehensive commit messages for two different changes in a single codebase and output them in the provided JSON format: one for a bug fix and another for a new feature. + { + role: 'system', + content: `${IDENTITY} Your mission is to create clean and comprehensive commit messages for two different changes in a single codebase and output them in the provided JSON format: one for a bug fix and another for a new feature. Here are the specific requirements and conventions that should be strictly followed: Commit Message Conventions: - The commit message consists of three parts: Header, Body, and Footer. - Header: - - Format: ${config.OCO_OMIT_SCOPE ? '`: `' : '`(): `'} + - Format: ${ + config.OCO_OMIT_SCOPE + ? '`: `' + : '`(): `' + } - ${prompts.join('\n- ')} JSON Output Format: @@ -246,9 +252,9 @@ Additional Details: - Allowing the server to listen on a port specified through the environment variable is considered a new feature. Example Git Diff is to follow:` - }, - INIT_DIFF_PROMPT - ]; + }, + INIT_DIFF_PROMPT +]; /** * Prompt to have LLM generate a message using @commitlint rules. @@ -262,25 +268,30 @@ const INIT_MAIN_PROMPT = ( prompts: string[] ): OpenAI.Chat.Completions.ChatCompletionMessageParam => ({ role: 'system', - content: `${IDENTITY} Your mission is to create clean and comprehensive commit messages in the given @commitlint convention and explain WHAT were the changes ${config.OCO_WHY ? 'and WHY the changes were done' : '' - }. I'll send you an output of 'git diff --staged' command, and you convert it into a commit message. -${config.OCO_EMOJI - ? 'Use GitMoji convention to preface the commit.' - : 'Do not preface the commit with anything.' - } -${config.OCO_DESCRIPTION - ? 'Add a short description of WHY the changes are done after the commit message. Don\'t start it with "This commit", just describe the changes.' - : "Don't add any descriptions to the commit, only commit message." - } + content: `${IDENTITY} Your mission is to create clean and comprehensive commit messages in the given @commitlint convention and explain WHAT were the changes ${ + config.OCO_WHY ? 'and WHY the changes were done' : '' + }. I'll send you an output of 'git diff --staged' command, and you convert it into a commit message. +${ + config.OCO_EMOJI + ? 'Use GitMoji convention to preface the commit.' + : 'Do not preface the commit with anything.' +} +${ + config.OCO_DESCRIPTION + ? 'Add a short description of WHY the changes are done after the commit message. Don\'t start it with "This commit", just describe the changes.' + : "Don't add any descriptions to the commit, only commit message." +} Use the present tense. Use ${language} to answer. -${config.OCO_ONE_LINE_COMMIT - ? 'Craft a concise commit message that encapsulates all changes made, with an emphasis on the primary updates. If the modifications share a common theme or scope, mention it succinctly; otherwise, leave the scope out to maintain focus. The goal is to provide a clear and unified overview of the changes in a one single message, without diverging into a list of commit per file change.' - : '' - } -${config.OCO_OMIT_SCOPE - ? 'Do not include a scope in the commit message format. Use the format: : ' - : '' - } +${ + config.OCO_ONE_LINE_COMMIT + ? 'Craft a concise commit message that encapsulates all changes made, with an emphasis on the primary updates. If the modifications share a common theme or scope, mention it succinctly; otherwise, leave the scope out to maintain focus. The goal is to provide a clear and unified overview of the changes in a one single message, without diverging into a list of commit per file change.' + : '' +} +${ + config.OCO_OMIT_SCOPE + ? 'Do not include a scope in the commit message format. Use the format: : ' + : '' +} You will strictly follow the following conventions to generate the content of the commit message: - ${prompts.join('\n- ')} diff --git a/src/modules/commitlint/utils.ts b/src/modules/commitlint/utils.ts index 1e02c8a..9bcc78c 100644 --- a/src/modules/commitlint/utils.ts +++ b/src/modules/commitlint/utils.ts @@ -21,7 +21,7 @@ export const getJSONBlock = (input: string): string => { if (jsonIndex > -1) { input = input.slice(jsonIndex + 8); const endJsonIndex = input.search('```'); - input = input.slice(0, endJsonIndex); + input = input.slice(0, endJsonIndex); } return input; }; diff --git a/src/prompts.ts b/src/prompts.ts index 020fa6c..7967a58 100644 --- a/src/prompts.ts +++ b/src/prompts.ts @@ -155,9 +155,9 @@ const INIT_MAIN_PROMPT = ( }); export const INIT_DIFF_PROMPT: OpenAI.Chat.Completions.ChatCompletionMessageParam = -{ - role: 'user', - content: `diff --git a/src/server.ts b/src/server.ts + { + role: 'user', + content: `diff --git a/src/server.ts b/src/server.ts index ad4db42..f3b18a9 100644 --- a/src/server.ts +++ b/src/server.ts @@ -181,7 +181,7 @@ export const INIT_DIFF_PROMPT: OpenAI.Chat.Completions.ChatCompletionMessagePara +app.listen(process.env.PORT || PORT, () => { + console.log(\`Server listening on port \${PORT}\`); });` -}; + }; const COMMIT_TYPES = { fix: '🐛', @@ -193,19 +193,19 @@ const generateCommitString = ( message: string ): string => { const cleanMessage = removeConventionalCommitWord(message); - return config.OCO_EMOJI - ? `${COMMIT_TYPES[type]} ${cleanMessage}` - : message; + return config.OCO_EMOJI ? `${COMMIT_TYPES[type]} ${cleanMessage}` : message; }; const getConsistencyContent = (translation: ConsistencyPrompt) => { - const fixMessage = config.OCO_OMIT_SCOPE && translation.commitFixOmitScope - ? translation.commitFixOmitScope - : translation.commitFix; + const fixMessage = + config.OCO_OMIT_SCOPE && translation.commitFixOmitScope + ? translation.commitFixOmitScope + : translation.commitFix; - const featMessage = config.OCO_OMIT_SCOPE && translation.commitFeatOmitScope - ? translation.commitFeatOmitScope - : translation.commitFeat; + const featMessage = + config.OCO_OMIT_SCOPE && translation.commitFeatOmitScope + ? translation.commitFeatOmitScope + : translation.commitFeat; const fix = generateCommitString('fix', fixMessage); const feat = config.OCO_ONE_LINE_COMMIT @@ -250,7 +250,7 @@ export const getMainCommitPrompt = async ( INIT_DIFF_PROMPT, INIT_CONSISTENCY_PROMPT( commitLintConfig.consistency[ - translation.localLanguage + translation.localLanguage ] as ConsistencyPrompt ) ]; diff --git a/src/utils/removeContentTags.ts b/src/utils/removeContentTags.ts index c665bf7..89562c3 100644 --- a/src/utils/removeContentTags.ts +++ b/src/utils/removeContentTags.ts @@ -4,20 +4,23 @@ * @param tag The tag name without angle brackets (e.g., 'think' for '') * @returns The content with the specified tags and their contents removed, and trimmed */ -export function removeContentTags(content: T, tag: string): T { +export function removeContentTags( + content: T, + tag: string +): T { if (!content || typeof content !== 'string') { return content; } - + // Dynamic implementation for other cases const openTag = `<${tag}>`; const closeTag = ``; - + // Parse the content and remove tags let result = ''; let skipUntil: number | null = null; let depth = 0; - + for (let i = 0; i < content.length; i++) { // Check for opening tag if (content.substring(i, i + openTag.length) === openTag) { @@ -29,7 +32,10 @@ export function removeContentTags(content: } } // Check for closing tag - else if (content.substring(i, i + closeTag.length) === closeTag && depth > 0) { + else if ( + content.substring(i, i + closeTag.length) === closeTag && + depth > 0 + ) { depth--; if (depth === 0) { i = i + closeTag.length - 1; // Skip the closing tag @@ -37,7 +43,7 @@ export function removeContentTags(content: continue; } } - + // Only add character if not inside a tag if (skipUntil === null) { result += content[i];