diff --git a/action/index.cjs b/action/index.cjs index a37bbd11..896311dc 100644 --- a/action/index.cjs +++ b/action/index.cjs @@ -150469,18 +150469,20 @@ const robot = (app) => { const includePatterns = (process.env.INCLUDE_PATTERNS || '').split(',').filter((v) => Boolean(v.trim())); loglevel_1.default.debug('ignoreList:', ignoreList); loglevel_1.default.debug('ignorePatterns:', ignorePatterns); + loglevel_1.default.debug('includePatterns:', includePatterns); changedFiles = changedFiles?.filter((file) => { const url = new URL(file.contents_url); + const pathname = decodeURIComponent(url.pathname); // if includePatterns is not empty, only include files that match the pattern if (includePatterns.length) { - return matchPatterns(includePatterns, url.pathname); + return matchPatterns(includePatterns, pathname); } if (ignoreList.includes(file.filename)) { return false; } // if ignorePatterns is not empty, ignore files that match the pattern if (ignorePatterns.length) { - return !matchPatterns(ignorePatterns, url.pathname); + return !matchPatterns(ignorePatterns, pathname); } return true; }); @@ -150502,10 +150504,10 @@ const robot = (app) => { } try { const res = await chat?.codeReview(patch); - if (!!res) { + if (!res.lgtm && !!res.review_comment) { ress.push({ path: file.filename, - body: res, + body: res.review_comment, position: patch.split('\n').length - 1, }); } @@ -150519,7 +150521,7 @@ const robot = (app) => { repo: repo.repo, owner: repo.owner, pull_number: context.pullRequest().pull_number, - body: "Code review by ChatGPT", + body: ress.length ? "Code review by ChatGPT" : "LGTM 👍", event: 'COMMENT', commit_id: commits[commits.length - 1].sha, comments: ress, @@ -150588,15 +150590,23 @@ class Chat { const answerLanguage = process.env.LANGUAGE ? `Answer me in ${process.env.LANGUAGE},` : ''; - const prompt = process.env.PROMPT || - 'Below is a code patch, please help me do a brief code review on it. Any bug risks and/or improvement suggestions are welcome:'; - return `${prompt}, ${answerLanguage}: - ${patch} + const userPrompt = process.env.PROMPT || 'Please review the following code patch. Focus on potential bugs, risks, and improvement suggestions.'; + const jsonFormatRequirement = '\nProvide your feedback in a strict JSON format with the following structure:\n' + + '{\n' + + ' "lgtm": boolean, // true if the code looks good to merge, false if there are concerns\n' + + ' "review_comment": string // Your detailed review comments. You can use markdown syntax in this string, but the overall response must be a valid JSON\n' + + '}\n' + + 'Ensure your response is a valid JSON object.\n'; + return `${userPrompt}${jsonFormatRequirement} ${answerLanguage}: + ${patch} `; }; codeReview = async (patch) => { if (!patch) { - return ''; + return { + lgtm: true, + review_comment: "" + }; } console.time('code-review cost'); const prompt = this.generatePrompt(patch); @@ -150611,12 +150621,27 @@ class Chat { temperature: +(process.env.temperature || 0) || 1, top_p: +(process.env.top_p || 0) || 1, max_tokens: process.env.max_tokens ? +process.env.max_tokens : undefined, + response_format: { + type: "json_object" + }, }); console.timeEnd('code-review cost'); if (res.choices.length) { - return res.choices[0].message.content; + try { + const json = JSON.parse(res.choices[0].message.content || ""); + return json; + } + catch (e) { + return { + lgtm: false, + review_comment: res.choices[0].message.content || "" + }; + } } - return ''; + return { + lgtm: true, + review_comment: "" + }; }; } exports.Chat = Chat; diff --git a/action/src/chat.d.ts b/action/src/chat.d.ts index 88fd6b1f..00c54cd9 100644 --- a/action/src/chat.d.ts +++ b/action/src/chat.d.ts @@ -3,5 +3,8 @@ export declare class Chat { private isAzure; constructor(apikey: string); private generatePrompt; - codeReview: (patch: string) => Promise; + codeReview: (patch: string) => Promise<{ + lgtm: boolean; + review_comment: string; + }>; } diff --git a/src/bot.ts b/src/bot.ts index dc142eec..6c1b043f 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -110,13 +110,15 @@ export const robot = (app: Probot) => { log.debug('ignoreList:', ignoreList); log.debug('ignorePatterns:', ignorePatterns); + log.debug('includePatterns:', includePatterns); changedFiles = changedFiles?.filter( (file) => { const url = new URL(file.contents_url) + const pathname = decodeURIComponent(url.pathname) // if includePatterns is not empty, only include files that match the pattern if (includePatterns.length) { - return matchPatterns(includePatterns, url.pathname) + return matchPatterns(includePatterns, pathname) } if (ignoreList.includes(file.filename)) { @@ -125,7 +127,7 @@ export const robot = (app: Probot) => { // if ignorePatterns is not empty, ignore files that match the pattern if (ignorePatterns.length) { - return !matchPatterns(ignorePatterns, url.pathname) + return !matchPatterns(ignorePatterns, pathname) } return true @@ -156,10 +158,10 @@ export const robot = (app: Probot) => { } try { const res = await chat?.codeReview(patch); - if (!!res) { + if (!res.lgtm && !!res.review_comment) { ress.push({ path: file.filename, - body: res, + body: res.review_comment, position: patch.split('\n').length - 1, }) } @@ -172,7 +174,7 @@ export const robot = (app: Probot) => { repo: repo.repo, owner: repo.owner, pull_number: context.pullRequest().pull_number, - body: "Code review by ChatGPT", + body: ress.length ? "Code review by ChatGPT" : "LGTM 👍", event: 'COMMENT', commit_id: commits[commits.length - 1].sha, comments: ress, diff --git a/src/chat.ts b/src/chat.ts index b6a547b2..bb82e536 100644 --- a/src/chat.ts +++ b/src/chat.ts @@ -31,18 +31,26 @@ export class Chat { ? `Answer me in ${process.env.LANGUAGE},` : ''; - const prompt = - process.env.PROMPT || - 'Below is a code patch, please help me do a brief code review on it. Any bug risks and/or improvement suggestions are welcome:'; + const userPrompt = process.env.PROMPT || 'Please review the following code patch. Focus on potential bugs, risks, and improvement suggestions.'; + + const jsonFormatRequirement = '\nProvide your feedback in a strict JSON format with the following structure:\n' + + '{\n' + + ' "lgtm": boolean, // true if the code looks good to merge, false if there are concerns\n' + + ' "review_comment": string // Your detailed review comments. You can use markdown syntax in this string, but the overall response must be a valid JSON\n' + + '}\n' + + 'Ensure your response is a valid JSON object.\n'; - return `${prompt}, ${answerLanguage}: - ${patch} + return `${userPrompt}${jsonFormatRequirement} ${answerLanguage}: + ${patch} `; }; - public codeReview = async (patch: string) => { + public codeReview = async (patch: string): Promise<{ lgtm: boolean, review_comment: string }> => { if (!patch) { - return ''; + return { + lgtm: true, + review_comment: "" + }; } console.time('code-review cost'); @@ -59,14 +67,28 @@ export class Chat { temperature: +(process.env.temperature || 0) || 1, top_p: +(process.env.top_p || 0) || 1, max_tokens: process.env.max_tokens ? +process.env.max_tokens : undefined, + response_format: { + type: "json_object" + }, }); console.timeEnd('code-review cost'); if (res.choices.length) { - return res.choices[0].message.content; + try { + const json = JSON.parse(res.choices[0].message.content || ""); + return json + } catch (e) { + return { + lgtm: false, + review_comment: res.choices[0].message.content || "" + } + } } - return ''; + return { + lgtm: true, + review_comment: "" + } }; }