@wildcard-ai/deepcontext
Version:
Advanced codebase indexing and semantic search MCP server
348 lines • 15.4 kB
JavaScript
/**
* Search Coordination Service
* Orchestrates all types of search operations including hybrid, BM25, vector, and intelligent search
*/
import { Logger } from '../utils/Logger.js';
export class SearchCoordinationService {
jinaApiService;
turbopufferService;
connectionExtractor;
configurationService;
logger;
constructor(jinaApiService, turbopufferService, connectionExtractor, configurationService, loggerName = 'SearchCoordinationService') {
this.jinaApiService = jinaApiService;
this.turbopufferService = turbopufferService;
this.connectionExtractor = connectionExtractor;
this.configurationService = configurationService;
this.logger = new Logger(loggerName);
}
/**
* Advanced hybrid search combining vector similarity and BM25 full-text search
*/
async searchHybrid(namespace, query, options = {}) {
const startTime = Date.now();
if (!query || query.trim().length === 0) {
return {
success: false,
results: [],
totalResults: 0,
searchTime: Date.now() - startTime,
searchTimeMs: Date.now() - startTime,
strategy: 'hybrid',
message: 'Empty query provided',
metadata: {
vectorResults: 0,
bm25Results: 0,
totalMatches: 0,
reranked: false
}
};
}
try {
this.logger.info(`🔍 Hybrid search: "${query}" in namespace: ${namespace}`);
// Generate embedding for hybrid search
this.logger.debug('Generating embedding for hybrid search...');
const embedding = await this.jinaApiService.generateEmbedding(query);
this.logger.info(`Embedding generated: ${embedding.length} dimensions`);
// Get search configuration
const searchConfig = this.configurationService.getSearchConfig();
// Use Turbopuffer hybrid search
this.logger.info('Calling Turbopuffer hybrid search...');
const rawResults = await this.turbopufferService.hybridSearch(namespace, {
embedding,
query,
limit: options.limit || searchConfig.defaultResultLimit,
vectorWeight: options.vectorWeight || searchConfig.defaultVectorWeight,
bm25Weight: options.bm25Weight || searchConfig.defaultBm25Weight
});
this.logger.info(`Raw results received: ${rawResults.length}`);
// Convert results to expected format with connection context
const results = await Promise.all(rawResults.map(async (result) => {
let content = result.metadata.content;
// Add connection context for better architecture understanding
const connections = await this.connectionExtractor(result.metadata.filePath, content);
return {
id: result.id,
score: result.score,
content: content,
filePath: result.metadata.filePath,
relativePath: result.metadata.relativePath || result.metadata.filePath,
startLine: result.metadata.startLine,
endLine: result.metadata.endLine,
symbols: result.metadata.symbols ? result.metadata.symbols.split(',').filter(Boolean) : [],
language: result.metadata.language,
similarity: result.score,
connections: connections
};
}));
const searchTime = Date.now() - startTime;
this.logger.info(`✅ Hybrid search completed: ${results.length} results in ${searchTime}ms`);
return {
success: true,
results,
totalResults: results.length,
searchTime,
searchTimeMs: searchTime,
strategy: 'hybrid',
message: `Found ${results.length} results`,
metadata: {
vectorResults: Math.floor(results.length * (options.vectorWeight || searchConfig.defaultVectorWeight)),
bm25Results: Math.floor(results.length * (options.bm25Weight || searchConfig.defaultBm25Weight)),
totalMatches: results.length,
reranked: options.enableReranking !== false
}
};
}
catch (error) {
this.logger.error('Hybrid search failed', {
error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined,
query: query.substring(0, 50)
});
return {
success: false,
results: [],
totalResults: 0,
searchTime: Date.now() - startTime,
searchTimeMs: Date.now() - startTime,
strategy: 'hybrid',
message: `Search failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
metadata: {
vectorResults: 0,
bm25Results: 0,
totalMatches: 0,
reranked: false
}
};
}
}
/**
* Pure BM25 full-text search using Turbopuffer
*/
async searchBM25(namespace, query, options = {}) {
const startTime = Date.now();
if (!query || query.trim().length === 0) {
return {
success: false,
results: [],
totalResults: 0,
searchTime: Date.now() - startTime,
searchTimeMs: Date.now() - startTime,
strategy: 'bm25',
message: 'Empty query provided'
};
}
try {
this.logger.info(`📝 BM25 search: "${query}" in namespace: ${namespace}`);
// Get search configuration
const searchConfig = this.configurationService.getSearchConfig();
const limit = options.limit || searchConfig.defaultResultLimit;
// Direct BM25 search through TurbopufferService
const searchResults = await this.turbopufferService.query(namespace, {
query: query,
limit: options.enableReranking ? limit * 2 : limit
});
const rawResults = searchResults.map((item) => ({
id: item.id,
score: item.score,
content: item.metadata?.content || '',
symbols: item.metadata?.symbols ? item.metadata.symbols.split(',').filter(Boolean) : [],
filePath: item.metadata?.filePath || '',
relativePath: item.metadata?.relativePath || item.metadata?.filePath || '',
startLine: item.metadata?.startLine || 0,
endLine: item.metadata?.endLine || 0,
language: item.metadata?.language || ''
}));
let results = rawResults;
// Apply reranking if enabled
if (options.enableReranking && rawResults.length > 0 && this.jinaApiService.isAvailable()) {
try {
this.logger.debug('Applying Jina reranking to BM25 results...');
const documents = rawResults.map((r) => r.content);
const rerankedIndices = await this.jinaApiService.rerank(query, documents, limit);
// Reorder results based on reranking scores
results = rerankedIndices.map(({ index, relevance_score }) => ({
...rawResults[index],
score: relevance_score
}));
this.logger.debug(`Reranking completed: ${rerankedIndices.length} results reordered`);
}
catch (error) {
this.logger.warn('Reranking failed, using original BM25 scores', {
error: error instanceof Error ? error.message : String(error)
});
}
}
const searchTime = Date.now() - startTime;
this.logger.info(`✅ BM25 search completed: ${results.length} results in ${searchTime}ms`);
return {
success: true,
results,
totalResults: results.length,
searchTime,
searchTimeMs: searchTime,
strategy: 'bm25',
message: `Found ${results.length} results`
};
}
catch (error) {
this.logger.error('BM25 search failed', {
error: error instanceof Error ? error.message : String(error),
query: query.substring(0, 50)
});
return {
success: false,
results: [],
totalResults: 0,
searchTime: Date.now() - startTime,
searchTimeMs: Date.now() - startTime,
strategy: 'bm25',
message: `Search failed: ${error instanceof Error ? error.message : 'Unknown error'}`
};
}
}
/**
* Vector similarity search
*/
async searchVector(namespace, query, limit = 10) {
const queryEmbedding = await this.jinaApiService.generateEmbedding(query);
return await this.turbopufferService.query(namespace, {
embedding: queryEmbedding,
limit
});
}
/**
* Symbol-based vector search
*/
async searchVectorBySymbols(namespace, symbols, limit = 10) {
const symbolQuery = symbols.join(' ');
return await this.searchVector(namespace, symbolQuery, limit);
}
/**
* Intelligent search with automatic namespace detection
*/
async searchWithIntelligence(query, codebasePath, indexedCodebases, maxResults = 10) {
const startTime = Date.now();
try {
// Determine namespace from codebase path or use first available
let namespace;
let actualCodebasePath;
if (codebasePath) {
// Normalize path for comparison
const path = await import('path');
const normalizedPath = path.resolve(codebasePath);
const indexed = indexedCodebases.get(normalizedPath);
if (!indexed) {
return {
success: false,
results: [],
totalResults: 0,
searchTime: Date.now() - startTime,
searchTimeMs: Date.now() - startTime,
strategy: 'intelligent',
message: `Codebase not found or not indexed: ${codebasePath}`
};
}
namespace = indexed.namespace;
actualCodebasePath = normalizedPath;
}
else {
// Use first available indexed codebase
const firstIndexed = Array.from(indexedCodebases.values())[0];
if (!firstIndexed) {
return {
success: false,
results: [],
totalResults: 0,
searchTime: Date.now() - startTime,
searchTimeMs: Date.now() - startTime,
strategy: 'intelligent',
message: 'No indexed codebases available'
};
}
namespace = firstIndexed.namespace;
actualCodebasePath = firstIndexed.path;
}
this.logger.info(`🔍 Intelligent search for: "${query}" in ${actualCodebasePath}`);
// Use hybrid search as the intelligent search strategy
const searchResult = await this.searchHybrid(namespace, query, {
limit: maxResults,
vectorWeight: 0.6,
bm25Weight: 0.4
});
// Apply Jina reranker v2 if API key is available and we have results
if (searchResult.success && searchResult.results.length > 0 && this.jinaApiService.isAvailable()) {
try {
const rerankedResults = await this.jinaApiService.rerankerResults(query, searchResult.results);
if (rerankedResults && rerankedResults.length > 0) {
searchResult.results = rerankedResults;
this.logger.info(`🎯 Results reranked using Jina v2: ${rerankedResults.length} results`);
}
}
catch (error) {
this.logger.warn('Reranking with Jina failed, using hybrid results', {
error: error instanceof Error ? error.message : String(error)
});
}
}
const searchTimeMs = Date.now() - startTime;
if (searchResult.success && searchResult.results.length > 0) {
this.logger.info(`✅ Search completed: ${searchResult.results.length} results in ${searchTimeMs}ms`);
return {
success: true,
results: searchResult.results,
totalResults: searchResult.results.length,
searchTime: searchTimeMs,
searchTimeMs,
strategy: 'intelligent',
message: `Found ${searchResult.results.length} relevant code chunks`
};
}
else {
return {
success: true,
results: [],
totalResults: 0,
searchTime: searchTimeMs,
searchTimeMs,
strategy: 'intelligent',
message: 'No relevant code chunks found for the query'
};
}
}
catch (error) {
this.logger.error('Intelligent search failed', {
error: error instanceof Error ? error.message : String(error),
query: query.substring(0, 50)
});
return {
success: false,
results: [],
totalResults: 0,
searchTime: Date.now() - startTime,
searchTimeMs: Date.now() - startTime,
strategy: 'intelligent',
message: `Search failed: ${error instanceof Error ? error.message : 'Unknown error'}`
};
}
}
/**
* Check if search services are available
*/
isAvailable() {
return this.jinaApiService.isAvailable() && this.turbopufferService.isAvailable();
}
/**
* Get search service status
*/
getStatus() {
const jinaAvailable = this.jinaApiService.isAvailable();
const turbopufferAvailable = this.turbopufferService.isAvailable();
return {
jinaAvailable,
turbopufferAvailable,
overallAvailable: jinaAvailable && turbopufferAvailable
};
}
}
//# sourceMappingURL=SearchCoordinationService.js.map