@juspay/yama 1.6.0 → 2.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 (79) hide show
  1. package/.mcp-config.example.json +26 -0
  2. package/CHANGELOG.md +46 -0
  3. package/README.md +311 -685
  4. package/dist/cli/v2.cli.d.ts +13 -0
  5. package/dist/cli/v2.cli.js +359 -0
  6. package/dist/index.d.ts +12 -13
  7. package/dist/index.js +18 -19
  8. package/dist/v2/config/ConfigLoader.d.ts +50 -0
  9. package/dist/v2/config/ConfigLoader.js +205 -0
  10. package/dist/v2/config/DefaultConfig.d.ts +9 -0
  11. package/dist/v2/config/DefaultConfig.js +187 -0
  12. package/dist/v2/core/LearningOrchestrator.d.ts +65 -0
  13. package/dist/v2/core/LearningOrchestrator.js +499 -0
  14. package/dist/v2/core/MCPServerManager.d.ts +22 -0
  15. package/dist/v2/core/MCPServerManager.js +100 -0
  16. package/dist/v2/core/SessionManager.d.ts +72 -0
  17. package/dist/v2/core/SessionManager.js +200 -0
  18. package/dist/v2/core/YamaV2Orchestrator.d.ts +112 -0
  19. package/dist/v2/core/YamaV2Orchestrator.js +549 -0
  20. package/dist/v2/learning/FeedbackExtractor.d.ts +46 -0
  21. package/dist/v2/learning/FeedbackExtractor.js +237 -0
  22. package/dist/v2/learning/KnowledgeBaseManager.d.ts +91 -0
  23. package/dist/v2/learning/KnowledgeBaseManager.js +475 -0
  24. package/dist/v2/learning/types.d.ts +121 -0
  25. package/dist/v2/learning/types.js +15 -0
  26. package/dist/v2/prompts/EnhancementSystemPrompt.d.ts +8 -0
  27. package/dist/v2/prompts/EnhancementSystemPrompt.js +216 -0
  28. package/dist/v2/prompts/LangfusePromptManager.d.ts +48 -0
  29. package/dist/v2/prompts/LangfusePromptManager.js +144 -0
  30. package/dist/v2/prompts/LearningSystemPrompt.d.ts +11 -0
  31. package/dist/v2/prompts/LearningSystemPrompt.js +180 -0
  32. package/dist/v2/prompts/PromptBuilder.d.ts +45 -0
  33. package/dist/v2/prompts/PromptBuilder.js +257 -0
  34. package/dist/v2/prompts/ReviewSystemPrompt.d.ts +8 -0
  35. package/dist/v2/prompts/ReviewSystemPrompt.js +270 -0
  36. package/dist/v2/types/config.types.d.ts +141 -0
  37. package/dist/v2/types/config.types.js +5 -0
  38. package/dist/v2/types/mcp.types.d.ts +191 -0
  39. package/dist/v2/types/mcp.types.js +6 -0
  40. package/dist/v2/types/v2.types.d.ts +182 -0
  41. package/dist/v2/types/v2.types.js +42 -0
  42. package/dist/v2/utils/ObservabilityConfig.d.ts +22 -0
  43. package/dist/v2/utils/ObservabilityConfig.js +48 -0
  44. package/package.json +16 -10
  45. package/yama.config.example.yaml +259 -204
  46. package/dist/cli/index.d.ts +0 -12
  47. package/dist/cli/index.js +0 -538
  48. package/dist/core/ContextGatherer.d.ts +0 -110
  49. package/dist/core/ContextGatherer.js +0 -470
  50. package/dist/core/Guardian.d.ts +0 -81
  51. package/dist/core/Guardian.js +0 -480
  52. package/dist/core/providers/BitbucketProvider.d.ts +0 -105
  53. package/dist/core/providers/BitbucketProvider.js +0 -489
  54. package/dist/features/CodeReviewer.d.ts +0 -173
  55. package/dist/features/CodeReviewer.js +0 -1707
  56. package/dist/features/DescriptionEnhancer.d.ts +0 -70
  57. package/dist/features/DescriptionEnhancer.js +0 -511
  58. package/dist/features/MultiInstanceProcessor.d.ts +0 -74
  59. package/dist/features/MultiInstanceProcessor.js +0 -360
  60. package/dist/types/index.d.ts +0 -624
  61. package/dist/types/index.js +0 -104
  62. package/dist/utils/Cache.d.ts +0 -103
  63. package/dist/utils/Cache.js +0 -444
  64. package/dist/utils/ConfigManager.d.ts +0 -88
  65. package/dist/utils/ConfigManager.js +0 -602
  66. package/dist/utils/ContentSimilarityService.d.ts +0 -74
  67. package/dist/utils/ContentSimilarityService.js +0 -215
  68. package/dist/utils/ExactDuplicateRemover.d.ts +0 -77
  69. package/dist/utils/ExactDuplicateRemover.js +0 -361
  70. package/dist/utils/Logger.d.ts +0 -31
  71. package/dist/utils/Logger.js +0 -214
  72. package/dist/utils/MemoryBankManager.d.ts +0 -73
  73. package/dist/utils/MemoryBankManager.js +0 -310
  74. package/dist/utils/ParallelProcessing.d.ts +0 -140
  75. package/dist/utils/ParallelProcessing.js +0 -333
  76. package/dist/utils/ProviderLimits.d.ts +0 -58
  77. package/dist/utils/ProviderLimits.js +0 -143
  78. package/dist/utils/RetryManager.d.ts +0 -78
  79. package/dist/utils/RetryManager.js +0 -205
