UNPKG

@hivetechs/hive-ai

Version:

Real-time streaming AI consensus platform with HTTP+SSE MCP integration for Claude Code, VS Code, Cursor, and Windsurf - powered by OpenRouter's unified API

343 lines 14.7 kB
/** * Database Interface - Unified Database Adapter * * This file now serves as a compatibility layer for the unified database. * All database operations are routed through the unified-database.ts module. */ // Import all functions from the unified database import { initializeUnifiedDatabase, getDatabase, createUser, getUser, getActiveConsensusProfile, addMessage, closeUnifiedDatabase } from './unified-database.js'; /** * Initialize the database - delegates to unified database */ export async function initializeDatabase() { return await initializeUnifiedDatabase(); } // Re-export from unified database export { createConversation } from './unified-database.js'; // Re-export from unified database export { addMessage } from './unified-database.js'; // Re-export pending sync functions from unified database export { addToPendingSync, markSyncComplete, recordSyncAttempt, getPendingSyncItems, clearPendingSyncData } from './unified-database.js'; /** * Record a pipeline stage result (legacy compatibility) */ export async function recordPipelineResult(id, messageId, stage, model, content) { // For now, save as a message with stage info return await addMessage(id, messageId, 'assistant', content, stage, model); } // Re-export from unified database export { getConversationHistory } from './unified-database.js'; /** * Check if any conversations exist in the database * This is useful for determining if we're in a new database state */ export async function checkForAnyConversations() { try { const database = await getDatabase(); const result = await database.get('SELECT COUNT(*) as count FROM conversations'); console.log(`[DATABASE] Found ${result.count} conversations in the database`); return result.count > 0; } catch (error) { console.error('[DATABASE] Error checking for conversations:', error); return false; } } /** * Check if a specific conversation exists in the database */ export async function checkConversationExists(conversationId) { try { const database = await getDatabase(); const result = await database.get('SELECT COUNT(*) as count FROM conversations WHERE id = ?', conversationId); console.log(`[DATABASE] Conversation ${conversationId} exists: ${result.count > 0 ? 'YES' : 'NO'}`); return result.count > 0; } catch (error) { console.error(`[DATABASE] Error checking if conversation ${conversationId} exists:`, error); return false; } } /** * List active conversations in the database (for debugging) */ export async function listActiveConversations(limit = 5) { try { const database = await getDatabase(); const conversations = await database.all('SELECT id, created_at, updated_at FROM conversations ORDER BY updated_at DESC LIMIT ?', limit); console.log(`[DATABASE] Found ${conversations.length} recent conversations:`); conversations.forEach((conv, i) => { console.log(`[DATABASE] ${i + 1}. ${conv.id} (last updated: ${conv.updated_at})`); }); return conversations; } catch (error) { console.error('[DATABASE] Error listing active conversations:', error); return []; } } /** * Get pipeline results for a specific message */ export async function getPipelineResults(messageId) { const database = await getDatabase(); return await database.all('SELECT * FROM messages WHERE conversation_id = ? AND stage IS NOT NULL ORDER BY timestamp ASC', messageId); } /** * Get recent conversations */ export async function getRecentConversations(limit = 10) { const database = await getDatabase(); return await database.all('SELECT * FROM conversations ORDER BY updated_at DESC LIMIT ?', limit); } /** * Get conversation metadata with topics * This combines data from conversations and conversation_topics tables */ export async function getConversationMetadata(conversationId) { try { const database = await getDatabase(); // Get the conversation basic info const conversation = await database.get('SELECT id, created_at, updated_at FROM conversations WHERE id = ?', conversationId); if (!conversation) { return null; } return { ...conversation, topics: null, // Topics functionality can be added later summary: null }; } catch (error) { console.error(`Error getting conversation metadata for ${conversationId}:`, error); return null; } } /** * Search for conversations by topic using keyword and topic matching */ export async function getConversationsByTopic(query, limit = 5) { try { const database = await getDatabase(); // Detect follow-up patterns for better contextual retrieval const followUpPatterns = [ /give me (\d+|\w+) examples?/i, /tell me more/i, /can you explain/i, /show me/i, /what about/i, /how does? it work/i, /examples? from/i, /from.*(earlier|previous|before)/i ]; const isFollowUp = followUpPatterns.some(pattern => pattern.test(query)); // For follow-up queries, prioritize recent conversations if (isFollowUp) { console.log('[DATABASE] Detected follow-up query, checking recent conversations'); const recentConversations = await database.all('SELECT id, created_at, updated_at, question, final_answer FROM knowledge_conversations WHERE created_at >= ? ORDER BY created_at DESC LIMIT ?', [new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString(), limit] // Last 2 hours ); if (recentConversations.length > 0) { console.log(`[DATABASE] Found ${recentConversations.length} recent conversations for follow-up`); return recentConversations; } } // Extract keywords from the query for matching const queryWords = query.toLowerCase() .replace(/[^\w\s]/g, ' ') .split(/\s+/) .filter(word => word.length > 2); // Filter out short words if (queryWords.length === 0) { // If no meaningful keywords, return recent conversations const conversations = await database.all('SELECT id, created_at, updated_at FROM conversations ORDER BY updated_at DESC LIMIT ?', limit); console.log(`[DATABASE] No keywords found, returning ${conversations.length} recent conversations`); return conversations; } // Build dynamic query to search both keywords and topics const placeholders = queryWords.map(() => '?').join(','); const keywordQuery = ` SELECT DISTINCT kc.conversation_id as id, kc.created_at, kc.updated_at, kc.question, kc.final_answer, ( SELECT COUNT(*) FROM conversation_keywords ck WHERE ck.conversation_id = kc.conversation_id AND LOWER(ck.keyword) IN (${placeholders}) ) + ( SELECT COUNT(*) FROM conversation_topics ct WHERE ct.conversation_id = kc.conversation_id AND LOWER(ct.topic) IN (${placeholders}) ) as relevance_score FROM knowledge_conversations kc WHERE kc.conversation_id IN ( SELECT DISTINCT conversation_id FROM conversation_keywords WHERE LOWER(keyword) IN (${placeholders}) UNION SELECT DISTINCT conversation_id FROM conversation_topics WHERE LOWER(topic) IN (${placeholders}) ) ORDER BY relevance_score DESC, kc.updated_at DESC LIMIT ? `; // Execute query with keywords repeated for each subquery const queryParams = [...queryWords, ...queryWords, ...queryWords, limit]; const conversations = await database.all(keywordQuery, queryParams); console.log(`[DATABASE] Found ${conversations.length} conversations matching keywords: ${queryWords.join(', ')}`); // If no topic-based matches found, fall back to recent conversations if (conversations.length === 0) { const recentConversations = await database.all('SELECT id, created_at, updated_at FROM conversations ORDER BY updated_at DESC LIMIT ?', limit); console.log(`[DATABASE] No topic matches, returning ${recentConversations.length} recent conversations`); return recentConversations; } return conversations; } catch (error) { console.error('[DATABASE] Error searching conversations by topic:', error); // Fallback to recent conversations on error try { const database = await getDatabase(); const conversations = await database.all('SELECT id, created_at, updated_at FROM conversations ORDER BY updated_at DESC LIMIT ?', limit); console.log(`[DATABASE] Error fallback: returning ${conversations.length} recent conversations`); return conversations; } catch (fallbackError) { console.error('[DATABASE] Fallback query also failed:', fallbackError); return []; } } } /** * Close the database connection */ /** * Record conversation usage for billing/limit tracking */ export async function recordConversationUsage(conversationId) { const database = await getDatabase(); await database.run('INSERT OR IGNORE INTO conversation_usage (conversation_id) VALUES (?)', conversationId); } /** * Get conversation usage count since a specific date */ export async function getConversationUsageCount(sinceDate) { const database = await getDatabase(); const result = await database.get('SELECT COUNT(*) as count FROM conversation_usage WHERE timestamp >= ?', sinceDate.toISOString()); return result?.count || 0; } /** * Get detailed usage statistics */ export async function getUsageStatistics(days = 30) { try { const database = await getDatabase(); const daysAgo = new Date(); daysAgo.setDate(daysAgo.getDate() - days); const twentyFourHoursAgo = new Date(); twentyFourHoursAgo.setTime(twentyFourHoursAgo.getTime() - (24 * 60 * 60 * 1000)); const sevenDaysAgo = new Date(); sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7); const [total, last24h, last7d] = await Promise.all([ database.get('SELECT COUNT(*) as count FROM conversation_usage WHERE timestamp >= ?', daysAgo.toISOString()), database.get('SELECT COUNT(*) as count FROM conversation_usage WHERE timestamp >= ?', twentyFourHoursAgo.toISOString()), database.get('SELECT COUNT(*) as count FROM conversation_usage WHERE timestamp >= ?', sevenDaysAgo.toISOString()) ]); const totalConversations = total?.count || 0; const dailyAverage = Math.round(totalConversations / days); return { totalConversations, dailyAverage, last24Hours: last24h?.count || 0, last7Days: last7d?.count || 0 }; } catch (error) { console.error('Error getting usage statistics:', error); return { totalConversations: 0, dailyAverage: 0, last24Hours: 0, last7Days: 0 }; } } // Re-export configuration functions from unified database export { setConfig, getConfig } from './unified-database.js'; /** * Delete a configuration value */ export async function deleteConfig(key) { const database = await getDatabase(); await database.run('DELETE FROM configurations WHERE key = ?', key); } /** * Get all configuration keys */ export async function getAllConfigKeys() { const database = await getDatabase(); const results = await database.all('SELECT key FROM configurations ORDER BY key'); return results.map(row => row.key); } /** * Store user license key and profile information */ export async function setUserProfile(email, licenseKey, tier = 'FREE') { // Generate a proper user ID - use email if available, otherwise generate UUID const userId = email || `user_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; await createUser(userId, email, licenseKey, tier); } /** * Get user profile by email */ export async function getUserProfile(email) { return await getUser(email); } // Re-export license functions from unified database export { getLicenseKeyFromDB, saveLicenseKeyToDB, getUserTier } from './unified-database.js'; // Re-export consensus profile functions from unified database export { createConsensusProfile } from './unified-database.js'; /** * Get a consensus profile by ID */ export async function getConsensusProfile(profileId) { const database = await getDatabase(); const result = await database.get('SELECT * FROM consensus_profiles WHERE id = ?', profileId); return result || null; } // Re-export from unified database export { getAllConsensusProfiles } from './unified-database.js'; // Re-export consensus profile functions from unified database export { setActiveConsensusProfile, getActiveConsensusProfile } from './unified-database.js'; /** * Delete a consensus profile */ export async function deleteConsensusProfile(profileId) { const database = await getDatabase(); // Check if this is the active profile const activeProfile = await getActiveConsensusProfile(); if (activeProfile?.id === profileId) { // Clear the active profile setting await database.run('DELETE FROM consensus_settings WHERE key = ?', 'active_profile_id'); } // Delete the profile await database.run('DELETE FROM consensus_profiles WHERE id = ?', profileId); } /** * Update a consensus profile */ export async function updateConsensusProfile(profileId, profileName, generatorModel, refinerModel, validatorModel, curatorModel) { const database = await getDatabase(); await database.run(`UPDATE consensus_profiles SET profile_name = ?, generator_model = ?, refiner_model = ?, validator_model = ?, curator_model = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`, profileName, generatorModel, refinerModel, validatorModel, curatorModel, profileId); } // Re-export OpenRouter functions from unified database export { getOpenRouterApiKey, saveOpenRouterApiKey, validateOpenRouterApiKey } from './unified-database.js'; // Re-export usage tracking functions from unified database export { getTierLimits, checkDailyUsageLimit, recordCompletedConversation } from './unified-database.js'; export async function closeDatabase() { await closeUnifiedDatabase(); } //# sourceMappingURL=database.js.map