@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
JavaScript
/**
* 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