@@ -1,360 +0,0 @@
1
- /**
2
- * Multi-Instance Processor for Parallel Code Review
3
- * Orchestrates multiple Neurolink SDK instances for diverse code analysis
4
- */
5
- import { createCodeReviewer } from "./CodeReviewer.js";
6
- import { createExactDuplicateRemover, } from "../utils/ExactDuplicateRemover.js";
7
- import { Semaphore, TokenBudgetManager, calculateOptimalConcurrency, } from "../utils/ParallelProcessing.js";
8
- import { getProviderTokenLimit } from "../utils/ProviderLimits.js";
9
- import { logger } from "../utils/Logger.js";
10
- /**
11
- * Multi-Instance Processor
12
- * Manages parallel execution of multiple CodeReviewer instances
13
- */
14
- export class MultiInstanceProcessor {
15
- bitbucketProvider;
16
- baseReviewConfig;
17
- duplicateRemover;
18
- constructor(bitbucketProvider, baseReviewConfig) {
19
- this.bitbucketProvider = bitbucketProvider;
20
- this.baseReviewConfig = baseReviewConfig;
21
- this.duplicateRemover = createExactDuplicateRemover();
22
- }
23
- /**
24
- * Process code review using multiple instances
25
- */
26
- async processWithMultipleInstances(context, options, multiInstanceConfig) {
27
- const startTime = Date.now();
28
- try {
29
- logger.phase("🚀 Starting multi-instance code review processing");
30
- logger.info(`🔄 Launching ${multiInstanceConfig.instanceCount} instances: ${multiInstanceConfig.instances.map((i) => i.name).join(", ")}`);
31
- // Step 1: Validate configuration
32
- this.validateMultiInstanceConfig(multiInstanceConfig);
33
- // Step 2: Execute instances in parallel
34
- const instanceResults = await this.executeInstancesInParallel(context, options, multiInstanceConfig);
35
- // Step 3: Deduplicate results
36
- const deduplicationResult = multiInstanceConfig.deduplication.enabled
37
- ? this.duplicateRemover.removeDuplicates(instanceResults)
38
- : this.createNonDeduplicatedResult(instanceResults);
39
- // Step 4: Apply final filtering if configured
40
- const finalViolations = this.applyFinalFiltering(deduplicationResult.uniqueViolations, multiInstanceConfig.deduplication);
41
- // Step 5: Create summary
42
- const totalProcessingTime = Date.now() - startTime;
43
- const summary = this.createSummary(instanceResults, deduplicationResult, finalViolations, totalProcessingTime);
44
- logger.success(`✅ Multi-instance processing completed: ${summary.totalViolationsFound} → ${summary.uniqueViolationsAfterDedup} violations ` +
45
- `(${summary.deduplicationRate.toFixed(1)}% reduction) in ${Math.round(totalProcessingTime / 1000)}s`);
46
- // Step 6: Log detailed statistics
47
- if (logger.getConfig().verbose) {
48
- logger.info(this.duplicateRemover.getDeduplicationStats(deduplicationResult));
49
- }
50
- return {
51
- instances: instanceResults,
52
- deduplication: deduplicationResult,
53
- finalViolations,
54
- summary,
55
- };
56
- }
57
- catch (error) {
58
- logger.error(`Multi-instance processing failed: ${error.message}`);
59
- throw error;
60
- }
61
- }
62
- /**
63
- * Execute all instances in parallel with concurrency control
64
- */
65
- async executeInstancesInParallel(context, options, multiInstanceConfig) {
66
- const instances = multiInstanceConfig.instances;
67
- // Calculate optimal concurrency
68
- const avgTokensPerInstance = this.estimateTokensPerInstance(context);
69
- const totalTokenBudget = this.calculateTotalTokenBudget(instances);
70
- const optimalConcurrency = calculateOptimalConcurrency(instances.length, Math.min(instances.length, 3), // Max 3 concurrent instances by default
71
- avgTokensPerInstance, totalTokenBudget);
72
- // Initialize concurrency control
73
- const semaphore = new Semaphore(optimalConcurrency);
74
- const tokenBudget = new TokenBudgetManager(totalTokenBudget);
75
- logger.info(`🎯 Parallel execution: ${optimalConcurrency} concurrent instances, ${totalTokenBudget} total token budget`);
76
- // Execute instances with controlled concurrency
77
- const instancePromises = instances.map((instanceConfig, index) => this.executeInstanceWithConcurrency(instanceConfig, context, options, semaphore, tokenBudget, index, instances.length));
78
- // Wait for all instances to complete
79
- const results = await Promise.allSettled(instancePromises);
80
- // Process results and handle failures
81
- const instanceResults = [];
82
- for (let i = 0; i < results.length; i++) {
83
- const result = results[i];
84
- const instanceConfig = instances[i];
85
- if (result.status === "fulfilled") {
86
- instanceResults.push(result.value);
87
- }
88
- else {
89
- logger.error(`❌ Instance ${instanceConfig.name} failed: ${result.reason.message}`);
90
- instanceResults.push({
91
- instanceName: instanceConfig.name,
92
- violations: [],
93
- processingTime: 0,
94
- error: result.reason.message,
95
- success: false,
96
- });
97
- }
98
- }
99
- return instanceResults;
100
- }
101
- /**
102
- * Execute a single instance with concurrency control
103
- */
104
- async executeInstanceWithConcurrency(instanceConfig, context, options, semaphore, tokenBudget, instanceIndex, totalInstances) {
105
- // Acquire semaphore permit
106
- await semaphore.acquire();
107
- try {
108
- const estimatedTokens = this.estimateTokensPerInstance(context);
109
- // Check token budget
110
- if (!tokenBudget.allocateForBatch(instanceIndex, estimatedTokens)) {
111
- throw new Error(`Insufficient token budget for instance ${instanceConfig.name}`);
112
- }
113
- logger.info(`🔄 Processing instance ${instanceIndex + 1}/${totalInstances}: ${instanceConfig.name} ` +
114
- `(${instanceConfig.provider}, temp: ${instanceConfig.temperature || "default"})`);
115
- // Execute the instance
116
- const result = await this.executeInstance(instanceConfig, context, options);
117
- logger.info(`✅ Instance ${instanceConfig.name} completed: ${result.violations.length} violations ` +
118
- `in ${Math.round(result.processingTime / 1000)}s`);
119
- return result;
120
- }
121
- finally {
122
- // Always release resources
123
- tokenBudget.releaseBatch(instanceIndex);
124
- semaphore.release();
125
- }
126
- }
127
- /**
128
- * Validate provider string against allowed provider types
129
- */
130
- validateProvider(provider) {
131
- const validProviders = [
132
- "auto",
133
- "google-ai",
134
- "openai",
135
- "anthropic",
136
- "azure",
137
- "bedrock",
138
- "vertex",
139
- ];
140
- if (!validProviders.includes(provider)) {
141
- logger.warn(`Unknown provider '${provider}', falling back to 'auto'`);
142
- return "auto";
143
- }
144
- return provider;
145
- }
146
- /**
147
- * Execute a single instance
148
- */
149
- async executeInstance(instanceConfig, context, options) {
150
- const startTime = Date.now();
151
- try {
152
- // Create instance-specific AI config
153
- const aiConfig = {
154
- provider: this.validateProvider(instanceConfig.provider),
155
- model: instanceConfig.model,
156
- temperature: instanceConfig.temperature,
157
- maxTokens: instanceConfig.maxTokens,
158
- timeout: instanceConfig.timeout,
159
- enableAnalytics: true,
160
- enableEvaluation: false,
161
- };
162
- // Create CodeReviewer for this instance
163
- const codeReviewer = createCodeReviewer(this.bitbucketProvider, aiConfig, this.baseReviewConfig);
164
- // Execute review with dry run to get violations without posting
165
- const instanceOptions = { ...options, dryRun: true };
166
- const reviewResult = await codeReviewer.reviewCodeWithContext(context, instanceOptions);
167
- const processingTime = Date.now() - startTime;
168
- return {
169
- instanceName: instanceConfig.name,
170
- violations: reviewResult.violations,
171
- processingTime,
172
- tokenUsage: this.extractTokenUsage(reviewResult),
173
- success: true,
174
- };
175
- }
176
- catch (error) {
177
- const processingTime = Date.now() - startTime;
178
- return {
179
- instanceName: instanceConfig.name,
180
- violations: [],
181
- processingTime,
182
- error: error.message,
183
- success: false,
184
- };
185
- }
186
- }
187
- /**
188
- * Validate multi-instance configuration
189
- */
190
- validateMultiInstanceConfig(config) {
191
- if (!config.enabled) {
192
- throw new Error("Multi-instance processing is not enabled");
193
- }
194
- if (config.instances.length === 0) {
195
- throw new Error("No instances configured for multi-instance processing");
196
- }
197
- if (config.instances.length !== config.instanceCount) {
198
- logger.warn(`Instance count mismatch: configured ${config.instanceCount}, found ${config.instances.length} instances`);
199
- }
200
- // Validate each instance
201
- for (const instance of config.instances) {
202
- if (!instance.name || !instance.provider) {
203
- throw new Error(`Invalid instance configuration: name and provider are required`);
204
- }
205
- }
206
- // Validate deduplication config
207
- if (config.deduplication.enabled) {
208
- if (config.deduplication.similarityThreshold < 0 ||
209
- config.deduplication.similarityThreshold > 100) {
210
- throw new Error("Similarity threshold must be between 0 and 100");
211
- }
212
- if (config.deduplication.maxCommentsToPost <= 0) {
213
- throw new Error("Max comments to post must be greater than 0");
214
- }
215
- }
216
- }
217
- /**
218
- * Estimate tokens per instance based on context
219
- */
220
- estimateTokensPerInstance(context) {
221
- // Base estimation: context size + overhead
222
- const contextSize = JSON.stringify(context).length;
223
- const estimatedTokens = Math.ceil(contextSize / 4); // ~4 chars per token
224
- // Add overhead for prompts and response
225
- const overhead = 5000;
226
- return estimatedTokens + overhead;
227
- }
228
- /**
229
- * Calculate total token budget for all instances
230
- */
231
- calculateTotalTokenBudget(instances) {
232
- // Use the most restrictive provider limit among all instances
233
- let minLimit = Infinity;
234
- for (const instance of instances) {
235
- const providerLimit = getProviderTokenLimit(instance.provider, true);
236
- const instanceLimit = instance.maxTokens || providerLimit;
237
- minLimit = Math.min(minLimit, instanceLimit);
238
- }
239
- // Total budget is the sum of all instance limits, but with safety margin
240
- // Use Math.floor to ensure integer result and avoid floating-point precision issues
241
- const totalBudget = Math.floor(instances.length * minLimit * 0.8); // 80% safety margin
242
- logger.debug(`Calculated total token budget: ${totalBudget} (${instances.length} instances × ${minLimit} × 0.8, floored)`);
243
- return totalBudget;
244
- }
245
- /**
246
- * Extract token usage from review result (if available)
247
- */
248
- extractTokenUsage(reviewResult) {
249
- // This would need to be implemented based on how NeuroLink returns usage data
250
- // For now, return undefined as we don't have access to this data
251
- return undefined;
252
- }
253
- /**
254
- * Create non-deduplicated result (when deduplication is disabled)
255
- */
256
- createNonDeduplicatedResult(instanceResults) {
257
- const allViolations = [];
258
- const instanceContributions = new Map();
259
- for (const result of instanceResults) {
260
- if (result.success && result.violations) {
261
- allViolations.push(...result.violations);
262
- instanceContributions.set(result.instanceName, result.violations.length);
263
- }
264
- }
265
- return {
266
- uniqueViolations: allViolations,
267
- duplicatesRemoved: {
268
- exactDuplicates: 0,
269
- normalizedDuplicates: 0,
270
- sameLineDuplicates: 0,
271
- },
272
- instanceContributions,
273
- processingMetrics: {
274
- totalViolationsInput: allViolations.length,
275
- exactDuplicatesRemoved: 0,
276
- normalizedDuplicatesRemoved: 0,
277
- sameLineDuplicatesRemoved: 0,
278
- finalUniqueViolations: allViolations.length,
279
- deduplicationRate: 0,
280
- instanceContributions: Object.fromEntries(instanceContributions),
281
- processingTimeMs: 0,
282
- },
283
- };
284
- }
285
- /**
286
- * Apply final filtering based on configuration
287
- */
288
- applyFinalFiltering(violations, deduplicationConfig) {
289
- if (!deduplicationConfig.maxCommentsToPost ||
290
- violations.length <= deduplicationConfig.maxCommentsToPost) {
291
- return violations;
292
- }
293
- logger.info(`📊 Applying final filtering: ${violations.length} → ${deduplicationConfig.maxCommentsToPost} violations`);
294
- // Sort by priority based on configuration
295
- const prioritized = this.prioritizeViolations(violations, deduplicationConfig.prioritizeBy);
296
- // Take only the top N violations
297
- const filtered = prioritized.slice(0, deduplicationConfig.maxCommentsToPost);
298
- logger.info(`🎯 Final filtering applied: kept top ${filtered.length} violations prioritized by ${deduplicationConfig.prioritizeBy}`);
299
- return filtered;
300
- }
301
- /**
302
- * Prioritize violations based on strategy
303
- */
304
- prioritizeViolations(violations, strategy) {
305
- const severityOrder = {
306
- CRITICAL: 4,
307
- MAJOR: 3,
308
- MINOR: 2,
309
- SUGGESTION: 1,
310
- };
311
- switch (strategy) {
312
- case "severity":
313
- return violations.sort((a, b) => {
314
- const aScore = severityOrder[a.severity] || 0;
315
- const bScore = severityOrder[b.severity] || 0;
316
- return bScore - aScore; // Higher severity first
317
- });
318
- case "similarity":
319
- case "confidence":
320
- // For now, fall back to severity-based sorting
321
- // These could be implemented with more sophisticated algorithms
322
- logger.debug(`Prioritization strategy '${strategy}' not fully implemented, using severity`);
323
- return this.prioritizeViolations(violations, "severity");
324
- default:
325
- logger.warn(`Unknown prioritization strategy: ${strategy}, using severity`);
326
- return this.prioritizeViolations(violations, "severity");
327
- }
328
- }
329
- /**
330
- * Create summary of multi-instance processing
331
- */
332
- createSummary(instanceResults, deduplicationResult, finalViolations, totalProcessingTime) {
333
- const successfulInstances = instanceResults.filter((r) => r.success).length;
334
- const failedInstances = instanceResults.length - successfulInstances;
335
- const totalViolationsFound = instanceResults
336
- .filter((r) => r.success)
337
- .reduce((sum, r) => sum + r.violations.length, 0);
338
- const deduplicationRate = totalViolationsFound > 0
339
- ? ((totalViolationsFound - finalViolations.length) /
340
- totalViolationsFound) *
341
- 100
342
- : 0;
343
- return {
344
- totalInstances: instanceResults.length,
345
- successfulInstances,
346
- failedInstances,
347
- totalViolationsFound,
348
- uniqueViolationsAfterDedup: finalViolations.length,
349
- deduplicationRate,
350
- totalProcessingTime,
351
- };
352
- }
353
- }
354
- /**
355
- * Factory function to create MultiInstanceProcessor
356
- */
357
- export function createMultiInstanceProcessor(bitbucketProvider, baseReviewConfig) {
358
- return new MultiInstanceProcessor(bitbucketProvider, baseReviewConfig);
359
- }
360
- //# sourceMappingURL=MultiInstanceProcessor.js.map