#!/usr/bin/env node /** * Embeddings-Based Conversation Memory Test Suite * * Tests the vector embeddings functionality for semantic search */ const axios = require('axios'); const BASE_URL = 'http://localhost:3020/api/v1/ai'; const colors = { reset: '\x1b[0m', green: '\x1b[32m', red: '\x1b[31m', yellow: '\x1b[33m', blue: '\x1b[34m', cyan: '\x1b[36m', }; function log(color, message) { console.log(`${colors[color]}${message}${colors.reset}`); } function logTest(testName) { console.log(`\n${colors.cyan}━━━ ${testName} ━━━${colors.reset}`); } function logSuccess(message) { log('green', `✓ ${message}`); } function logError(message) { log('red', `✗ ${message}`); } function logInfo(message) { log('blue', `ℹ ${message}`); } async function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // Test 1: Health Check async function testHealthCheck() { logTest('Test 1: Embeddings Service Health Check'); try { const response = await axios.get(`${BASE_URL}/test/embeddings/health`); if (response.data.success && response.data.data.status === 'ok') { logSuccess(`Health check passed: ${response.data.data.message}`); return true; } else { logError(`Health check failed: ${response.data.data.message}`); return false; } } catch (error) { logError(`Health check error: ${error.message}`); if (error.response?.data) { console.log(JSON.stringify(error.response.data, null, 2)); } return false; } } // Test 2: Generate Embedding async function testGenerateEmbedding() { logTest('Test 2: Generate Vector Embedding'); try { const testText = "My baby had a feeding session with 4 oz of formula"; logInfo(`Generating embedding for: "${testText}"`); const response = await axios.post(`${BASE_URL}/test/embeddings/generate`, { text: testText }); if (response.data.success) { const { dimensions, tokenCount, model, preview } = response.data.data; logSuccess(`Embedding generated successfully`); logInfo(` Model: ${model}`); logInfo(` Dimensions: ${dimensions}`); logInfo(` Token count: ${tokenCount}`); logInfo(` Preview (first 5): [${preview.join(', ')}...]`); return true; } else { logError('Embedding generation failed'); return false; } } catch (error) { logError(`Embedding generation error: ${error.message}`); if (error.response?.data) { console.log(JSON.stringify(error.response.data, null, 2)); } return false; } } // Test 3: Create Conversation with Embeddings async function testCreateConversationWithEmbeddings() { logTest('Test 3: Create Conversation and Store Embeddings'); try { const conversations = [ { message: "My baby slept for 3 hours during the night", topic: "sleep" }, { message: "She had a feeding session with 5 oz of formula", topic: "feeding" }, { message: "Changed a wet diaper at 3pm", topic: "diaper" }, { message: "Baby has a slight fever, should I be worried?", topic: "health" }, { message: "She started crawling today! So excited!", topic: "development" }, ]; const conversationIds = []; for (const conv of conversations) { logInfo(`Creating conversation: "${conv.message}" (${conv.topic})`); const response = await axios.post(`${BASE_URL}/chat`, { message: conv.message }); if (response.data.success) { const conversationId = response.data.data.conversationId; conversationIds.push({ id: conversationId, topic: conv.topic, message: conv.message }); logSuccess(` Created conversation ${conversationId}`); logInfo(` AI Response: ${response.data.data.message.substring(0, 100)}...`); } else { logError(` Failed to create conversation`); } // Wait to allow embeddings to be stored await sleep(1000); } logSuccess(`Created ${conversationIds.length} conversations with embeddings`); return conversationIds; } catch (error) { logError(`Conversation creation error: ${error.message}`); if (error.response?.data) { console.log(JSON.stringify(error.response.data, null, 2)); } return []; } } // Test 4: Semantic Search async function testSemanticSearch(conversationIds) { logTest('Test 4: Semantic Search for Similar Conversations'); const searchQueries = [ { query: "How long should my baby sleep at night?", expectedTopic: "sleep" }, { query: "What's the right amount of milk for feeding?", expectedTopic: "feeding" }, { query: "When should I change diapers?", expectedTopic: "diaper" }, { query: "Is a high temperature dangerous?", expectedTopic: "health" }, { query: "What are the milestones for a 6 month old?", expectedTopic: "development" }, ]; let successCount = 0; for (const searchQuery of searchQueries) { logInfo(`\nSearching: "${searchQuery.query}"`); try { const response = await axios.post(`${BASE_URL}/test/embeddings/search`, { query: searchQuery.query, userId: 'test_user_123', threshold: 0.5, limit: 3 }); if (response.data.success && response.data.data.results.length > 0) { const results = response.data.data.results; logSuccess(` Found ${results.length} similar conversation(s)`); results.forEach((result, index) => { const similarity = (result.similarity * 100).toFixed(1); logInfo(` ${index + 1}. Similarity: ${similarity}%`); logInfo(` Topics: [${result.topics.join(', ')}]`); logInfo(` Content: "${result.messageContent.substring(0, 60)}..."`); // Check if expected topic is in results if (result.topics.includes(searchQuery.expectedTopic)) { logSuccess(` ✓ Found expected topic: ${searchQuery.expectedTopic}`); } }); successCount++; } else { logError(` No similar conversations found`); } } catch (error) { logError(` Search error: ${error.message}`); if (error.response?.data) { console.log(JSON.stringify(error.response.data, null, 2)); } } } logInfo(`\nSemantic search success rate: ${successCount}/${searchQueries.length}`); return successCount === searchQueries.length; } // Test 5: Get Embeddings Stats async function testEmbeddingsStats() { logTest('Test 5: Get User Embeddings Statistics'); try { const response = await axios.get(`${BASE_URL}/test/embeddings/stats/test_user_123`); if (response.data.success) { const stats = response.data.data; logSuccess('Retrieved embeddings statistics'); logInfo(` Total embeddings: ${stats.totalEmbeddings}`); logInfo(` Conversations with embeddings: ${stats.conversationsWithEmbeddings}`); logInfo(` Topics distribution:`); Object.entries(stats.topicsDistribution).forEach(([topic, count]) => { logInfo(` - ${topic}: ${count}`); }); return true; } else { logError('Failed to retrieve stats'); return false; } } catch (error) { logError(`Stats retrieval error: ${error.message}`); if (error.response?.data) { console.log(JSON.stringify(error.response.data, null, 2)); } return false; } } // Test 6: Conversation with Semantic Memory async function testConversationWithSemanticMemory() { logTest('Test 6: New Conversation Using Semantic Memory'); try { logInfo('Creating follow-up question that should find semantic context...'); const response = await axios.post(`${BASE_URL}/chat`, { message: "My baby is having trouble sleeping, any tips?" }); if (response.data.success) { logSuccess('Conversation created with semantic context'); logInfo(`AI Response: ${response.data.data.message.substring(0, 200)}...`); // Check if response seems contextual (contains sleep-related info) const responseText = response.data.data.message.toLowerCase(); if (responseText.includes('sleep') || responseText.includes('nap')) { logSuccess('Response appears to use semantic context (mentions sleep)'); return true; } else { logInfo('Response created, but semantic context usage unclear'); return true; } } else { logError('Conversation creation failed'); return false; } } catch (error) { logError(`Semantic memory test error: ${error.message}`); if (error.response?.data) { console.log(JSON.stringify(error.response.data, null, 2)); } return false; } } // Main test runner async function runTests() { console.log(`\n${colors.yellow}╔════════════════════════════════════════════════╗${colors.reset}`); console.log(`${colors.yellow}║ Embeddings-Based Conversation Memory Tests ║${colors.reset}`); console.log(`${colors.yellow}╚════════════════════════════════════════════════╝${colors.reset}\n`); const results = { total: 6, passed: 0, failed: 0 }; // Test 1: Health Check if (await testHealthCheck()) { results.passed++; } else { results.failed++; logError('Health check failed - stopping tests'); return results; } await sleep(500); // Test 2: Generate Embedding if (await testGenerateEmbedding()) { results.passed++; } else { results.failed++; } await sleep(500); // Test 3: Create Conversations const conversationIds = await testCreateConversationWithEmbeddings(); if (conversationIds.length > 0) { results.passed++; } else { results.failed++; } await sleep(2000); // Wait for embeddings to be stored // Test 4: Semantic Search if (await testSemanticSearch(conversationIds)) { results.passed++; } else { results.failed++; } await sleep(500); // Test 5: Embeddings Stats if (await testEmbeddingsStats()) { results.passed++; } else { results.failed++; } await sleep(500); // Test 6: Semantic Memory if (await testConversationWithSemanticMemory()) { results.passed++; } else { results.failed++; } // Summary console.log(`\n${colors.yellow}╔════════════════════════════════════════════════╗${colors.reset}`); console.log(`${colors.yellow}║ Test Summary ║${colors.reset}`); console.log(`${colors.yellow}╚════════════════════════════════════════════════╝${colors.reset}\n`); log('blue', `Total tests: ${results.total}`); log('green', `Passed: ${results.passed}`); if (results.failed > 0) { log('red', `Failed: ${results.failed}`); } else { log('green', `Failed: ${results.failed}`); } const successRate = ((results.passed / results.total) * 100).toFixed(1); console.log(); if (results.failed === 0) { log('green', `✓ All tests passed! (${successRate}%)`); } else { log('yellow', `⚠ Some tests failed (${successRate}% success rate)`); } console.log(); return results; } // Run tests runTests().catch(error => { logError(`Fatal error: ${error.message}`); console.error(error); process.exit(1); });