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.
Files changed (153) hide show
  1. package/.cr-aia.yml +23 -0
  2. package/.crignore +0 -0
  3. package/dist/index.js +27 -0
  4. package/package.json +85 -0
  5. package/src/analysis/FindingsExtractor.ts +431 -0
  6. package/src/analysis/ai-detection/analyzers/BaseAnalyzer.ts +267 -0
  7. package/src/analysis/ai-detection/analyzers/DocumentationAnalyzer.ts +622 -0
  8. package/src/analysis/ai-detection/analyzers/GitHistoryAnalyzer.ts +430 -0
  9. package/src/analysis/ai-detection/core/AIDetectionEngine.ts +467 -0
  10. package/src/analysis/ai-detection/types/DetectionTypes.ts +406 -0
  11. package/src/analysis/ai-detection/utils/SubmissionConverter.ts +390 -0
  12. package/src/analysis/context/ReviewContext.ts +378 -0
  13. package/src/analysis/context/index.ts +7 -0
  14. package/src/analysis/index.ts +8 -0
  15. package/src/analysis/tokens/TokenAnalysisFormatter.ts +154 -0
  16. package/src/analysis/tokens/TokenAnalyzer.ts +747 -0
  17. package/src/analysis/tokens/index.ts +8 -0
  18. package/src/clients/base/abstractClient.ts +190 -0
  19. package/src/clients/base/httpClient.ts +160 -0
  20. package/src/clients/base/index.ts +12 -0
  21. package/src/clients/base/modelDetection.ts +107 -0
  22. package/src/clients/base/responseProcessor.ts +586 -0
  23. package/src/clients/factory/clientFactory.ts +55 -0
  24. package/src/clients/factory/index.ts +8 -0
  25. package/src/clients/implementations/index.ts +8 -0
  26. package/src/clients/implementations/openRouterClient.ts +411 -0
  27. package/src/clients/openRouterClient.ts +863 -0
  28. package/src/clients/openRouterClientWrapper.ts +44 -0
  29. package/src/clients/utils/directoryStructure.ts +52 -0
  30. package/src/clients/utils/index.ts +11 -0
  31. package/src/clients/utils/languageDetection.ts +44 -0
  32. package/src/clients/utils/promptFormatter.ts +105 -0
  33. package/src/clients/utils/promptLoader.ts +53 -0
  34. package/src/clients/utils/tokenCounter.ts +297 -0
  35. package/src/core/ApiClientSelector.ts +37 -0
  36. package/src/core/ConfigurationService.ts +591 -0
  37. package/src/core/ConsolidationService.ts +423 -0
  38. package/src/core/InteractiveDisplayManager.ts +81 -0
  39. package/src/core/OutputManager.ts +275 -0
  40. package/src/core/ReviewGenerator.ts +140 -0
  41. package/src/core/fileDiscovery.ts +237 -0
  42. package/src/core/handlers/EstimationHandler.ts +104 -0
  43. package/src/core/handlers/FileProcessingHandler.ts +204 -0
  44. package/src/core/handlers/OutputHandler.ts +125 -0
  45. package/src/core/handlers/ReviewExecutor.ts +104 -0
  46. package/src/core/reviewOrchestrator.ts +333 -0
  47. package/src/core/utils/ModelInfoUtils.ts +56 -0
  48. package/src/formatters/outputFormatter.ts +62 -0
  49. package/src/formatters/utils/IssueFormatters.ts +83 -0
  50. package/src/formatters/utils/JsonFormatter.ts +77 -0
  51. package/src/formatters/utils/MarkdownFormatters.ts +609 -0
  52. package/src/formatters/utils/MetadataFormatter.ts +269 -0
  53. package/src/formatters/utils/ModelInfoExtractor.ts +115 -0
  54. package/src/index.ts +27 -0
  55. package/src/plugins/PluginInterface.ts +50 -0
  56. package/src/plugins/PluginManager.ts +126 -0
  57. package/src/prompts/PromptManager.ts +69 -0
  58. package/src/prompts/cache/PromptCache.ts +50 -0
  59. package/src/prompts/promptText/common/variables/css-frameworks.json +33 -0
  60. package/src/prompts/promptText/common/variables/framework-versions.json +45 -0
  61. package/src/prompts/promptText/frameworks/react/comprehensive.hbs +19 -0
  62. package/src/prompts/promptText/languages/css/comprehensive.hbs +18 -0
  63. package/src/prompts/promptText/languages/generic/comprehensive.hbs +20 -0
  64. package/src/prompts/promptText/languages/html/comprehensive.hbs +18 -0
  65. package/src/prompts/promptText/languages/javascript/comprehensive.hbs +18 -0
  66. package/src/prompts/promptText/languages/python/comprehensive.hbs +18 -0
  67. package/src/prompts/promptText/languages/typescript/comprehensive.hbs +18 -0
  68. package/src/runtime/auth/service.ts +58 -0
  69. package/src/runtime/auth/session.ts +103 -0
  70. package/src/runtime/auth/types.ts +11 -0
  71. package/src/runtime/cliEntry.ts +196 -0
  72. package/src/runtime/errors.ts +13 -0
  73. package/src/runtime/fileCollector.ts +188 -0
  74. package/src/runtime/manifest.ts +64 -0
  75. package/src/runtime/openrouterProxy.ts +45 -0
  76. package/src/runtime/proxyConfig.ts +94 -0
  77. package/src/runtime/proxyEnvironment.ts +71 -0
  78. package/src/runtime/reportMerge.ts +102 -0
  79. package/src/runtime/reporting/markdownReportBuilder.ts +138 -0
  80. package/src/runtime/reporting/reportDataCollector.ts +234 -0
  81. package/src/runtime/reporting/summaryGenerator.ts +86 -0
  82. package/src/runtime/reviewPipeline.ts +155 -0
  83. package/src/runtime/runAiCodeReview.ts +153 -0
  84. package/src/runtime/runtimeConfig.ts +5 -0
  85. package/src/runtime/ui/Layout.tsx +57 -0
  86. package/src/runtime/ui/RuntimeApp.tsx +150 -0
  87. package/src/runtime/ui/inkModules.ts +73 -0
  88. package/src/runtime/ui/screens/AuthScreen.tsx +128 -0
  89. package/src/runtime/ui/screens/ModeSelection.tsx +52 -0
  90. package/src/runtime/ui/screens/ProgressScreen.tsx +55 -0
  91. package/src/runtime/ui/screens/ResultsScreen.tsx +76 -0
  92. package/src/strategies/ArchitecturalReviewStrategy.ts +54 -0
  93. package/src/strategies/CodingTestReviewStrategy.ts +920 -0
  94. package/src/strategies/ConsolidatedReviewStrategy.ts +59 -0
  95. package/src/strategies/ExtractPatternsReviewStrategy.ts +64 -0
  96. package/src/strategies/MultiPassReviewStrategy.ts +785 -0
  97. package/src/strategies/ReviewStrategy.ts +64 -0
  98. package/src/strategies/StrategyFactory.ts +79 -0
  99. package/src/strategies/index.ts +14 -0
  100. package/src/tokenizers/baseTokenizer.ts +61 -0
  101. package/src/tokenizers/gptTokenizer.ts +27 -0
  102. package/src/tokenizers/index.ts +8 -0
  103. package/src/types/apiResponses.ts +40 -0
  104. package/src/types/cli.ts +24 -0
  105. package/src/types/common.ts +39 -0
  106. package/src/types/configuration.ts +201 -0
  107. package/src/types/handlebars.d.ts +5 -0
  108. package/src/types/patch.d.ts +25 -0
  109. package/src/types/review.ts +294 -0
  110. package/src/types/reviewContext.d.ts +65 -0
  111. package/src/types/reviewSchema.ts +181 -0
  112. package/src/types/structuredReview.ts +167 -0
  113. package/src/types/tokenAnalysis.ts +56 -0
  114. package/src/utils/FileReader.ts +93 -0
  115. package/src/utils/FileWriter.ts +76 -0
  116. package/src/utils/PathGenerator.ts +97 -0
  117. package/src/utils/api/apiUtils.ts +14 -0
  118. package/src/utils/api/index.ts +1 -0
  119. package/src/utils/apiErrorHandler.ts +287 -0
  120. package/src/utils/ciDataCollector.ts +252 -0
  121. package/src/utils/codingTestConfigLoader.ts +466 -0
  122. package/src/utils/dependencies/aiDependencyAnalyzer.ts +454 -0
  123. package/src/utils/detection/frameworkDetector.ts +879 -0
  124. package/src/utils/detection/index.ts +10 -0
  125. package/src/utils/detection/projectTypeDetector.ts +518 -0
  126. package/src/utils/diagramGenerator.ts +206 -0
  127. package/src/utils/errorLogger.ts +60 -0
  128. package/src/utils/estimationUtils.ts +407 -0
  129. package/src/utils/fileFilters.ts +373 -0
  130. package/src/utils/fileSystem.ts +57 -0
  131. package/src/utils/index.ts +36 -0
  132. package/src/utils/logger.ts +240 -0
  133. package/src/utils/pathValidator.ts +98 -0
  134. package/src/utils/priorityFilter.ts +59 -0
  135. package/src/utils/projectDocs.ts +189 -0
  136. package/src/utils/promptPaths.ts +29 -0
  137. package/src/utils/promptTemplateManager.ts +157 -0
  138. package/src/utils/review/consolidateReview.ts +553 -0
  139. package/src/utils/review/fixDisplay.ts +100 -0
  140. package/src/utils/review/fixImplementation.ts +61 -0
  141. package/src/utils/review/index.ts +36 -0
  142. package/src/utils/review/interactiveProcessing.ts +294 -0
  143. package/src/utils/review/progressTracker.ts +296 -0
  144. package/src/utils/review/reviewExtraction.ts +382 -0
  145. package/src/utils/review/types.ts +46 -0
  146. package/src/utils/reviewActionHandler.ts +18 -0
  147. package/src/utils/reviewParser.ts +253 -0
  148. package/src/utils/sanitizer.ts +238 -0
  149. package/src/utils/smartFileSelector.ts +255 -0
  150. package/src/utils/templateLoader.ts +514 -0
  151. package/src/utils/treeGenerator.ts +153 -0
  152. package/tsconfig.build.json +14 -0
  153. package/tsconfig.json +59 -0
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @fileoverview Token analysis module exports.
3
+ *
4
+ * This module exports all token analysis related functions and types.
5
+ */
6
+
7
+ export * from './TokenAnalysisFormatter';
8
+ export * from './TokenAnalyzer';
@@ -0,0 +1,190 @@
1
+ /**
2
+ * @fileoverview Abstract base client for interacting with various AI APIs.
3
+ *
4
+ * This module defines an abstract base class that encapsulates common functionality
5
+ * across different AI client implementations (OpenAI, Anthropic, Google, etc.).
6
+ * It provides a unified interface for model detection, initialization, review generation,
7
+ * and other shared operations.
8
+ */
9
+
10
+ import type {
11
+ CostInfo,
12
+ FileInfo,
13
+ ReviewOptions,
14
+ ReviewResult,
15
+ ReviewType,
16
+ } from '../../types/review';
17
+ import type { StructuredReview } from '../../types/structuredReview';
18
+ import logger from '../../utils/logger';
19
+ import type { ProjectDocs } from '../../utils/projectDocs';
20
+
21
+ /**
22
+ * Abstract base class for AI model clients
23
+ */
24
+ export abstract class AbstractClient {
25
+ protected modelName = '';
26
+ protected isInitialized = false;
27
+
28
+ /**
29
+ * Check if the client is initialized
30
+ * @returns True if initialized, false otherwise
31
+ */
32
+ public getIsInitialized(): boolean {
33
+ return this.isInitialized;
34
+ }
35
+
36
+ /**
37
+ * Check if the provided model name is supported by this client
38
+ * @param modelName The full model name (potentially with provider prefix)
39
+ * @returns Object indicating if this is the correct client for the model, and parsed model info
40
+ */
41
+ public abstract isModelSupported(modelName: string): {
42
+ isCorrect: boolean;
43
+ adapter: string;
44
+ modelName: string;
45
+ };
46
+
47
+ /**
48
+ * Initialize the client with the appropriate configuration
49
+ * @returns Promise resolving to a boolean indicating success
50
+ */
51
+ public abstract initialize(): Promise<boolean>;
52
+
53
+ /**
54
+ * Generate a review for a single file
55
+ * @param fileContent Content of the file to review
56
+ * @param filePath Path to the file
57
+ * @param reviewType Type of review to perform
58
+ * @param projectDocs Optional project documentation
59
+ * @param options Review options
60
+ * @returns Promise resolving to the review result
61
+ */
62
+ public abstract generateReview(
63
+ fileContent: string,
64
+ filePath: string,
65
+ reviewType: ReviewType,
66
+ projectDocs?: ProjectDocs | null,
67
+ options?: ReviewOptions,
68
+ ): Promise<ReviewResult>;
69
+
70
+ /**
71
+ * Generate a consolidated review for multiple files
72
+ * @param files Array of file information objects
73
+ * @param projectName Name of the project
74
+ * @param reviewType Type of review to perform
75
+ * @param projectDocs Optional project documentation
76
+ * @param options Review options
77
+ * @returns Promise resolving to the review result
78
+ */
79
+ public abstract generateConsolidatedReview(
80
+ files: FileInfo[],
81
+ projectName: string,
82
+ reviewType: ReviewType,
83
+ projectDocs?: ProjectDocs | null,
84
+ options?: ReviewOptions,
85
+ ): Promise<ReviewResult>;
86
+
87
+ /**
88
+ * Generate an architectural review for a project
89
+ * @param files Array of file information objects
90
+ * @param projectName Name of the project
91
+ * @param projectDocs Optional project documentation
92
+ * @param options Review options
93
+ * @returns Promise resolving to the review result
94
+ */
95
+ public abstract generateArchitecturalReview(
96
+ files: FileInfo[],
97
+ projectName: string,
98
+ projectDocs?: ProjectDocs | null,
99
+ options?: ReviewOptions,
100
+ ): Promise<ReviewResult>;
101
+
102
+ /**
103
+ * Process the response and extract structured data if possible
104
+ * @param content The response content to process
105
+ * @returns The processed structured data or null
106
+ */
107
+ protected processResponseForStructuredData(content: string): unknown | null {
108
+ try {
109
+ // First, check if the response is wrapped in a code block
110
+ const jsonMatch = content.match(/```(?:json)?\s*([\s\S]*?)\s*```/);
111
+ const jsonContent = jsonMatch ? jsonMatch[1] : content;
112
+
113
+ // Check if the content is valid JSON
114
+ const structuredData = JSON.parse(jsonContent);
115
+
116
+ // Validate that it has the expected structure
117
+ if (!structuredData.summary || !Array.isArray(structuredData.issues)) {
118
+ logger.warn('Response is valid JSON but does not have the expected structure');
119
+ }
120
+
121
+ return structuredData;
122
+ } catch (parseError) {
123
+ logger.warn(
124
+ `Response is not valid JSON: ${
125
+ parseError instanceof Error ? parseError.message : String(parseError)
126
+ }`,
127
+ );
128
+ // Return null for non-JSON content
129
+ return null;
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Create a standard review result object
135
+ * @param content The review content
136
+ * @param filePath The file path or identifier
137
+ * @param reviewType The type of review
138
+ * @param cost Optional cost information
139
+ * @returns The standardized review result object
140
+ */
141
+ protected createReviewResult(
142
+ content: string,
143
+ filePath: string,
144
+ reviewType: ReviewType,
145
+ cost?: CostInfo,
146
+ ): ReviewResult {
147
+ const structuredData = this.processResponseForStructuredData(content);
148
+
149
+ return {
150
+ content,
151
+ cost,
152
+ modelUsed: this.getFullModelName(),
153
+ filePath,
154
+ reviewType,
155
+ timestamp: new Date().toISOString(),
156
+ structuredData: structuredData as StructuredReview | undefined,
157
+ };
158
+ }
159
+
160
+ /**
161
+ * Get the full model name including provider prefix
162
+ * @returns The full model name
163
+ */
164
+ protected getFullModelName(): string {
165
+ const modelParts = this.modelName.split(':');
166
+ if (modelParts.length === 2) {
167
+ return this.modelName; // Already has provider prefix
168
+ }
169
+ return `${this.getProviderName()}:${this.modelName}`;
170
+ }
171
+
172
+ /**
173
+ * Get the provider name for this client
174
+ * @returns The provider name (e.g., "openai", "anthropic", "gemini")
175
+ */
176
+ protected abstract getProviderName(): string;
177
+
178
+ /**
179
+ * Handle common error cases in API interactions
180
+ * @param error The error that occurred
181
+ * @param operation The operation that was being performed
182
+ * @param filePath The file path or identifier related to the error
183
+ * @throws The processed error
184
+ */
185
+ protected handleApiError(error: unknown, operation: string, filePath: string): never {
186
+ const errorMessage = error instanceof Error ? error.message : String(error);
187
+ logger.error(`Error ${operation} for ${filePath}: ${errorMessage}`);
188
+ throw error;
189
+ }
190
+ }
@@ -0,0 +1,160 @@
1
+ /**
2
+ * @fileoverview HTTP client utilities for API interactions.
3
+ *
4
+ * This module provides common HTTP request handling functionality with retry logic,
5
+ * error handling, and timeout management. It's designed to be used by the various
6
+ * AI API client implementations.
7
+ */
8
+
9
+ import { ApiError } from '../../utils/apiErrorHandler';
10
+ import logger from '../../utils/logger';
11
+
12
+ /**
13
+ * Fetch with automatic retry for transient errors
14
+ * @param url The URL to fetch from
15
+ * @param options Request options
16
+ * @param retries Number of retries to attempt
17
+ * @returns Promise resolving to the Response object
18
+ * @throws Error if all retries fail
19
+ */
20
+ export async function fetchWithRetry(
21
+ url: string,
22
+ options: RequestInit,
23
+ retries = 3,
24
+ ): Promise<Response> {
25
+ logger.debug(`[FETCH DEBUG] fetchWithRetry called with url: ${url}`);
26
+ for (let i = 0; i < retries; i++) {
27
+ try {
28
+ logger.debug(`Making API request to ${url} (attempt ${i + 1}/${retries})`);
29
+ const res = await fetch(url, options);
30
+
31
+ if (res.ok) {
32
+ // Log successful response headers for debugging (OpenRouter specific)
33
+ const requestId = res.headers.get('x-request-id') || res.headers.get('request-id');
34
+ if (requestId) {
35
+ logger.debug(`Successful API response - Request ID: ${requestId}`);
36
+ }
37
+ return res;
38
+ }
39
+
40
+ logger.error(`[FETCH DEBUG] Request failed with status: ${res.status}`);
41
+
42
+ // Handle rate limiting and server errors with exponential backoff
43
+ if (res.status === 429 || res.status >= 500) {
44
+ const retryAfter = res.headers.get('Retry-After');
45
+ const delayMs = retryAfter ? parseInt(retryAfter, 10) * 1000 : 1000 * 2 ** i;
46
+
47
+ logger.warn(`Request failed with status ${res.status}. Retrying in ${delayMs / 1000}s...`);
48
+ await new Promise((r) => setTimeout(r, delayMs));
49
+ } else {
50
+ // For other errors, try to get more detailed information
51
+ try {
52
+ const errorBody = await res.json();
53
+ const requestId = res.headers.get('x-request-id') || res.headers.get('request-id');
54
+
55
+ logger.error(`API error response: ${JSON.stringify(errorBody, null, 2)}`);
56
+ logger.error(`Request URL: ${url}`);
57
+ logger.error(`Request headers: ${JSON.stringify(options.headers, null, 2)}`);
58
+
59
+ // Log request body if it exists (but mask API keys)
60
+ let modelInfo = '';
61
+ if (options.body) {
62
+ try {
63
+ const body = JSON.parse(options.body as string);
64
+ if (body.messages) {
65
+ // Only log message structure, not content
66
+ logger.error(`Request had ${body.messages.length} messages`);
67
+ }
68
+ modelInfo = body.model || '';
69
+ logger.error(`Request model: ${body.model}`);
70
+ logger.error(`Request max_tokens: ${body.max_tokens}`);
71
+ logger.error(`Request max_completion_tokens: ${body.max_completion_tokens}`);
72
+ } catch {
73
+ // If body isn't JSON, just note that
74
+ logger.error('Request body was not JSON');
75
+ }
76
+ }
77
+
78
+ // Create detailed error message
79
+ const errorDetails: string[] = [
80
+ `API request failed`,
81
+ `Endpoint: ${url}`,
82
+ `Status: ${res.status}`,
83
+ `Error: ${errorBody.error?.message || JSON.stringify(errorBody)}`,
84
+ ];
85
+
86
+ if (requestId) {
87
+ errorDetails.push(`Request ID: ${requestId}`);
88
+ }
89
+
90
+ if (modelInfo) {
91
+ errorDetails.push(`Model: ${modelInfo}`);
92
+ }
93
+
94
+ throw new ApiError(errorDetails.join('\n '));
95
+ } catch (parseError) {
96
+ // If we can't parse the error response, just use the status
97
+ if (parseError instanceof ApiError) {
98
+ throw parseError;
99
+ }
100
+
101
+ logger.error(`Failed to parse error response, status: ${res.status}`);
102
+ throw new ApiError(
103
+ `API request failed\n Endpoint: ${url}\n Status: ${res.status}\n Error: Unable to parse error response`,
104
+ );
105
+ }
106
+ }
107
+ } catch (error) {
108
+ // For network errors or other exceptions, retry if we have retries left
109
+ if (i < retries - 1 && !(error instanceof ApiError)) {
110
+ const delayMs = 1000 * 2 ** i;
111
+ logger.warn(
112
+ `Request failed with error: ${error instanceof Error ? error.message : String(error)}. Retrying in ${delayMs / 1000}s...`,
113
+ );
114
+ await new Promise((r) => setTimeout(r, delayMs));
115
+ } else {
116
+ throw error;
117
+ }
118
+ }
119
+ }
120
+
121
+ throw new ApiError(`Failed after ${retries} retries`);
122
+ }
123
+
124
+ /**
125
+ * Generic retry mechanism for any asynchronous function
126
+ * @param fn The function to retry
127
+ * @param retries Maximum number of retries
128
+ * @returns Promise resolving to the function's result
129
+ * @throws Error if all retries fail
130
+ */
131
+ export async function withRetry<T>(fn: () => Promise<T>, retries = 3): Promise<T> {
132
+ for (let i = 0; i < retries; i++) {
133
+ try {
134
+ return await fn();
135
+ } catch (error: unknown) {
136
+ // Type guard for errors with status property
137
+ const err = error as { status?: number };
138
+
139
+ // Handle rate limiting and server errors
140
+ if ((err.status === 429 || (err.status && err.status >= 500)) && i < retries - 1) {
141
+ const delayMs = 1000 * 2 ** i;
142
+ logger.warn(
143
+ `Operation failed with status ${err.status}. Retrying in ${delayMs / 1000}s...`,
144
+ );
145
+ await new Promise((r) => setTimeout(r, delayMs));
146
+ } else if (i < retries - 1) {
147
+ // For other errors, retry with backoff
148
+ const delayMs = 1000 * 2 ** i;
149
+ logger.warn(
150
+ `Operation failed: ${error instanceof Error ? error.message : String(error)}. Retrying in ${delayMs / 1000}s...`,
151
+ );
152
+ await new Promise((r) => setTimeout(r, delayMs));
153
+ } else {
154
+ throw error;
155
+ }
156
+ }
157
+ }
158
+
159
+ throw new ApiError(`Failed after ${retries} retries`);
160
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @fileoverview Common base functionality for AI API clients.
3
+ *
4
+ * This module serves as the main entry point for the base client functionality,
5
+ * providing a unified interface for client implementation, HTTP requests,
6
+ * response processing, and model detection.
7
+ */
8
+
9
+ export * from './abstractClient';
10
+ export * from './httpClient';
11
+ export * from './modelDetection';
12
+ export * from './responseProcessor';
@@ -0,0 +1,107 @@
1
+ /**
2
+ * @fileoverview Model detection and initialization utilities for AI clients.
3
+ *
4
+ * This module provides common functionality for detecting model providers,
5
+ * parsing model names, validating API keys, and initializing client instances.
6
+ */
7
+
8
+ import { getConfig } from '../../core/ConfigurationService';
9
+ import logger from '../../utils/logger';
10
+
11
+ /**
12
+ * Result of model detection
13
+ */
14
+ export interface ModelDetectionResult {
15
+ /** Whether this is the correct client for the model */
16
+ isCorrect: boolean;
17
+ /** The provider/adapter name (e.g., "openai", "anthropic", "gemini") */
18
+ adapter: string;
19
+ /** The specific model name without provider prefix */
20
+ modelName: string;
21
+ }
22
+
23
+ /**
24
+ * Parse a model name string into provider and model components
25
+ * @param modelNameString The model name string (possibly with provider prefix)
26
+ * @param defaultProvider The default provider to use if none is specified
27
+ * @returns Object with adapter and modelName properties
28
+ */
29
+ export function parseModelName(
30
+ modelNameString: string,
31
+ defaultProvider: string,
32
+ ): { adapter: string; modelName: string } {
33
+ if (!modelNameString) {
34
+ return { adapter: defaultProvider, modelName: '' };
35
+ }
36
+
37
+ // Parse the model name
38
+ return modelNameString.includes(':')
39
+ ? {
40
+ adapter: modelNameString.split(':')[0],
41
+ modelName: modelNameString.split(':')[1],
42
+ }
43
+ : {
44
+ adapter: defaultProvider,
45
+ modelName: modelNameString,
46
+ };
47
+ }
48
+
49
+ /**
50
+ * Detect if a model name is for a specific provider
51
+ * @param providerName The provider to check for
52
+ * @param modelNameString The model name to check (with or without provider prefix)
53
+ * @returns ModelDetectionResult with detection information
54
+ */
55
+ export function detectModelProvider(
56
+ providerName: string,
57
+ modelNameString?: string,
58
+ ): ModelDetectionResult {
59
+ // Get the model from configuration if not provided
60
+ const config = getConfig();
61
+ const selectedModel = modelNameString || config.model || '';
62
+
63
+ // Parse the model name
64
+ const { adapter, modelName } = parseModelName(selectedModel, providerName);
65
+
66
+ return {
67
+ isCorrect: adapter === providerName,
68
+ adapter,
69
+ modelName,
70
+ };
71
+ }
72
+
73
+ /**
74
+ * Validate API key for a provider
75
+ * @param providerName The provider to validate the API key for
76
+ * @param apiKeyName Environment variable name for the API key (optional)
77
+ * @returns Whether the API key is valid
78
+ */
79
+ export function validateApiKey(providerName: string, apiKeyName?: string): boolean {
80
+ // Only support OpenRouter
81
+ if (providerName.toLowerCase() !== 'openrouter') {
82
+ logger.error(`Provider ${providerName} is not supported. Only OpenRouter is supported.`);
83
+ return false;
84
+ }
85
+
86
+ // Try to get the API key from the config
87
+ const config = getConfig();
88
+ const apiKey = config.openRouterApiKey;
89
+
90
+ // Check if we have an API key
91
+ if (!apiKey) {
92
+ const envVarName = apiKeyName || 'AI_CODE_REVIEW_OPENROUTER_API_KEY';
93
+ logger.error(`No OpenRouter API key found in configuration.`);
94
+ logger.error('Please add the following to your .env.local file:');
95
+ logger.error(`- ${envVarName}=your_openrouter_api_key_here`);
96
+ return false;
97
+ }
98
+
99
+ // Log API key status
100
+ if (config.debug) {
101
+ logger.info(`Using real OpenRouter API responses.`);
102
+ } else {
103
+ logger.info(`API key found for OpenRouter. Using real API responses.`);
104
+ }
105
+
106
+ return true;
107
+ }