codereview-aia 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cr-aia.yml +23 -0
- package/.crignore +0 -0
- package/dist/index.js +27 -0
- package/package.json +85 -0
- package/src/analysis/FindingsExtractor.ts +431 -0
- package/src/analysis/ai-detection/analyzers/BaseAnalyzer.ts +267 -0
- package/src/analysis/ai-detection/analyzers/DocumentationAnalyzer.ts +622 -0
- package/src/analysis/ai-detection/analyzers/GitHistoryAnalyzer.ts +430 -0
- package/src/analysis/ai-detection/core/AIDetectionEngine.ts +467 -0
- package/src/analysis/ai-detection/types/DetectionTypes.ts +406 -0
- package/src/analysis/ai-detection/utils/SubmissionConverter.ts +390 -0
- package/src/analysis/context/ReviewContext.ts +378 -0
- package/src/analysis/context/index.ts +7 -0
- package/src/analysis/index.ts +8 -0
- package/src/analysis/tokens/TokenAnalysisFormatter.ts +154 -0
- package/src/analysis/tokens/TokenAnalyzer.ts +747 -0
- package/src/analysis/tokens/index.ts +8 -0
- package/src/clients/base/abstractClient.ts +190 -0
- package/src/clients/base/httpClient.ts +160 -0
- package/src/clients/base/index.ts +12 -0
- package/src/clients/base/modelDetection.ts +107 -0
- package/src/clients/base/responseProcessor.ts +586 -0
- package/src/clients/factory/clientFactory.ts +55 -0
- package/src/clients/factory/index.ts +8 -0
- package/src/clients/implementations/index.ts +8 -0
- package/src/clients/implementations/openRouterClient.ts +411 -0
- package/src/clients/openRouterClient.ts +863 -0
- package/src/clients/openRouterClientWrapper.ts +44 -0
- package/src/clients/utils/directoryStructure.ts +52 -0
- package/src/clients/utils/index.ts +11 -0
- package/src/clients/utils/languageDetection.ts +44 -0
- package/src/clients/utils/promptFormatter.ts +105 -0
- package/src/clients/utils/promptLoader.ts +53 -0
- package/src/clients/utils/tokenCounter.ts +297 -0
- package/src/core/ApiClientSelector.ts +37 -0
- package/src/core/ConfigurationService.ts +591 -0
- package/src/core/ConsolidationService.ts +423 -0
- package/src/core/InteractiveDisplayManager.ts +81 -0
- package/src/core/OutputManager.ts +275 -0
- package/src/core/ReviewGenerator.ts +140 -0
- package/src/core/fileDiscovery.ts +237 -0
- package/src/core/handlers/EstimationHandler.ts +104 -0
- package/src/core/handlers/FileProcessingHandler.ts +204 -0
- package/src/core/handlers/OutputHandler.ts +125 -0
- package/src/core/handlers/ReviewExecutor.ts +104 -0
- package/src/core/reviewOrchestrator.ts +333 -0
- package/src/core/utils/ModelInfoUtils.ts +56 -0
- package/src/formatters/outputFormatter.ts +62 -0
- package/src/formatters/utils/IssueFormatters.ts +83 -0
- package/src/formatters/utils/JsonFormatter.ts +77 -0
- package/src/formatters/utils/MarkdownFormatters.ts +609 -0
- package/src/formatters/utils/MetadataFormatter.ts +269 -0
- package/src/formatters/utils/ModelInfoExtractor.ts +115 -0
- package/src/index.ts +27 -0
- package/src/plugins/PluginInterface.ts +50 -0
- package/src/plugins/PluginManager.ts +126 -0
- package/src/prompts/PromptManager.ts +69 -0
- package/src/prompts/cache/PromptCache.ts +50 -0
- package/src/prompts/promptText/common/variables/css-frameworks.json +33 -0
- package/src/prompts/promptText/common/variables/framework-versions.json +45 -0
- package/src/prompts/promptText/frameworks/react/comprehensive.hbs +19 -0
- package/src/prompts/promptText/languages/css/comprehensive.hbs +18 -0
- package/src/prompts/promptText/languages/generic/comprehensive.hbs +20 -0
- package/src/prompts/promptText/languages/html/comprehensive.hbs +18 -0
- package/src/prompts/promptText/languages/javascript/comprehensive.hbs +18 -0
- package/src/prompts/promptText/languages/python/comprehensive.hbs +18 -0
- package/src/prompts/promptText/languages/typescript/comprehensive.hbs +18 -0
- package/src/runtime/auth/service.ts +58 -0
- package/src/runtime/auth/session.ts +103 -0
- package/src/runtime/auth/types.ts +11 -0
- package/src/runtime/cliEntry.ts +196 -0
- package/src/runtime/errors.ts +13 -0
- package/src/runtime/fileCollector.ts +188 -0
- package/src/runtime/manifest.ts +64 -0
- package/src/runtime/openrouterProxy.ts +45 -0
- package/src/runtime/proxyConfig.ts +94 -0
- package/src/runtime/proxyEnvironment.ts +71 -0
- package/src/runtime/reportMerge.ts +102 -0
- package/src/runtime/reporting/markdownReportBuilder.ts +138 -0
- package/src/runtime/reporting/reportDataCollector.ts +234 -0
- package/src/runtime/reporting/summaryGenerator.ts +86 -0
- package/src/runtime/reviewPipeline.ts +155 -0
- package/src/runtime/runAiCodeReview.ts +153 -0
- package/src/runtime/runtimeConfig.ts +5 -0
- package/src/runtime/ui/Layout.tsx +57 -0
- package/src/runtime/ui/RuntimeApp.tsx +150 -0
- package/src/runtime/ui/inkModules.ts +73 -0
- package/src/runtime/ui/screens/AuthScreen.tsx +128 -0
- package/src/runtime/ui/screens/ModeSelection.tsx +52 -0
- package/src/runtime/ui/screens/ProgressScreen.tsx +55 -0
- package/src/runtime/ui/screens/ResultsScreen.tsx +76 -0
- package/src/strategies/ArchitecturalReviewStrategy.ts +54 -0
- package/src/strategies/CodingTestReviewStrategy.ts +920 -0
- package/src/strategies/ConsolidatedReviewStrategy.ts +59 -0
- package/src/strategies/ExtractPatternsReviewStrategy.ts +64 -0
- package/src/strategies/MultiPassReviewStrategy.ts +785 -0
- package/src/strategies/ReviewStrategy.ts +64 -0
- package/src/strategies/StrategyFactory.ts +79 -0
- package/src/strategies/index.ts +14 -0
- package/src/tokenizers/baseTokenizer.ts +61 -0
- package/src/tokenizers/gptTokenizer.ts +27 -0
- package/src/tokenizers/index.ts +8 -0
- package/src/types/apiResponses.ts +40 -0
- package/src/types/cli.ts +24 -0
- package/src/types/common.ts +39 -0
- package/src/types/configuration.ts +201 -0
- package/src/types/handlebars.d.ts +5 -0
- package/src/types/patch.d.ts +25 -0
- package/src/types/review.ts +294 -0
- package/src/types/reviewContext.d.ts +65 -0
- package/src/types/reviewSchema.ts +181 -0
- package/src/types/structuredReview.ts +167 -0
- package/src/types/tokenAnalysis.ts +56 -0
- package/src/utils/FileReader.ts +93 -0
- package/src/utils/FileWriter.ts +76 -0
- package/src/utils/PathGenerator.ts +97 -0
- package/src/utils/api/apiUtils.ts +14 -0
- package/src/utils/api/index.ts +1 -0
- package/src/utils/apiErrorHandler.ts +287 -0
- package/src/utils/ciDataCollector.ts +252 -0
- package/src/utils/codingTestConfigLoader.ts +466 -0
- package/src/utils/dependencies/aiDependencyAnalyzer.ts +454 -0
- package/src/utils/detection/frameworkDetector.ts +879 -0
- package/src/utils/detection/index.ts +10 -0
- package/src/utils/detection/projectTypeDetector.ts +518 -0
- package/src/utils/diagramGenerator.ts +206 -0
- package/src/utils/errorLogger.ts +60 -0
- package/src/utils/estimationUtils.ts +407 -0
- package/src/utils/fileFilters.ts +373 -0
- package/src/utils/fileSystem.ts +57 -0
- package/src/utils/index.ts +36 -0
- package/src/utils/logger.ts +240 -0
- package/src/utils/pathValidator.ts +98 -0
- package/src/utils/priorityFilter.ts +59 -0
- package/src/utils/projectDocs.ts +189 -0
- package/src/utils/promptPaths.ts +29 -0
- package/src/utils/promptTemplateManager.ts +157 -0
- package/src/utils/review/consolidateReview.ts +553 -0
- package/src/utils/review/fixDisplay.ts +100 -0
- package/src/utils/review/fixImplementation.ts +61 -0
- package/src/utils/review/index.ts +36 -0
- package/src/utils/review/interactiveProcessing.ts +294 -0
- package/src/utils/review/progressTracker.ts +296 -0
- package/src/utils/review/reviewExtraction.ts +382 -0
- package/src/utils/review/types.ts +46 -0
- package/src/utils/reviewActionHandler.ts +18 -0
- package/src/utils/reviewParser.ts +253 -0
- package/src/utils/sanitizer.ts +238 -0
- package/src/utils/smartFileSelector.ts +255 -0
- package/src/utils/templateLoader.ts +514 -0
- package/src/utils/treeGenerator.ts +153 -0
- package/tsconfig.build.json +14 -0
- package/tsconfig.json +59 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Wrapper for the OpenRouter client.
|
|
3
|
+
*
|
|
4
|
+
* This module provides a wrapper for the OpenRouter client to handle dynamic imports.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { FileInfo, ReviewOptions, ReviewResult, ReviewType } from '../types/review';
|
|
8
|
+
import type { ProjectDocs } from '../utils/projectDocs';
|
|
9
|
+
|
|
10
|
+
// Import the OpenRouter client directly
|
|
11
|
+
import * as openRouterClient from './openRouterClient';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Initialize the OpenRouter client
|
|
15
|
+
* @returns Promise resolving to a boolean indicating if initialization was successful
|
|
16
|
+
*/
|
|
17
|
+
export async function initializeAnyOpenRouterModel(): Promise<boolean> {
|
|
18
|
+
return openRouterClient.initializeAnyOpenRouterModel();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Generate a consolidated review using the OpenRouter API
|
|
23
|
+
* @param fileInfos Array of file information objects
|
|
24
|
+
* @param project Project name
|
|
25
|
+
* @param reviewType Type of review to perform
|
|
26
|
+
* @param projectDocs Optional project documentation
|
|
27
|
+
* @param options Review options
|
|
28
|
+
* @returns Promise resolving to the review result
|
|
29
|
+
*/
|
|
30
|
+
export async function generateOpenRouterConsolidatedReview(
|
|
31
|
+
fileInfos: FileInfo[],
|
|
32
|
+
project: string,
|
|
33
|
+
reviewType: ReviewType,
|
|
34
|
+
projectDocs: ProjectDocs | null,
|
|
35
|
+
options: ReviewOptions,
|
|
36
|
+
): Promise<ReviewResult> {
|
|
37
|
+
return openRouterClient.generateOpenRouterConsolidatedReview(
|
|
38
|
+
fileInfos,
|
|
39
|
+
project,
|
|
40
|
+
reviewType,
|
|
41
|
+
projectDocs,
|
|
42
|
+
options,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Utility for generating directory structure representations.
|
|
3
|
+
*
|
|
4
|
+
* This module provides functions for generating directory structure
|
|
5
|
+
* representations from file paths, which is useful for providing context
|
|
6
|
+
* in code reviews.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { FileInfo } from '../../types/review';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Generate a directory structure representation from file paths
|
|
13
|
+
* @param files Array of file information objects
|
|
14
|
+
* @returns String representation of directory structure
|
|
15
|
+
*/
|
|
16
|
+
export function generateDirectoryStructure(files: FileInfo[]): string {
|
|
17
|
+
const structure: Record<string, any> = {};
|
|
18
|
+
|
|
19
|
+
// Build tree structure
|
|
20
|
+
for (const file of files) {
|
|
21
|
+
// Skip files without relativePath
|
|
22
|
+
if (!file.relativePath) continue;
|
|
23
|
+
|
|
24
|
+
const parts = file.relativePath.split('/');
|
|
25
|
+
let current = structure;
|
|
26
|
+
|
|
27
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
28
|
+
const part = parts[i];
|
|
29
|
+
if (!current[part]) {
|
|
30
|
+
current[part] = {};
|
|
31
|
+
}
|
|
32
|
+
current = current[part];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const fileName = parts[parts.length - 1];
|
|
36
|
+
current[fileName] = null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Convert to string representation
|
|
40
|
+
function stringifyStructure(obj: Record<string, any>, indent = 0): string {
|
|
41
|
+
let result = '';
|
|
42
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
43
|
+
result += `${' '.repeat(indent) + (value === null ? '📄 ' : '📁 ') + key}\n`;
|
|
44
|
+
if (value !== null) {
|
|
45
|
+
result += stringifyStructure(value, indent + 1);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return stringifyStructure(structure);
|
|
52
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Index file for client utilities.
|
|
3
|
+
*
|
|
4
|
+
* This module exports all client utilities for easy importing.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export * from './directoryStructure';
|
|
8
|
+
export * from './languageDetection';
|
|
9
|
+
export * from './promptFormatter';
|
|
10
|
+
export * from './promptLoader';
|
|
11
|
+
export { estimateTokenCount, getCostInfo, getCostInfoFromText } from './tokenCounter';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Utilities for detecting programming languages from file extensions.
|
|
3
|
+
*
|
|
4
|
+
* This module provides functions for mapping file extensions to programming languages,
|
|
5
|
+
* which is useful for formatting code snippets and providing language-specific hints
|
|
6
|
+
* to AI models.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get the language name from a file extension
|
|
11
|
+
* @param extension File extension
|
|
12
|
+
* @returns Language name
|
|
13
|
+
*/
|
|
14
|
+
export function getLanguageFromExtension(extension: string): string {
|
|
15
|
+
const extensionMap: Record<string, string> = {
|
|
16
|
+
js: 'javascript',
|
|
17
|
+
jsx: 'javascript',
|
|
18
|
+
ts: 'typescript',
|
|
19
|
+
tsx: 'typescript',
|
|
20
|
+
py: 'python',
|
|
21
|
+
rb: 'ruby',
|
|
22
|
+
java: 'java',
|
|
23
|
+
go: 'go',
|
|
24
|
+
rs: 'rust',
|
|
25
|
+
php: 'php',
|
|
26
|
+
cs: 'csharp',
|
|
27
|
+
cpp: 'cpp',
|
|
28
|
+
c: 'c',
|
|
29
|
+
h: 'c',
|
|
30
|
+
hpp: 'cpp',
|
|
31
|
+
swift: 'swift',
|
|
32
|
+
kt: 'kotlin',
|
|
33
|
+
md: 'markdown',
|
|
34
|
+
json: 'json',
|
|
35
|
+
yml: 'yaml',
|
|
36
|
+
yaml: 'yaml',
|
|
37
|
+
html: 'html',
|
|
38
|
+
css: 'css',
|
|
39
|
+
scss: 'scss',
|
|
40
|
+
sql: 'sql',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
return extensionMap[extension.toLowerCase()] || extension;
|
|
44
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Utilities for formatting prompts for AI models.
|
|
3
|
+
*
|
|
4
|
+
* This module provides functions for formatting prompts for different AI models,
|
|
5
|
+
* including handling code blocks, project context, and review instructions.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import type { FileInfo } from '../../types/review';
|
|
10
|
+
import { formatProjectDocs, type ProjectDocs } from '../../utils/projectDocs';
|
|
11
|
+
import { getLanguageFromExtension } from '../utils/languageDetection';
|
|
12
|
+
import { generateDirectoryStructure } from './directoryStructure';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Format a code block with the appropriate language
|
|
16
|
+
* @param fileContent The content of the file
|
|
17
|
+
* @param filePath The path to the file
|
|
18
|
+
* @returns The formatted code block
|
|
19
|
+
*/
|
|
20
|
+
export function formatCodeBlock(fileContent: string, filePath: string): string {
|
|
21
|
+
// Get the file extension and language
|
|
22
|
+
const fileExtension = path.extname(filePath).slice(1);
|
|
23
|
+
const language = getLanguageFromExtension(fileExtension);
|
|
24
|
+
|
|
25
|
+
// Format the code block with language
|
|
26
|
+
return `\`\`\`${language}\n${fileContent}\n\`\`\``;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Format a single file review prompt
|
|
31
|
+
* @param promptTemplate The prompt template
|
|
32
|
+
* @param fileContent The content of the file
|
|
33
|
+
* @param filePath The path to the file
|
|
34
|
+
* @param projectDocs Optional project documentation
|
|
35
|
+
* @returns The formatted prompt
|
|
36
|
+
*/
|
|
37
|
+
export function formatSingleFileReviewPrompt(
|
|
38
|
+
promptTemplate: string,
|
|
39
|
+
fileContent: string,
|
|
40
|
+
filePath: string,
|
|
41
|
+
projectDocs?: ProjectDocs | null,
|
|
42
|
+
): string {
|
|
43
|
+
// Format the code block
|
|
44
|
+
const codeBlock = formatCodeBlock(fileContent, filePath);
|
|
45
|
+
|
|
46
|
+
// Format project documentation if available
|
|
47
|
+
const projectContext = projectDocs ? formatProjectDocs(projectDocs) : '';
|
|
48
|
+
|
|
49
|
+
// Prepare the user prompt
|
|
50
|
+
return `${promptTemplate}
|
|
51
|
+
|
|
52
|
+
${projectContext ? `## Project Context\n${projectContext}\n\n` : ''}## File to Review: ${filePath}
|
|
53
|
+
|
|
54
|
+
${codeBlock}
|
|
55
|
+
|
|
56
|
+
Please review this code and provide feedback according to the instructions. DO NOT REPEAT THE INSTRUCTIONS. DO NOT ASK FOR CODE TO REVIEW. FOCUS ONLY ON PROVIDING THE CODE REVIEW CONTENT.`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Format a consolidated review prompt
|
|
61
|
+
* @param promptTemplate The prompt template
|
|
62
|
+
* @param projectName The name of the project
|
|
63
|
+
* @param files Array of file information
|
|
64
|
+
* @param projectDocs Optional project documentation
|
|
65
|
+
* @returns The formatted prompt
|
|
66
|
+
*/
|
|
67
|
+
export function formatConsolidatedReviewPrompt(
|
|
68
|
+
promptTemplate: string,
|
|
69
|
+
projectName: string,
|
|
70
|
+
files: Array<{ relativePath?: string; content: string; sizeInBytes: number }>,
|
|
71
|
+
projectDocs?: ProjectDocs | null,
|
|
72
|
+
): string {
|
|
73
|
+
// Format project documentation if available
|
|
74
|
+
const projectContext = projectDocs ? formatProjectDocs(projectDocs) : '';
|
|
75
|
+
|
|
76
|
+
// Convert the file array to FileInfo format for the directory structure generator
|
|
77
|
+
const fileInfos: FileInfo[] = files.map((file) => ({
|
|
78
|
+
path: file.relativePath || file.sizeInBytes.toString(), // Use sizeInBytes as fallback for path
|
|
79
|
+
relativePath: file.relativePath,
|
|
80
|
+
content: file.content,
|
|
81
|
+
}));
|
|
82
|
+
|
|
83
|
+
// Create a project structure summary using the utility function
|
|
84
|
+
const directoryStructure = generateDirectoryStructure(fileInfos);
|
|
85
|
+
|
|
86
|
+
// Prepare file summaries
|
|
87
|
+
const fileSummaries = files
|
|
88
|
+
.map((file) => `- ${file.relativePath || 'unnamed file'} (${file.sizeInBytes} bytes)`)
|
|
89
|
+
.join('\n');
|
|
90
|
+
|
|
91
|
+
// Prepare the user prompt
|
|
92
|
+
return `${promptTemplate}
|
|
93
|
+
|
|
94
|
+
${projectContext ? `## Project Context\n${projectContext}\n\n` : ''}## Project: ${projectName}
|
|
95
|
+
|
|
96
|
+
## Directory Structure
|
|
97
|
+
\`\`\`
|
|
98
|
+
${directoryStructure}
|
|
99
|
+
\`\`\`
|
|
100
|
+
|
|
101
|
+
## File Summaries
|
|
102
|
+
${fileSummaries}
|
|
103
|
+
|
|
104
|
+
Please review this codebase and provide feedback according to the instructions. DO NOT REPEAT THE INSTRUCTIONS. DO NOT ASK FOR CODE TO REVIEW. FOCUS ONLY ON PROVIDING THE CODE REVIEW CONTENT.`;
|
|
105
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Utilities for loading prompt templates.
|
|
3
|
+
*
|
|
4
|
+
* IMPORTANT: This module provides functions for loading prompt templates from BUNDLED PROMPTS ONLY.
|
|
5
|
+
* The system prioritizes bundled prompts and only falls back to file system prompts if a bundled prompt is not found.
|
|
6
|
+
*
|
|
7
|
+
* All core prompts are defined in the bundledPrompts.ts file and accessed through the PromptManager.
|
|
8
|
+
* This ensures that the system always has access to the prompts it needs, regardless of
|
|
9
|
+
* where it's installed or how it's packaged.
|
|
10
|
+
*
|
|
11
|
+
* This module is a compatibility layer that uses the PromptManager internally.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { PromptManager } from '../../prompts/PromptManager';
|
|
15
|
+
import type { ReviewOptions, ReviewType } from '../../types/review';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Load a prompt template
|
|
19
|
+
* @param reviewType Type of review to perform
|
|
20
|
+
* @param options Review options including language
|
|
21
|
+
* @returns Promise resolving to the prompt template
|
|
22
|
+
*
|
|
23
|
+
* IMPORTANT: This function prioritizes bundled prompts.
|
|
24
|
+
* The system will first try to use bundled prompts defined in bundledPrompts.ts.
|
|
25
|
+
* Only if a bundled prompt is not found will it fall back to custom templates.
|
|
26
|
+
*
|
|
27
|
+
* This ensures that the system always has access to the prompts it needs,
|
|
28
|
+
* regardless of where it's installed or how it's packaged.
|
|
29
|
+
*/
|
|
30
|
+
export async function loadPromptTemplate(
|
|
31
|
+
reviewType: ReviewType,
|
|
32
|
+
languageOrOptions?: string | ReviewOptions,
|
|
33
|
+
): Promise<string> {
|
|
34
|
+
// Get the prompt manager instance
|
|
35
|
+
const promptManager = PromptManager.getInstance();
|
|
36
|
+
|
|
37
|
+
// Convert string language to options object if needed
|
|
38
|
+
let options: ReviewOptions | undefined;
|
|
39
|
+
|
|
40
|
+
if (typeof languageOrOptions === 'string') {
|
|
41
|
+
options = {
|
|
42
|
+
language: languageOrOptions as any,
|
|
43
|
+
type: 'quick-fixes',
|
|
44
|
+
includeTests: false,
|
|
45
|
+
output: 'markdown',
|
|
46
|
+
};
|
|
47
|
+
} else {
|
|
48
|
+
options = languageOrOptions;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Use the prompt manager to get the template
|
|
52
|
+
return promptManager.getPromptTemplate(reviewType, options);
|
|
53
|
+
}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Token counting and cost estimation utilities for AI API usage.
|
|
3
|
+
*
|
|
4
|
+
* This module provides utilities for estimating token usage and associated costs
|
|
5
|
+
* when using OpenRouter models. It implements accurate token counting using
|
|
6
|
+
* model-specific tokenizers and maintains current pricing information for OpenRouter models.
|
|
7
|
+
*
|
|
8
|
+
* Key responsibilities:
|
|
9
|
+
* - Counting tokens for input and output text using model-specific tokenizers
|
|
10
|
+
* - Calculating approximate API costs based on token usage and model
|
|
11
|
+
* - Tracking different pricing tiers for various AI models
|
|
12
|
+
* - Providing cost information for review outputs
|
|
13
|
+
* - Supporting cost-aware decision making for API usage
|
|
14
|
+
*
|
|
15
|
+
* These utilities help users understand the resource usage and costs associated
|
|
16
|
+
* with their code reviews, enabling better planning and resource allocation.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { countTokens } from '../../tokenizers';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Utility functions for estimating token counts and costs for AI API calls
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Count tokens in a text using the appropriate tokenizer for a model
|
|
27
|
+
* @param text Text to count tokens for
|
|
28
|
+
* @param modelName Name of the model (optional)
|
|
29
|
+
* @returns Token count
|
|
30
|
+
*/
|
|
31
|
+
export function estimateTokenCount(text: string, modelName?: string): number {
|
|
32
|
+
return countTokens(text, modelName || 'fallback');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Current pricing for OpenRouter models
|
|
37
|
+
* Source: https://openrouter.ai/models
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Interface for model pricing with standard per-token costs
|
|
42
|
+
*/
|
|
43
|
+
interface StandardPricing {
|
|
44
|
+
inputTokenCost: number; // Cost per 1K input tokens in USD
|
|
45
|
+
outputTokenCost: number; // Cost per 1K output tokens in USD
|
|
46
|
+
type: 'standard';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Interface for model pricing with tiered costs based on token count
|
|
51
|
+
*/
|
|
52
|
+
interface TieredPricing {
|
|
53
|
+
type: 'tiered';
|
|
54
|
+
tiers: {
|
|
55
|
+
threshold: number; // Token threshold for this tier
|
|
56
|
+
inputTokenCost: number; // Cost per 1K input tokens in USD for this tier
|
|
57
|
+
outputTokenCost: number; // Cost per 1K output tokens in USD for this tier
|
|
58
|
+
}[];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Union type for different pricing models
|
|
63
|
+
*/
|
|
64
|
+
type ModelPricing = StandardPricing | TieredPricing;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Pricing information for OpenRouter models
|
|
68
|
+
*/
|
|
69
|
+
const MODEL_PRICING: Record<string, ModelPricing> = {
|
|
70
|
+
// OpenRouter models (via OpenRouter)
|
|
71
|
+
'anthropic/claude-3-opus': {
|
|
72
|
+
type: 'standard',
|
|
73
|
+
inputTokenCost: 0.015, // $15.00 per 1M tokens
|
|
74
|
+
outputTokenCost: 0.075, // $75.00 per 1M tokens
|
|
75
|
+
},
|
|
76
|
+
'anthropic/claude-3-sonnet': {
|
|
77
|
+
type: 'standard',
|
|
78
|
+
inputTokenCost: 0.003, // $3.00 per 1M tokens
|
|
79
|
+
outputTokenCost: 0.015, // $15.00 per 1M tokens
|
|
80
|
+
},
|
|
81
|
+
'anthropic/claude-3-haiku': {
|
|
82
|
+
type: 'standard',
|
|
83
|
+
inputTokenCost: 0.00025, // $0.25 per 1M tokens
|
|
84
|
+
outputTokenCost: 0.00125, // $1.25 per 1M tokens
|
|
85
|
+
},
|
|
86
|
+
'openai/gpt-4-turbo': {
|
|
87
|
+
type: 'standard',
|
|
88
|
+
inputTokenCost: 0.01, // $10.00 per 1M tokens
|
|
89
|
+
outputTokenCost: 0.03, // $30.00 per 1M tokens
|
|
90
|
+
},
|
|
91
|
+
'openai/gpt-4o': {
|
|
92
|
+
type: 'standard',
|
|
93
|
+
inputTokenCost: 0.005, // $5.00 per 1M tokens (OpenRouter pricing)
|
|
94
|
+
outputTokenCost: 0.015, // $15.00 per 1M tokens (OpenRouter pricing)
|
|
95
|
+
},
|
|
96
|
+
'openai/gpt-4o-mini': {
|
|
97
|
+
type: 'standard',
|
|
98
|
+
inputTokenCost: 0.00015, // $0.15 per 1M tokens (OpenRouter pricing)
|
|
99
|
+
outputTokenCost: 0.0006, // $0.60 per 1M tokens (OpenRouter pricing)
|
|
100
|
+
},
|
|
101
|
+
'openai/gpt-3.5-turbo': {
|
|
102
|
+
type: 'standard',
|
|
103
|
+
inputTokenCost: 0.0005, // $0.50 per 1M tokens (OpenRouter pricing)
|
|
104
|
+
outputTokenCost: 0.0015, // $1.50 per 1M tokens (OpenRouter pricing)
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
'x-ai/grok-4-fast': {
|
|
108
|
+
type: 'standard',
|
|
109
|
+
inputTokenCost: 0.0002, // $0.20 per 1M input tokens (approx)
|
|
110
|
+
outputTokenCost: 0.0005, // $0.50 per 1M output tokens (approx)
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
// Default fallback pricing (OpenRouter average)
|
|
114
|
+
default: {
|
|
115
|
+
type: 'standard',
|
|
116
|
+
inputTokenCost: 0.005, // $5.00 per 1M tokens
|
|
117
|
+
outputTokenCost: 0.015, // $15.00 per 1M tokens
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get the pricing for a specific model
|
|
123
|
+
* @param modelName Name of the model
|
|
124
|
+
* @returns Pricing information for the model
|
|
125
|
+
*/
|
|
126
|
+
function getModelPricing(modelName: string): ModelPricing {
|
|
127
|
+
// Handle OpenRouter model names (remove the 'openrouter:' prefix)
|
|
128
|
+
let actualModelName = modelName;
|
|
129
|
+
if (modelName.startsWith('openrouter:')) {
|
|
130
|
+
actualModelName = modelName.substring('openrouter:'.length);
|
|
131
|
+
} else if (modelName.startsWith('openrouter-')) {
|
|
132
|
+
actualModelName = modelName.substring('openrouter-'.length);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Check if we have pricing for this model
|
|
136
|
+
if (MODEL_PRICING[actualModelName]) {
|
|
137
|
+
return MODEL_PRICING[actualModelName];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Default fallback
|
|
141
|
+
return MODEL_PRICING.default;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Calculate the cost for a specific tier
|
|
146
|
+
* @param tokens Number of tokens
|
|
147
|
+
* @param tokenCost Cost per 1K tokens
|
|
148
|
+
* @param tierStart Start of the tier
|
|
149
|
+
* @param tierEnd End of the tier (or undefined for no upper limit)
|
|
150
|
+
* @returns Cost for this tier
|
|
151
|
+
*/
|
|
152
|
+
function calculateTierCost(
|
|
153
|
+
tokens: number,
|
|
154
|
+
tokenCost: number,
|
|
155
|
+
tierStart: number,
|
|
156
|
+
tierEnd?: number,
|
|
157
|
+
): number {
|
|
158
|
+
// Calculate how many tokens fall within this tier
|
|
159
|
+
const tierTokens = tierEnd
|
|
160
|
+
? Math.min(Math.max(0, tokens - tierStart), tierEnd - tierStart)
|
|
161
|
+
: Math.max(0, tokens - tierStart);
|
|
162
|
+
|
|
163
|
+
// Calculate the cost for this tier
|
|
164
|
+
return (tierTokens / 1000) * tokenCost;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Calculate the estimated cost for an AI API call
|
|
169
|
+
* @param inputTokens Number of input tokens
|
|
170
|
+
* @param outputTokens Number of output tokens
|
|
171
|
+
* @param modelName Name of the model used
|
|
172
|
+
* @returns Estimated cost in USD
|
|
173
|
+
*/
|
|
174
|
+
export function calculateCost(
|
|
175
|
+
inputTokens: number,
|
|
176
|
+
outputTokens: number,
|
|
177
|
+
modelName = 'openrouter:anthropic/claude-3-opus-20240229',
|
|
178
|
+
): number {
|
|
179
|
+
const pricing = getModelPricing(modelName);
|
|
180
|
+
|
|
181
|
+
let inputCost = 0;
|
|
182
|
+
let outputCost = 0;
|
|
183
|
+
|
|
184
|
+
if (pricing.type === 'standard') {
|
|
185
|
+
// Standard pricing is simple - just multiply by the cost per token
|
|
186
|
+
inputCost = (inputTokens / 1000) * pricing.inputTokenCost;
|
|
187
|
+
outputCost = (outputTokens / 1000) * pricing.outputTokenCost;
|
|
188
|
+
} else if (pricing.type === 'tiered') {
|
|
189
|
+
// Tiered pricing requires calculating costs for each tier
|
|
190
|
+
const tiers = pricing.tiers;
|
|
191
|
+
|
|
192
|
+
// Calculate input token cost across tiers
|
|
193
|
+
for (let i = 0; i < tiers.length; i++) {
|
|
194
|
+
const tierStart = tiers[i].threshold;
|
|
195
|
+
const tierEnd = i < tiers.length - 1 ? tiers[i + 1].threshold : undefined;
|
|
196
|
+
|
|
197
|
+
inputCost += calculateTierCost(inputTokens, tiers[i].inputTokenCost, tierStart, tierEnd);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Calculate output token cost across tiers
|
|
201
|
+
for (let i = 0; i < tiers.length; i++) {
|
|
202
|
+
const tierStart = tiers[i].threshold;
|
|
203
|
+
const tierEnd = i < tiers.length - 1 ? tiers[i + 1].threshold : undefined;
|
|
204
|
+
|
|
205
|
+
outputCost += calculateTierCost(outputTokens, tiers[i].outputTokenCost, tierStart, tierEnd);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return inputCost + outputCost;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Format a cost value as a currency string
|
|
214
|
+
* @param cost Cost value in USD
|
|
215
|
+
* @returns Formatted cost string
|
|
216
|
+
*/
|
|
217
|
+
export function formatCost(cost: number): string {
|
|
218
|
+
return `$${cost.toFixed(6)} USD`;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Cost information for an API call
|
|
223
|
+
*/
|
|
224
|
+
export interface CostInfo {
|
|
225
|
+
inputTokens: number;
|
|
226
|
+
outputTokens: number;
|
|
227
|
+
totalTokens: number;
|
|
228
|
+
estimatedCost: number;
|
|
229
|
+
formattedCost: string;
|
|
230
|
+
cost: number; // Alias for estimatedCost for backward compatibility
|
|
231
|
+
passCount?: number; // Number of passes in multi-pass review
|
|
232
|
+
perPassCosts?: PassCostInfo[]; // Cost breakdown per pass
|
|
233
|
+
contextMaintenanceFactor?: number; // Factor for context maintenance
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Cost information for a single pass in a multi-pass review
|
|
238
|
+
*/
|
|
239
|
+
export interface PassCostInfo {
|
|
240
|
+
passNumber: number;
|
|
241
|
+
inputTokens: number;
|
|
242
|
+
outputTokens: number;
|
|
243
|
+
totalTokens: number;
|
|
244
|
+
estimatedCost: number;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Calculate cost information for an API call based on text
|
|
249
|
+
* @param inputText Input text sent to the API
|
|
250
|
+
* @param outputText Output text received from the API
|
|
251
|
+
* @param modelName Name of the model used
|
|
252
|
+
* @returns Cost information
|
|
253
|
+
*/
|
|
254
|
+
export function getCostInfoFromText(
|
|
255
|
+
inputText: string,
|
|
256
|
+
outputText: string,
|
|
257
|
+
modelName = 'openrouter:anthropic/claude-3-opus-20240229',
|
|
258
|
+
): CostInfo {
|
|
259
|
+
const inputTokens = estimateTokenCount(inputText, modelName);
|
|
260
|
+
const outputTokens = estimateTokenCount(outputText, modelName);
|
|
261
|
+
const totalTokens = inputTokens + outputTokens;
|
|
262
|
+
const estimatedCost = calculateCost(inputTokens, outputTokens, modelName);
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
inputTokens,
|
|
266
|
+
outputTokens,
|
|
267
|
+
totalTokens,
|
|
268
|
+
estimatedCost,
|
|
269
|
+
formattedCost: formatCost(estimatedCost),
|
|
270
|
+
cost: estimatedCost, // Alias for backward compatibility
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Calculate cost information for an API call based on token counts
|
|
276
|
+
* @param inputTokens Number of input tokens
|
|
277
|
+
* @param outputTokens Number of output tokens
|
|
278
|
+
* @param modelName Name of the model used
|
|
279
|
+
* @returns Cost information
|
|
280
|
+
*/
|
|
281
|
+
export function getCostInfo(
|
|
282
|
+
inputTokens: number,
|
|
283
|
+
outputTokens: number,
|
|
284
|
+
modelName = 'openrouter:anthropic/claude-3-opus-20240229',
|
|
285
|
+
): CostInfo {
|
|
286
|
+
const totalTokens = inputTokens + outputTokens;
|
|
287
|
+
const estimatedCost = calculateCost(inputTokens, outputTokens, modelName);
|
|
288
|
+
|
|
289
|
+
return {
|
|
290
|
+
inputTokens,
|
|
291
|
+
outputTokens,
|
|
292
|
+
totalTokens,
|
|
293
|
+
estimatedCost,
|
|
294
|
+
formattedCost: formatCost(estimatedCost),
|
|
295
|
+
cost: estimatedCost, // Alias for backward compatibility
|
|
296
|
+
};
|
|
297
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import logger from '../utils/logger';
|
|
2
|
+
import { getConfig } from '../core/ConfigurationService';
|
|
3
|
+
import { initializeAnyOpenRouterModel } from '../clients/openRouterClientWrapper';
|
|
4
|
+
|
|
5
|
+
export type ApiClientType = 'OpenRouter';
|
|
6
|
+
|
|
7
|
+
export interface ApiClientConfig {
|
|
8
|
+
clientType: ApiClientType;
|
|
9
|
+
modelName: string;
|
|
10
|
+
initialized: boolean;
|
|
11
|
+
provider: 'openrouter';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function selectApiClient(cliOptions?: any): Promise<ApiClientConfig> {
|
|
15
|
+
logger.debug('selectApiClient called');
|
|
16
|
+
|
|
17
|
+
// Get model from ConfigurationService or CLI options
|
|
18
|
+
const config = getConfig(cliOptions);
|
|
19
|
+
const requestedModel = cliOptions?.model || config.model || '';
|
|
20
|
+
const normalized = requestedModel.replace(/['"`]/g, '').trim();
|
|
21
|
+
const [, modelName] = normalized.includes(':') ? normalized.split(':', 2) : ['openrouter', normalized];
|
|
22
|
+
|
|
23
|
+
if (!modelName) {
|
|
24
|
+
throw new Error('AI_CODE_REVIEW_MODEL must specify an OpenRouter-compatible model.');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
await initializeAnyOpenRouterModel();
|
|
28
|
+
|
|
29
|
+
logger.info(`Using OpenRouter model: ${modelName}`);
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
clientType: 'OpenRouter',
|
|
33
|
+
modelName: `openrouter:${modelName}`,
|
|
34
|
+
initialized: true,
|
|
35
|
+
provider: 'openrouter',
|
|
36
|
+
};
|
|
37
|
+
}
|