Skip to main content
Glama
RecommendationRepository.ts7.71 kB
import Database from 'better-sqlite3'; import { SearchRecommendation, RecommendationEffectiveness, AdaptiveLearningParams, SuggestionStrategy, StorageError } from '../types/index.js'; import { randomUUID } from 'node:crypto'; export class RecommendationRepository { private db: Database.Database; constructor(db: Database.Database) { this.db = db; } /** * Store a search recommendation * @param recommendation The recommendation to store * @returns The stored recommendation with ID */ async storeRecommendation(recommendation: Omit<SearchRecommendation, 'id'>): Promise<SearchRecommendation> { try { const id = `rec_${Date.now()}_${randomUUID().replace(/-/g, '').substring(0, 9)}`; const now = new Date(); const stmt = this.db.prepare(` INSERT INTO search_recommendations (id, query, suggested_terms, suggestion_strategy, tfidf_threshold, confidence, generated_at, expires_at, total_documents, analyzed_documents) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `); stmt.run( id, recommendation.query, JSON.stringify(recommendation.suggestedTerms), recommendation.suggestionStrategy, recommendation.tfidfThreshold, recommendation.confidence, recommendation.generatedAt.toISOString(), recommendation.expiresAt.toISOString(), recommendation.totalDocuments, recommendation.analyzedDocuments ); return { ...recommendation, id }; } catch (error: any) { throw new StorageError( `Failed to store recommendation: ${error.message}`, error ); } } /** * Get cached recommendation for a query * @param query The search query * @returns Recommendation if found and not expired, null otherwise */ async getRecommendation(query: string): Promise<SearchRecommendation | null> { try { const stmt = this.db.prepare(` SELECT id, query, suggested_terms, suggestion_strategy, tfidf_threshold, confidence, generated_at, expires_at, total_documents, analyzed_documents FROM search_recommendations WHERE query = ? AND expires_at > datetime('now') ORDER BY generated_at DESC LIMIT 1 `); const row = stmt.get(query) as any; if (!row) return null; return { id: row.id, query: row.query, suggestedTerms: JSON.parse(row.suggested_terms), suggestionStrategy: row.suggestion_strategy as SuggestionStrategy, tfidfThreshold: row.tfidf_threshold, confidence: row.confidence, generatedAt: new Date(row.generated_at), expiresAt: new Date(row.expires_at), totalDocuments: row.total_documents, analyzedDocuments: row.analyzed_documents }; } catch (error: any) { throw new StorageError( `Failed to get recommendation: ${error.message}`, error ); } } /** * Record effectiveness of a recommendation * @param effectiveness The effectiveness data to store */ async recordEffectiveness(effectiveness: RecommendationEffectiveness): Promise<void> { try { const stmt = this.db.prepare(` INSERT INTO recommendation_effectiveness (recommendation_id, was_used, improved_results, usage_time, effectiveness_score, original_result_count, improved_result_count) VALUES (?, ?, ?, ?, ?, ?, ?) `); stmt.run( effectiveness.recommendationId, effectiveness.wasUsed ? 1 : 0, effectiveness.improvedResults === undefined ? null : (effectiveness.improvedResults ? 1 : 0), effectiveness.usageTime?.toISOString() || null, effectiveness.effectivenessScore, effectiveness.originalResultCount, effectiveness.improvedResultCount || null ); } catch (error: any) { throw new StorageError( `Failed to record effectiveness: ${error.message}`, error ); } } /** * Get learning parameters (creates defaults if not exist) * @returns Current adaptive learning parameters */ async getLearningParameters(): Promise<AdaptiveLearningParams> { try { // Try to get existing parameters const stmt = this.db.prepare(` SELECT current_tfidf_threshold, effectiveness_history, strategy_weights, last_updated, learning_rate FROM learning_parameters WHERE id = 1 `); const row = stmt.get() as any; if (row) { return { currentTfidfThreshold: row.current_tfidf_threshold, effectivenessHistory: JSON.parse(row.effectiveness_history), strategyWeights: JSON.parse(row.strategy_weights), lastUpdated: new Date(row.last_updated), learningRate: row.learning_rate }; } // Create default parameters const defaults = { currentTfidfThreshold: 0.25, effectivenessHistory: [] as number[], strategyWeights: { [SuggestionStrategy.TERM_REMOVAL]: 1.0, [SuggestionStrategy.TERM_REFINEMENT]: 1.0, [SuggestionStrategy.CONTEXTUAL_ADDITION]: 1.0 } as Record<SuggestionStrategy, number>, lastUpdated: new Date(), learningRate: 0.05 }; const insertStmt = this.db.prepare(` INSERT INTO learning_parameters (id, current_tfidf_threshold, effectiveness_history, strategy_weights, learning_rate) VALUES (1, ?, ?, ?, ?) `); insertStmt.run( defaults.currentTfidfThreshold, JSON.stringify(defaults.effectivenessHistory), JSON.stringify(defaults.strategyWeights), defaults.learningRate ); return defaults; } catch (error: any) { throw new StorageError( `Failed to get learning parameters: ${error.message}`, error ); } } /** * Update learning parameters * @param params The updated learning parameters */ async updateLearningParameters(params: AdaptiveLearningParams): Promise<void> { try { const stmt = this.db.prepare(` UPDATE learning_parameters SET current_tfidf_threshold = ?, effectiveness_history = ?, strategy_weights = ?, last_updated = ?, learning_rate = ? WHERE id = 1 `); stmt.run( params.currentTfidfThreshold, JSON.stringify(params.effectivenessHistory), JSON.stringify(params.strategyWeights), params.lastUpdated.toISOString(), params.learningRate ); } catch (error: any) { throw new StorageError( `Failed to update learning parameters: ${error.message}`, error ); } } /** * Clean up expired recommendations * @returns Number of expired recommendations cleaned up */ async cleanupExpiredRecommendations(): Promise<number> { try { const stmt = this.db.prepare('DELETE FROM search_recommendations WHERE expires_at <= datetime(\'now\')'); const result = stmt.run(); return result.changes; } catch (error: any) { throw new StorageError( `Failed to cleanup expired recommendations: ${error.message}`, error ); } } /** * Clear all recommendation and learning data */ async clear(): Promise<void> { try { this.db.exec('DELETE FROM search_recommendations'); this.db.exec('DELETE FROM recommendation_effectiveness'); // Keep learning parameters for continuity: DELETE FROM learning_parameters } catch (error: any) { throw new StorageError( `Failed to clear recommendation data: ${error.message}`, error ); } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/PatrickRuddiman/local-search-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server