import {minimatch} from 'minimatch' import {TokenLimits} from './limits' const info = console.log export type SeverityLevel = 'critical' | 'major' | 'minor' | 'info' export class Options { debug: boolean disableReview: boolean disableReleaseNotes: boolean maxFiles: number reviewSimpleChanges: boolean reviewCommentLGTM: boolean pathFilters: PathFilter systemMessage: string lightModel: string heavyModel: string modelTemperature: number retries: number timeoutMS: number concurrencyLimit: number githubConcurrencyLimit: number lightTokenLimits: TokenLimits heavyTokenLimits: TokenLimits apiBaseUrl: string language: string createRemedyPR: boolean minimumSeverity: SeverityLevel minConfidence: number denseMode: boolean showImpactAnalysis: boolean enableAutoPR: boolean static readonly HEAVY_MODELS = [ 'llama-3.3-70b-versatile', 'meta-llama/llama-4-scout-17b-16e-instruct', 'deepseek-r1-distill-llama-70b', 'openai/gpt-oss-120b', 'qwen/qwen3-32b' ] static readonly LIGHT_MODELS = [ 'meta-llama/llama-4-scout-17b-16e-instruct', 'llama-3.1-8b-instant', 'groq/compound' ] static readonly SECURITY_MODELS = [ 'openai/gpt-oss-safeguard-20b', 'meta-llama/llama-guard-4-12b', 'meta-llama/llama-prompt-guard-2-86m', 'meta-llama/llama-prompt-guard-2-22m' ] constructor( debug: boolean, disableReview: boolean, disableReleaseNotes: boolean, maxFiles = '0', reviewSimpleChanges = false, reviewCommentLGTM = false, pathFilters: string[] | null = null, systemMessage = '', lightModel = 'meta-llama/llama-4-scout-17b-16e-instruct', heavyModel = 'llama-3.3-70b-versatile', modelTemperature = '0.0', retries = '3', timeoutMS = '120000', concurrencyLimit = '3', githubConcurrencyLimit = '10', apiBaseUrl = 'https://api.groq.com/openai/v1', language = 'en-US', createRemedyPR = true, minimumSeverity: SeverityLevel = 'minor', minConfidence = '0', denseMode = 'false', showImpactAnalysis = 'true', enableAutoPR = false ) { this.debug = debug this.disableReview = disableReview this.disableReleaseNotes = disableReleaseNotes this.maxFiles = parseInt(maxFiles) this.reviewSimpleChanges = reviewSimpleChanges this.reviewCommentLGTM = reviewCommentLGTM this.pathFilters = new PathFilter(pathFilters) this.systemMessage = systemMessage this.lightModel = lightModel this.heavyModel = heavyModel this.modelTemperature = parseFloat(modelTemperature) this.retries = parseInt(retries) this.timeoutMS = parseInt(timeoutMS) this.concurrencyLimit = parseInt(concurrencyLimit) this.githubConcurrencyLimit = parseInt(githubConcurrencyLimit) this.apiBaseUrl = apiBaseUrl this.language = language this.createRemedyPR = createRemedyPR this.minimumSeverity = minimumSeverity this.minConfidence = parseInt(minConfidence) this.denseMode = denseMode === 'true' this.showImpactAnalysis = showImpactAnalysis === 'true' this.enableAutoPR = enableAutoPR this.lightTokenLimits = new TokenLimits(this.lightModel) this.heavyTokenLimits = new TokenLimits(this.heavyModel) } // print all options using core.info print(): void { info(`debug: ${this.debug}`) info(`disable_review: ${this.disableReview}`) info(`disable_release_notes: ${this.disableReleaseNotes}`) info(`max_files: ${this.maxFiles}`) info(`review_simple_changes: ${this.reviewSimpleChanges}`) info(`review_comment_lgtm: ${this.reviewCommentLGTM}`) info(`path_filters: ${this.pathFilters}`) info(`system_message: ${this.systemMessage}`) info(`light_model: ${this.lightModel}`) info(`heavy_model: ${this.heavyModel}`) info(`model_temperature: ${this.modelTemperature}`) info(`retries: ${this.retries}`) info(`timeout_ms: ${this.timeoutMS}`) info(`concurrency_limit: ${this.concurrencyLimit}`) info(`github_concurrency_limit: ${this.githubConcurrencyLimit}`) info(`summary_token_limits: ${this.lightTokenLimits.string()}`) info(`review_token_limits: ${this.heavyTokenLimits.string()}`) info(`api_base_url: ${this.apiBaseUrl}`) info(`language: ${this.language}`) info(`create_remedy_pr: ${this.createRemedyPR}`) info(`enable_auto_pr: ${this.enableAutoPR}`) } checkPath(path: string): boolean { const ok = this.pathFilters.check(path) info(`checking path: ${path} => ${ok}`) return ok } private static readonly SEVERITY_ORDER: SeverityLevel[] = [ 'critical', 'major', 'minor', 'info' ] meetsMinimumSeverity(findingSeverity: SeverityLevel): boolean { const minIndex = Options.SEVERITY_ORDER.indexOf(this.minimumSeverity) const findingIndex = Options.SEVERITY_ORDER.indexOf(findingSeverity) return findingIndex >= minIndex } meetsConfidenceThreshold(confidence: number): boolean { return confidence >= this.minConfidence } } export class PathFilter { private readonly rules: Array<[string /* rule */, boolean /* exclude */]> constructor(rules: string[] | null = null) { this.rules = [] if (rules != null) { for (const rule of rules) { const trimmed = rule?.trim() if (trimmed) { if (trimmed.startsWith('!')) { this.rules.push([trimmed.substring(1).trim(), true]) } else { this.rules.push([trimmed, false]) } } } } } check(path: string): boolean { if (this.rules.length === 0) { return true } let included = false let excluded = false let inclusionRuleExists = false for (const [rule, exclude] of this.rules) { if (minimatch(path, rule)) { if (exclude) { excluded = true } else { included = true } } if (!exclude) { inclusionRuleExists = true } } return (!inclusionRuleExists || included) && !excluded } } export class AIOptions { model: string tokenLimits: TokenLimits temperature: number constructor( model = 'meta-llama/llama-4-scout-17b-16e-instruct', tokenLimits: TokenLimits | null = null, temperature = 0.0 ) { this.model = model this.temperature = temperature if (tokenLimits != null) { this.tokenLimits = tokenLimits } else { this.tokenLimits = new TokenLimits(model) } } }