@aaswe/codebase-ai
Version:
AI-Assisted Software Engineering (AASWE) - Rich codebase context for IDE LLMs
925 lines • 36 kB
JavaScript
;
/**
* TTL Context Loader for MCP Server
*
* Enhanced TTL context loading service that integrates with the automatic
* analysis system to provide rich, concrete codebase context to LLMs.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TTLContextLoader = void 0;
const promises_1 = require("fs/promises");
const chokidar_1 = require("chokidar");
const glob_1 = require("glob");
const path_1 = require("path");
const crypto_1 = require("crypto");
const events_1 = require("events");
const logger_1 = __importDefault(require("../../utils/logger"));
/**
* Enhanced TTL Context Loader
*
* Provides intelligent context loading from TTL files generated by
* the automatic analysis system, with concrete code information
* and business context preservation.
*/
class TTLContextLoader extends events_1.EventEmitter {
config;
_knowledgeGraphPopulator;
_rdfGenerator;
_informationExtractor;
ttlFiles = new Map();
contextCache = new Map();
fileWatcher = null;
loadingQueue = new Set();
metrics;
cacheCleanupInterval;
constructor(config, knowledgeGraphPopulator, rdfGenerator, informationExtractor) {
super();
this.config = config;
this._knowledgeGraphPopulator = knowledgeGraphPopulator;
this._rdfGenerator = rdfGenerator;
this._informationExtractor = informationExtractor;
this.metrics = this.initializeMetrics();
logger_1.default.info('TTLContextLoader initialized', {
watchEnabled: config.watchEnabled,
cacheEnabled: config.cacheEnabled,
maxCacheSize: config.maxCacheSize,
hasKnowledgeGraphPopulator: !!this._knowledgeGraphPopulator,
hasRDFGenerator: !!this._rdfGenerator,
hasInformationExtractor: !!this._informationExtractor
});
}
/**
* Initialize metrics
*/
initializeMetrics() {
return {
ttlFiles: {
total: 0,
loaded: 0,
failed: 0,
watching: 0
},
context: {
requests: 0,
cacheHits: 0,
cacheMisses: 0,
averageResponseTime: 0,
averageRelevanceScore: 0
},
performance: {
memoryUsage: 0,
loadingQueueSize: 0,
cacheSize: 0,
lastCleanup: new Date()
}
};
}
/**
* Start the TTL context loader
*/
async start() {
try {
logger_1.default.info('Starting TTL context loader...');
// Initialize file watching if enabled
if (this.config.watchEnabled) {
await this.initializeFileWatcher();
}
// Load existing TTL files
await this.loadAllTTLFiles();
// Start cache cleanup if enabled
if (this.config.cacheEnabled) {
this.startCacheCleanup();
}
logger_1.default.info('TTL context loader started', {
ttlFiles: this.ttlFiles.size,
watching: this.config.watchEnabled
});
}
catch (error) {
logger_1.default.error('Failed to start TTL context loader', { error });
throw error;
}
}
/**
* Stop the TTL context loader
*/
async stop() {
try {
logger_1.default.info('Stopping TTL context loader...');
// Stop file watcher
if (this.fileWatcher) {
await this.fileWatcher.close();
this.fileWatcher = null;
}
// Stop cache cleanup
if (this.cacheCleanupInterval) {
clearInterval(this.cacheCleanupInterval);
this.cacheCleanupInterval = undefined;
}
// Clear caches
this.contextCache.clear();
this.loadingQueue.clear();
logger_1.default.info('TTL context loader stopped');
}
catch (error) {
logger_1.default.error('Failed to stop TTL context loader', { error });
throw error;
}
}
/**
* Load context for a specific request
*/
async loadContext(request) {
const startTime = Date.now();
this.metrics.context.requests++;
try {
// Check cache first
if (this.config.cacheEnabled) {
const cached = this.getCachedContext(request);
if (cached) {
this.metrics.context.cacheHits++;
return cached;
}
this.metrics.context.cacheMisses++;
}
// Select relevant TTL files
const selection = await this.selectRelevantTTLFiles(request);
// Load concrete context from selected files
const sources = await this.loadConcreteSources(selection, request);
// Generate enhanced context with business insights
const context = await this.generateEnhancedContext(sources, request);
// Build response
const processingTime = Date.now() - startTime;
const response = {
context: context.formattedContext,
sources,
metadata: {
totalTokens: this.estimateTokens(context.formattedContext),
processingTime,
relevanceScore: this.calculateAverageRelevance(sources),
cached: false
},
suggestions: {
relatedFiles: selection.alternatives[0]?.files || [],
followUpQueries: context.followUpQueries,
improvements: context.improvements
}
};
// Cache the response
if (this.config.cacheEnabled) {
this.cacheContext(request, response);
}
// Update metrics
this.updateMetrics(processingTime, response.metadata.relevanceScore);
return response;
}
catch (error) {
logger_1.default.error('Failed to load TTL context', { request, error });
throw error;
}
}
/**
* Get all loaded TTL files
*/
getTTLFiles() {
return new Map(this.ttlFiles);
}
/**
* Get TTL file by path
*/
getTTLFile(path) {
return this.ttlFiles.get(path);
}
/**
* Refresh TTL file from disk
*/
async refreshTTLFile(path) {
try {
await this.loadTTLFile(path);
this.clearRelatedCache(path);
this.emit('ttl_file_refreshed', { path, timestamp: Date.now() });
}
catch (error) {
logger_1.default.error('Failed to refresh TTL file', { path, error });
throw error;
}
}
/**
* Get loader metrics
*/
getMetrics() {
this.metrics.performance.cacheSize = this.contextCache.size;
this.metrics.performance.loadingQueueSize = this.loadingQueue.size;
this.metrics.performance.memoryUsage = process.memoryUsage().heapUsed;
return { ...this.metrics };
}
/**
* Initialize file watcher
*/
async initializeFileWatcher() {
const patterns = this.config.watchPatterns || ['**/*.module-knowledge.ttl'];
this.fileWatcher = (0, chokidar_1.watch)(patterns, {
ignored: this.config.watchIgnored || [/node_modules/, /.git/, /dist/, /build/],
persistent: true,
ignoreInitial: true,
awaitWriteFinish: {
stabilityThreshold: this.config.watchDebounce || 1000,
pollInterval: 100
}
});
this.fileWatcher.on('add', (path) => this.handleFileEvent('created', path));
this.fileWatcher.on('change', (path) => this.handleFileEvent('modified', path));
this.fileWatcher.on('unlink', (path) => this.handleFileEvent('deleted', path));
logger_1.default.info('TTL file watcher initialized', { patterns });
}
/**
* Handle file system events
*/
async handleFileEvent(type, path) {
try {
const event = { type, path, timestamp: Date.now() };
switch (type) {
case 'created':
case 'modified':
await this.loadTTLFile(path);
this.clearRelatedCache(path);
break;
case 'deleted':
this.ttlFiles.delete(path);
this.clearRelatedCache(path);
this.metrics.ttlFiles.total = Math.max(0, this.metrics.ttlFiles.total - 1);
break;
}
this.emit('ttl_file_changed', event);
}
catch (error) {
logger_1.default.error('Failed to handle TTL file event', { type, path, error });
}
}
/**
* Load all TTL files from the project
*/
async loadAllTTLFiles() {
try {
const patterns = this.config.loadPatterns || ['**/*.module-knowledge.ttl'];
const ttlFiles = [];
for (const pattern of patterns) {
const files = await (0, glob_1.glob)(pattern, {
ignore: this.config.loadIgnored || ['node_modules/**', '.git/**', 'dist/**', 'build/**'],
absolute: true
});
ttlFiles.push(...files);
}
logger_1.default.info(`Found ${ttlFiles.length} TTL files to load`);
// Load files in parallel with concurrency limit
const concurrency = this.config.loadConcurrency || 5;
const chunks = this.chunkArray(ttlFiles, concurrency);
for (const chunk of chunks) {
const loadPromises = chunk.map(filePath => this.loadTTLFile(filePath));
await Promise.allSettled(loadPromises);
}
this.metrics.ttlFiles.total = ttlFiles.length;
this.metrics.ttlFiles.loaded = this.ttlFiles.size;
this.metrics.ttlFiles.failed = ttlFiles.length - this.ttlFiles.size;
logger_1.default.info(`Successfully loaded ${this.ttlFiles.size}/${ttlFiles.length} TTL files`);
}
catch (error) {
logger_1.default.error('Failed to load TTL files', { error });
throw error;
}
}
/**
* Load a single TTL file
*/
async loadTTLFile(path) {
if (this.loadingQueue.has(path)) {
return; // Already loading
}
this.loadingQueue.add(path);
try {
// Check if file exists and is readable
await (0, promises_1.access)(path);
const content = await (0, promises_1.readFile)(path, 'utf-8');
const stats = await (0, promises_1.stat)(path);
const hash = (0, crypto_1.createHash)('md5').update(content).digest('hex');
// Check if file has changed
const existing = this.ttlFiles.get(path);
if (existing && existing.hash === hash) {
return; // No changes
}
// Extract concrete information and metadata
const metadata = await this.extractTTLMetadata(content, path);
const concreteContext = await this.extractConcreteContext(content, path);
const ttlFile = {
path,
content,
lastModified: stats.mtime,
size: stats.size,
hash,
parsed: this.parseTTLContent(content),
metadata: {
...metadata,
concreteContext,
loadedAt: new Date(),
version: this.extractVersion(content)
}
};
this.ttlFiles.set(path, ttlFile);
logger_1.default.debug('TTL file loaded', {
path: (0, path_1.relative)(process.cwd(), path),
size: stats.size,
module: metadata.module,
language: metadata.language
});
}
catch (error) {
logger_1.default.error('Failed to load TTL file', { path, error });
this.metrics.ttlFiles.failed++;
}
finally {
this.loadingQueue.delete(path);
}
}
/**
* Extract enhanced TTL metadata
*/
async extractTTLMetadata(content, path) {
const lines = content.split('\n');
const comments = lines.filter(line => line.trim().startsWith('#'));
// Extract basic metadata
const module = this.extractModuleName(path);
const language = this.extractLanguage(path, content);
const dependencies = this.extractDependencies(content);
const businessContext = this.extractBusinessContext(comments);
// Extract architectural patterns
const architecturalPatterns = this.extractArchitecturalPatterns(content);
// Extract quality metrics
const qualityMetrics = this.extractQualityMetrics(content);
return {
module,
language,
dependencies,
businessContext,
architecturalPatterns,
qualityMetrics,
extractedAt: new Date()
};
}
/**
* Extract concrete code context
*/
async extractConcreteContext(content, path) {
try {
// Parse TTL content to extract concrete information
const classes = this.extractClasses(content);
const methods = this.extractMethods(content);
const properties = this.extractProperties(content);
const relationships = this.extractRelationships(content);
const imports = this.extractImports(content);
return {
classes,
methods,
properties,
relationships,
imports,
codeStructure: this.extractCodeStructure(content),
businessDomain: this.extractBusinessDomain(content),
extractedAt: new Date()
};
}
catch (error) {
logger_1.default.error('Failed to extract concrete context', { path, error });
return {
classes: [],
methods: [],
properties: [],
relationships: [],
imports: [],
codeStructure: {},
businessDomain: {},
extractedAt: new Date()
};
}
}
/**
* Select relevant TTL files for context request
*/
async selectRelevantTTLFiles(request) {
const relevanceScores = {};
const selectedFiles = [];
const requestDir = (0, path_1.dirname)(request.filePath);
const requestLanguage = this.detectLanguageFromPath(request.filePath);
for (const [filePath, ttlFile] of this.ttlFiles) {
let score = 0;
// File proximity scoring (higher weight)
const fileDir = (0, path_1.dirname)(filePath);
if (fileDir === requestDir)
score += 0.4;
else if (fileDir.startsWith(requestDir) || requestDir.startsWith(fileDir))
score += 0.25;
// Language matching (high weight)
if (ttlFile.metadata.language === requestLanguage)
score += 0.3;
// Query matching (if provided)
if (request.query) {
const queryLower = request.query.toLowerCase();
if (ttlFile.content.toLowerCase().includes(queryLower))
score += 0.2;
if (ttlFile.metadata.businessContext.some(ctx => ctx.toLowerCase().includes(queryLower)))
score += 0.15;
}
// Intent-based scoring
if (request.intent) {
score += this.calculateIntentScore(request.intent, ttlFile);
}
// Dependency matching
if (ttlFile.metadata.dependencies.some(dep => request.filePath.includes(dep) || dep.includes(request.filePath))) {
score += 0.15;
}
// Concrete context relevance
if (ttlFile.metadata.concreteContext) {
score += this.calculateConcreteContextScore(request, ttlFile.metadata.concreteContext);
}
relevanceScores[filePath] = score;
if (score > this.config.relevanceThreshold) {
selectedFiles.push(filePath);
}
}
// Sort by relevance and limit
selectedFiles.sort((a, b) => relevanceScores[b] - relevanceScores[a]);
const limitedFiles = selectedFiles.slice(0, this.config.maxFiles);
return {
selectedFiles: limitedFiles,
relevanceScores,
reasoning: `Selected ${limitedFiles.length} files based on proximity (40%), language (30%), query matching (20%), and concrete context (10%)`,
alternatives: [{
files: selectedFiles.slice(this.config.maxFiles),
score: 0.5,
reason: 'Alternative files with lower relevance scores'
}]
};
}
/**
* Load concrete sources from selected files
*/
async loadConcreteSources(selection, request) {
const sources = [];
let totalTokens = 0;
for (const filePath of selection.selectedFiles) {
if (totalTokens >= (request.maxTokens || this.config.maxTokens))
break;
const ttlFile = this.ttlFiles.get(filePath);
if (!ttlFile)
continue;
// Format TTL content for LLM consumption
const formattedContent = this.formatTTLForLLM(ttlFile, request);
const tokenCount = this.estimateTokens(formattedContent);
if (totalTokens + tokenCount <= (request.maxTokens || this.config.maxTokens)) {
const source = {
type: 'ttl',
path: filePath,
content: formattedContent,
relevanceScore: selection.relevanceScores[filePath] || 0,
metadata: {
lastModified: ttlFile.lastModified,
size: ttlFile.size,
language: ttlFile.metadata.language,
module: ttlFile.metadata.module
}
};
sources.push(source);
totalTokens += tokenCount;
}
}
return sources;
}
/**
* Generate enhanced context with business insights
*/
async generateEnhancedContext(sources, request) {
const contextLines = [
'# Enhanced Codebase Context',
`# Current File: ${request.filePath}`,
`# Cursor Position: Line ${request.cursorPosition.line}, Column ${request.cursorPosition.column}`,
''
];
if (request.query) {
contextLines.push(`# Query: ${request.query}`, '');
}
if (request.intent) {
contextLines.push(`# Intent: ${request.intent}`, '');
}
contextLines.push('# Concrete Knowledge Sources:');
// Add sources with enhanced formatting
for (const source of sources) {
const ttlFile = this.ttlFiles.get(source.path);
if (!ttlFile)
continue;
contextLines.push('', `## ${source.metadata.module} (${source.metadata.language?.toUpperCase()})`, `Relevance: ${(source.relevanceScore * 100).toFixed(1)}%`, `Path: ${(0, path_1.relative)(process.cwd(), source.path)}`, `Last Modified: ${source.metadata.lastModified.toISOString()}`, '');
// Add concrete context summary
if (ttlFile.metadata.concreteContext) {
contextLines.push('### Concrete Code Structure:', `Classes: ${ttlFile.metadata.concreteContext.classes.length}`, `Methods: ${ttlFile.metadata.concreteContext.methods.length}`, `Properties: ${ttlFile.metadata.concreteContext.properties.length}`, '');
}
// Add architectural patterns
if (ttlFile.metadata.architecturalPatterns?.length) {
contextLines.push('### Architectural Patterns:', ...ttlFile.metadata.architecturalPatterns.map(pattern => `- ${pattern}`), '');
}
contextLines.push(source.content);
}
// Generate follow-up queries
const followUpQueries = this.generateFollowUpQueries(request, sources);
// Generate improvements
const improvements = this.generateImprovements(sources);
return {
formattedContext: contextLines.join('\n'),
followUpQueries,
improvements
};
}
/**
* Format TTL file for LLM consumption
*/
formatTTLForLLM(ttlFile, _request) {
const lines = [
`# Module: ${ttlFile.metadata.module}`,
`# Language: ${ttlFile.metadata.language}`,
`# Dependencies: ${ttlFile.metadata.dependencies.join(', ')}`,
`# Last Modified: ${ttlFile.lastModified.toISOString()}`,
''
];
// Add concrete context if available
if (ttlFile.metadata.concreteContext) {
lines.push('# Concrete Code Elements:', `# Classes: ${ttlFile.metadata.concreteContext.classes.map(c => c.name).join(', ')}`, `# Key Methods: ${ttlFile.metadata.concreteContext.methods.slice(0, 5).map(m => m.name).join(', ')}`, '');
}
// Add business context
if (ttlFile.metadata.businessContext.length > 0) {
lines.push('# Business Context:', ...ttlFile.metadata.businessContext.map(ctx => `# ${ctx}`), '');
}
// Add the actual TTL content
lines.push('# Knowledge Content:', ttlFile.content);
return lines.join('\n');
}
// Helper methods for extraction and processing
extractClasses(content) {
const classes = [];
const lines = content.split('\n');
for (const line of lines) {
// Look for class definitions in TTL
const classMatch = line.match(/:(\w+)\s+a\s+:Class/);
if (classMatch) {
classes.push({
name: classMatch[1],
type: 'class',
properties: []
});
}
}
return classes;
}
extractMethods(content) {
const methods = [];
const lines = content.split('\n');
for (const line of lines) {
// Look for method definitions in TTL
const methodMatch = line.match(/:(\w+)\s+a\s+:Method/);
if (methodMatch) {
methods.push({
name: methodMatch[1],
signature: '',
returnType: 'unknown'
});
}
}
return methods;
}
extractProperties(content) {
const properties = [];
const lines = content.split('\n');
for (const line of lines) {
// Look for property definitions in TTL
const propMatch = line.match(/:(\w+)\s+a\s+:Property/);
if (propMatch) {
properties.push({
name: propMatch[1],
type: 'unknown',
access: 'public'
});
}
}
return properties;
}
extractRelationships(content) {
const relationships = [];
const lines = content.split('\n');
for (const line of lines) {
// Look for relationships in TTL
const relMatch = line.match(/:(\w+)\s+:(\w+)\s+:(\w+)/);
if (relMatch) {
relationships.push({
from: relMatch[1],
to: relMatch[3],
type: relMatch[2]
});
}
}
return relationships;
}
extractImports(content) {
const imports = [];
const lines = content.split('\n');
for (const line of lines) {
// Look for import statements in TTL comments
if (line.includes('import') || line.includes('require')) {
const importMatch = line.match(/["']([^"']+)["']/);
if (importMatch) {
imports.push(importMatch[1]);
}
}
}
return imports;
}
extractCodeStructure(content) {
return {
complexity: this.calculateComplexity(content),
patterns: this.extractArchitecturalPatterns(content),
metrics: this.extractQualityMetrics(content)
};
}
extractBusinessDomain(content) {
const domain = {};
const lines = content.split('\n');
for (const line of lines) {
if (line.includes('domain:') || line.includes('business:')) {
const match = line.match(/(?:domain|business):\s*(.+)/i);
if (match) {
domain.primary = match[1].trim();
}
}
}
return domain;
}
extractArchitecturalPatterns(content) {
const patterns = [];
const lowerContent = content.toLowerCase();
const patternKeywords = [
'singleton', 'factory', 'observer', 'strategy', 'decorator',
'mvc', 'mvp', 'mvvm', 'repository', 'service', 'dao',
'microservice', 'monolith', 'layered', 'hexagonal'
];
for (const pattern of patternKeywords) {
if (lowerContent.includes(pattern)) {
patterns.push(pattern);
}
}
return patterns;
}
extractQualityMetrics(content) {
return {
complexity: this.calculateComplexity(content),
maintainability: this.calculateMaintainability(content),
testability: this.calculateTestability(content)
};
}
calculateComplexity(content) {
// Simple complexity calculation based on content length and structure
const lines = content.split('\n').filter(line => line.trim().length > 0);
return Math.min(10, Math.floor(lines.length / 10));
}
calculateMaintainability(content) {
// Simple maintainability score
const hasComments = content.includes('#');
const hasStructure = content.includes(':');
return (hasComments ? 5 : 0) + (hasStructure ? 5 : 0);
}
calculateTestability(content) {
// Simple testability score
const hasTests = content.toLowerCase().includes('test');
const hasMocks = content.toLowerCase().includes('mock');
return (hasTests ? 5 : 0) + (hasMocks ? 5 : 0);
}
extractModuleName(path) {
const parts = path.split('/');
const fileName = parts[parts.length - 1];
return fileName.replace('.module-knowledge.ttl', '') || 'unknown';
}
extractLanguage(path, content) {
// Check path first
if (path.includes('/typescript/') || path.includes('.ts'))
return 'typescript';
if (path.includes('/javascript/') || path.includes('.js'))
return 'javascript';
if (path.includes('/python/') || path.includes('.py'))
return 'python';
if (path.includes('/java/') || path.includes('.java'))
return 'java';
// Check content for language hints
const lowerContent = content.toLowerCase();
if (lowerContent.includes('typescript'))
return 'typescript';
if (lowerContent.includes('javascript'))
return 'javascript';
if (lowerContent.includes('python'))
return 'python';
if (lowerContent.includes('java') && !lowerContent.includes('javascript'))
return 'java';
return 'unknown';
}
extractDependencies(content) {
const dependencies = [];
const lines = content.split('\n');
for (const line of lines) {
if (line.includes('imports:') || line.includes('depends:') || line.includes('Dependencies:')) {
const quotedMatches = line.match(/["']([^"']+)["']/g);
if (quotedMatches) {
quotedMatches.forEach(match => {
const dep = match.replace(/["']/g, '');
if (dep && !dependencies.includes(dep)) {
dependencies.push(dep);
}
});
}
}
}
return dependencies;
}
extractBusinessContext(comments) {
return comments
.map(c => c.replace('#', '').trim())
.filter(c => c.length > 0 && !c.startsWith('Module:') && !c.startsWith('Language:'));
}
extractVersion(content) {
const versionMatch = content.match(/version:\s*["']?([^"'\s]+)["']?/i);
return versionMatch ? versionMatch[1] : '1.0.0';
}
parseTTLContent(_content) {
// Simplified TTL parsing
return {
triples: [],
prefixes: {},
classes: [],
properties: [],
individuals: []
};
}
detectLanguageFromPath(filePath) {
if (filePath.endsWith('.ts') || filePath.includes('/typescript/'))
return 'typescript';
if (filePath.endsWith('.js') || filePath.includes('/javascript/'))
return 'javascript';
if (filePath.endsWith('.py') || filePath.includes('/python/'))
return 'python';
if (filePath.endsWith('.java') || filePath.includes('/java/'))
return 'java';
return 'unknown';
}
calculateIntentScore(intent, ttlFile) {
const content = ttlFile.content.toLowerCase();
switch (intent) {
case 'debugging':
return content.includes('error') || content.includes('exception') ? 0.2 : 0;
case 'refactoring':
return content.includes('pattern') || content.includes('architecture') ? 0.15 : 0;
case 'documentation':
return content.includes('comment') || content.includes('doc') ? 0.1 : 0;
default:
return 0;
}
}
calculateConcreteContextScore(request, concreteContext) {
let score = 0;
if (request.query) {
const queryLower = request.query.toLowerCase();
// Check if query matches class names
if (concreteContext.classes.some(c => c.name.toLowerCase().includes(queryLower))) {
score += 0.15;
}
// Check if query matches method names
if (concreteContext.methods.some(m => m.name.toLowerCase().includes(queryLower))) {
score += 0.1;
}
}
return score;
}
estimateTokens(text) {
// Simple token estimation (roughly 4 characters per token)
return Math.ceil(text.length / 4);
}
calculateAverageRelevance(sources) {
if (sources.length === 0)
return 0;
const total = sources.reduce((sum, source) => sum + source.relevanceScore, 0);
return total / sources.length;
}
getCachedContext(request) {
const cacheKey = this.generateCacheKey(request);
const cached = this.contextCache.get(cacheKey);
if (!cached)
return null;
const now = Date.now();
if (now - cached.timestamp > cached.ttl) {
this.contextCache.delete(cacheKey);
return null;
}
cached.hits++;
cached.context.metadata.cached = true;
return cached.context;
}
cacheContext(request, response) {
const cacheKey = this.generateCacheKey(request);
const cached = {
key: cacheKey,
context: response,
timestamp: Date.now(),
ttl: this.config.cacheTtl,
hits: 0
};
this.contextCache.set(cacheKey, cached);
// Limit cache size
if (this.contextCache.size > this.config.maxCacheSize) {
const oldestKey = this.contextCache.keys().next().value;
if (oldestKey) {
this.contextCache.delete(oldestKey);
}
}
}
generateCacheKey(request) {
const key = JSON.stringify({
filePath: request.filePath,
cursorPosition: request.cursorPosition,
query: request.query,
intent: request.intent,
maxTokens: request.maxTokens
});
return (0, crypto_1.createHash)('md5').update(key).digest('hex');
}
clearRelatedCache(filePath) {
for (const [key, cached] of this.contextCache.entries()) {
if (cached.context.sources.some(s => s.path === filePath)) {
this.contextCache.delete(key);
}
}
}
updateMetrics(processingTime, relevanceScore) {
const total = this.metrics.context.requests;
this.metrics.context.averageResponseTime =
(this.metrics.context.averageResponseTime * (total - 1) + processingTime) / total;
this.metrics.context.averageRelevanceScore =
(this.metrics.context.averageRelevanceScore * (total - 1) + relevanceScore) / total;
}
chunkArray(array, chunkSize) {
const chunks = [];
for (let i = 0; i < array.length; i += chunkSize) {
chunks.push(array.slice(i, i + chunkSize));
}
return chunks;
}
generateFollowUpQueries(request, sources) {
const queries = [
'What are the main dependencies of this module?',
'How does this code relate to other parts of the system?',
'What are the key architectural patterns used here?'
];
if (request.intent === 'debugging') {
queries.unshift('What are common issues in this type of code?');
}
if (request.intent === 'refactoring') {
queries.push('What refactoring opportunities exist here?');
}
// Add context-specific queries based on sources
if (sources.some(s => s.metadata.language === 'typescript')) {
queries.push('What TypeScript-specific patterns are used?');
}
return queries.slice(0, 4);
}
generateImprovements(sources) {
const improvements = [];
if (sources.length < 3) {
improvements.push('Consider adding more detailed business context to TTL files');
}
if (sources.some(s => s.metadata.size > 10000)) {
improvements.push('Large TTL files detected - consider splitting for better performance');
}
const languages = new Set(sources.map(s => s.metadata.language).filter(Boolean));
if (languages.size > 2) {
improvements.push('Multi-language codebase detected - ensure consistent patterns across languages');
}
improvements.push('Enhance TTL files with more architectural insights');
return improvements;
}
startCacheCleanup() {
this.cacheCleanupInterval = setInterval(() => {
const now = Date.now();
for (const [key, cached] of this.contextCache.entries()) {
if (now - cached.timestamp > cached.ttl) {
this.contextCache.delete(key);
}
}
this.metrics.performance.lastCleanup = new Date();
}, 60000); // Clean every minute
}
}
exports.TTLContextLoader = TTLContextLoader;
//# sourceMappingURL=TTLContextLoader.js.map