diff --git a/README.md b/README.md index 103231f4..bfaabd59 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,9 @@ with: incentives: requirePriceLabel: true contentEvaluator: + openAi: + model: "gpt-4o" + endpoint: "https://api.openai.com/v1" multipliers: - role: [ISSUE_SPECIFICATION] relevance: 1 diff --git a/src/configuration/content-evaluator-config.ts b/src/configuration/content-evaluator-config.ts index 6e60a35a..91a155c8 100644 --- a/src/configuration/content-evaluator-config.ts +++ b/src/configuration/content-evaluator-config.ts @@ -1,7 +1,22 @@ import { Static, Type } from "@sinclair/typebox"; import { commentType } from "./formatting-evaluator-config"; +const openAiType = Type.Object( + { + /** + * AI model to use for comment evaluation. + */ + model: Type.String({ default: "gpt-4o" }), + /** + * Specific endpoint to send the comments to. + */ + endpoint: Type.String({ default: "" }), + }, + { default: {} } +); + export const contentEvaluatorConfigurationType = Type.Object({ + openAi: openAiType, /** * Multipliers applied to different types of comments */ diff --git a/src/configuration/formatting-evaluator-config.ts b/src/configuration/formatting-evaluator-config.ts index 0a9161bd..30368c12 100644 --- a/src/configuration/formatting-evaluator-config.ts +++ b/src/configuration/formatting-evaluator-config.ts @@ -32,6 +32,7 @@ const htmlType = Type.Record(Type.String(), Type.Number(), { ul: 1, td: 1, hr: 0, + pre: 0, }, }); diff --git a/src/octokit.ts b/src/octokit.ts index 7bad219e..2d6a35eb 100644 --- a/src/octokit.ts +++ b/src/octokit.ts @@ -2,11 +2,11 @@ import { Octokit } from "@octokit/rest"; import { retry } from "@octokit/plugin-retry"; import program from "./parser/command-line"; import configuration from "./configuration/config-reader"; -import { paginateGraphQL, paginateGraphQLInterface } from "@octokit/plugin-paginate-graphql"; +import { paginateGraphQL } from "@octokit/plugin-paginate-graphql"; const customOctokit = Octokit.plugin(retry, paginateGraphQL); -let octokitInstance: (Octokit & paginateGraphQLInterface) | null = null; +let octokitInstance: InstanceType | null = null; function getOctokitInstance() { if (!octokitInstance) { diff --git a/src/parser/content-evaluator-module.ts b/src/parser/content-evaluator-module.ts index ec306e5c..56a0cbb2 100644 --- a/src/parser/content-evaluator-module.ts +++ b/src/parser/content-evaluator-module.ts @@ -18,8 +18,11 @@ import { GithubCommentScore, Module, Result } from "./processor"; * Evaluates and rates comments. */ export class ContentEvaluatorModule implements Module { - readonly _openAi = new OpenAI({ apiKey: OPENAI_API_KEY }); readonly _configuration: ContentEvaluatorConfiguration | null = configuration.incentives.contentEvaluator; + readonly _openAi = new OpenAI({ + apiKey: OPENAI_API_KEY, + ...(this._configuration?.openAi.endpoint && { baseURL: this._configuration.openAi.endpoint }), + }); private readonly _fixedRelevances: { [k: string]: number } = {}; _getEnumValue(key: CommentType) { @@ -86,7 +89,9 @@ export class ContentEvaluatorModule implements Module { } } - const relevancesByAI = await this._evaluateComments(specificationBody, commentsToEvaluate); + const relevancesByAI = commentsToEvaluate.length + ? await this._evaluateComments(specificationBody, commentsToEvaluate) + : {}; if (Object.keys(relevancesByAI).length !== commentsToEvaluate.length) { console.error("Relevance / Comment length mismatch! \nWill use 1 as relevance for missing comments."); @@ -137,7 +142,7 @@ export class ContentEvaluatorModule implements Module { const maxTokens = this._calculateMaxTokens(dummyResponse); const response: OpenAI.Chat.ChatCompletion = await this._openAi.chat.completions.create({ - model: "gpt-4o-2024-08-06", + model: this._configuration?.openAi.model || "gpt-4o-2024-08-06", response_format: { type: "json_object" }, messages: [ { diff --git a/src/parser/github-comment-module.ts b/src/parser/github-comment-module.ts index da9ef3a5..0aec633c 100644 --- a/src/parser/github-comment-module.ts +++ b/src/parser/github-comment-module.ts @@ -77,6 +77,10 @@ export class GithubCommentModule implements Module { async postComment(body: string, updateLastComment = true) { const { eventPayload } = program; + if (!this._configuration?.post) { + logger.debug("Won't post a comment since posting is disabled.", { body }); + return; + } if (updateLastComment && this._lastCommentId !== null) { await getOctokitInstance().issues.updateComment({ body, diff --git a/tests/__mocks__/results/valid-configuration.json b/tests/__mocks__/results/valid-configuration.json index f0d3108c..9ad69d3f 100644 --- a/tests/__mocks__/results/valid-configuration.json +++ b/tests/__mocks__/results/valid-configuration.json @@ -7,56 +7,40 @@ "evmNetworkId": 100, "evmPrivateEncrypted": "kmpTKq5Wh9r9x5j3U9GqZr3NYnjK2g0HtbzeUBOuLC2y3x8ja_SKBNlB2AZ6LigXHP_HeMitftVUtzmoj8CFfVP9SqjWoL6IPku1hVTWkdTn97g1IxzmjydFxjdcf0wuDW1hvVtoq3Uw5yALABqxcQ", "incentives": { - "requirePriceLabel": true, "contentEvaluator": { "multipliers": [ { - "role": [ - "ISSUE_SPECIFICATION" - ], - "relevance": 1 + "relevance": 1, + "role": ["ISSUE_SPECIFICATION"] }, { - "role": [ - "PULL_AUTHOR" - ], - "relevance": 1 + "relevance": 1, + "role": ["PULL_AUTHOR"] }, { - "role": [ - "PULL_ASSIGNEE" - ], - "relevance": 1 + "relevance": 1, + "role": ["PULL_ASSIGNEE"] }, { - "role": [ - "PULL_COLLABORATOR" - ], - "relevance": 1 + "relevance": 1, + "role": ["PULL_COLLABORATOR"] }, { - "role": [ - "PULL_CONTRIBUTOR" - ], - "relevance": 1 + "relevance": 1, + "role": ["PULL_CONTRIBUTOR"] } - ] - }, - "userExtractor": { - "redeemTask": true + ], + "openAi": { + "endpoint": "", + "model": "gpt-4o" + } }, "dataPurge": {}, "formattingEvaluator": { "multipliers": [ { "multiplier": 1, - "role": [ - "ISSUE_SPECIFICATION" - ], "rewards": { - "regex": { - "\\b\\w+\\b": 0.1 - }, "html": { "a": 1, "blockquote": 0, @@ -72,22 +56,21 @@ "hr": 0, "img": 0, "li": 1, - "ul": 1, "p": 1, + "pre": 0, "strong": 0, - "td": 1 + "td": 1, + "ul": 1 + }, + "regex": { + "\\b\\w+\\b": 0.1 } - } + }, + "role": ["ISSUE_SPECIFICATION"] }, { "multiplier": 1, - "role": [ - "ISSUE_AUTHOR" - ], "rewards": { - "regex": { - "\\b\\w+\\b": 0.2 - }, "html": { "a": 1, "blockquote": 0, @@ -103,22 +86,21 @@ "hr": 0, "img": 0, "li": 1, - "ul": 1, "p": 1, + "pre": 0, "strong": 0, - "td": 1 + "td": 1, + "ul": 1 + }, + "regex": { + "\\b\\w+\\b": 0.2 } - } + }, + "role": ["ISSUE_AUTHOR"] }, { "multiplier": 0, - "role": [ - "ISSUE_ASSIGNEE" - ], "rewards": { - "regex": { - "\\b\\w+\\b": 0 - }, "html": { "a": 1, "blockquote": 0, @@ -134,22 +116,21 @@ "hr": 0, "img": 0, "li": 1, - "ul": 1, "p": 1, + "pre": 0, "strong": 0, - "td": 1 + "td": 1, + "ul": 1 + }, + "regex": { + "\\b\\w+\\b": 0 } - } + }, + "role": ["ISSUE_ASSIGNEE"] }, { "multiplier": 1, - "role": [ - "ISSUE_COLLABORATOR" - ], "rewards": { - "regex": { - "\\b\\w+\\b": 0.1 - }, "html": { "a": 1, "blockquote": 0, @@ -165,22 +146,21 @@ "hr": 0, "img": 0, "li": 1, - "ul": 1, "p": 1, + "pre": 0, "strong": 0, - "td": 1 + "td": 1, + "ul": 1 + }, + "regex": { + "\\b\\w+\\b": 0.1 } - } + }, + "role": ["ISSUE_COLLABORATOR"] }, { "multiplier": 0.25, - "role": [ - "ISSUE_CONTRIBUTOR" - ], "rewards": { - "regex": { - "\\b\\w+\\b": 0.1 - }, "html": { "a": 1, "blockquote": 0, @@ -196,22 +176,21 @@ "hr": 0, "img": 0, "li": 1, - "ul": 1, "p": 1, + "pre": 0, "strong": 0, - "td": 1 + "td": 1, + "ul": 1 + }, + "regex": { + "\\b\\w+\\b": 0.1 } - } + }, + "role": ["ISSUE_CONTRIBUTOR"] }, { "multiplier": 0, - "role": [ - "PULL_SPECIFICATION" - ], "rewards": { - "regex": { - "\\b\\w+\\b": 0 - }, "html": { "a": 1, "blockquote": 0, @@ -227,22 +206,21 @@ "hr": 0, "img": 0, "li": 1, - "ul": 1, "p": 1, + "pre": 0, "strong": 0, - "td": 1 + "td": 1, + "ul": 1 + }, + "regex": { + "\\b\\w+\\b": 0 } - } + }, + "role": ["PULL_SPECIFICATION"] }, { "multiplier": 2, - "role": [ - "PULL_AUTHOR" - ], "rewards": { - "regex": { - "\\b\\w+\\b": 0.2 - }, "html": { "a": 1, "blockquote": 0, @@ -258,22 +236,21 @@ "hr": 0, "img": 0, "li": 1, - "ul": 1, "p": 1, + "pre": 0, "strong": 0, - "td": 1 + "td": 1, + "ul": 1 + }, + "regex": { + "\\b\\w+\\b": 0.2 } - } + }, + "role": ["PULL_AUTHOR"] }, { "multiplier": 1, - "role": [ - "PULL_ASSIGNEE" - ], "rewards": { - "regex": { - "\\b\\w+\\b": 0.1 - }, "html": { "a": 1, "blockquote": 0, @@ -289,22 +266,21 @@ "hr": 0, "img": 0, "li": 1, - "ul": 1, "p": 1, + "pre": 0, "strong": 0, - "td": 1 + "td": 1, + "ul": 1 + }, + "regex": { + "\\b\\w+\\b": 0.1 } - } + }, + "role": ["PULL_ASSIGNEE"] }, { "multiplier": 1, - "role": [ - "PULL_COLLABORATOR" - ], "rewards": { - "regex": { - "\\b\\w+\\b": 0.1 - }, "html": { "a": 1, "blockquote": 0, @@ -320,22 +296,21 @@ "hr": 0, "img": 0, "li": 1, - "ul": 1, "p": 1, + "pre": 0, "strong": 0, - "td": 1 + "td": 1, + "ul": 1 + }, + "regex": { + "\\b\\w+\\b": 0.1 } - } + }, + "role": ["PULL_COLLABORATOR"] }, { "multiplier": 0.25, - "role": [ - "PULL_CONTRIBUTOR" - ], "rewards": { - "regex": { - "\\b\\w+\\b": 0.1 - }, "html": { "a": 1, "blockquote": 0, @@ -351,12 +326,17 @@ "hr": 0, "img": 0, "li": 1, - "ul": 1, "p": 1, + "pre": 0, "strong": 0, - "td": 1 + "td": 1, + "ul": 1 + }, + "regex": { + "\\b\\w+\\b": 0.1 } - } + }, + "role": ["PULL_CONTRIBUTOR"] } ] }, @@ -364,6 +344,10 @@ "debug": true, "post": false }, - "permitGeneration": {} + "permitGeneration": {}, + "requirePriceLabel": true, + "userExtractor": { + "redeemTask": true + } } }