const puppeteer = require('puppeteer'); const { browserConfig } = require('./browser_config'); const AuthHelper = require('./auth_helper'); const { getPuppeteerLaunchOptions } = require('./puppeteer_config'); const { setupDefaultModel } = require('./model_helper'); (async () => { const browser = await puppeteer.launch(getPuppeteerLaunchOptions()); const page = await browser.newPage(); // Log console errors page.on('console', msg => { if (msg.type() === 'error') { console.log(` Browser error: ${msg.text()}`); } }); try { // Register and login const auth = new AuthHelper(page); const username = 'metrics_' + Date.now(); const password = 'T3st!Secure#2024$LDR'; console.log('Creating user:', username); await auth.register(username, password); console.log('\n=== TESTING LLM DIRECTLY FIRST ===\n'); // First make a direct LLM API call to generate some early metrics await page.evaluate(async () => { try { const response = await fetch('/api/v1/quick_summary', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ query: 'What is JavaScript? Give a very brief answer.', iterations: 1, search_tool: 'wikipedia' }) }); console.log('API Response status:', response.status); console.log('API Response headers:', response.headers); let data; const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json')) { data = await response.json(); } else { data = await response.text(); } console.log('LLM API Response:', data); if (response.ok && typeof data === 'object' && data.summary) { console.log('✅ LLM query successful, tokens should be tracked'); console.log('Summary preview:', data.summary.substring(0, 100) + '...'); } else { console.log('❌ LLM query failed with status:', response.status); console.log('Error details:', data); } } catch (error) { console.log('Error calling LLM API:', error.toString()); } }); // Wait for metrics to be processed await new Promise(resolve => setTimeout(resolve, 2000)); console.log('\n=== CHECKING INITIAL METRICS ===\n'); // Navigate to metrics page to check if the LLM call was tracked await page.goto('http://127.0.0.1:5000/metrics', { waitUntil: 'domcontentloaded' }); const initialMetrics = await page.evaluate(() => { const content = { title: document.title, totalTokens: document.querySelector('[data-metric="total-tokens"]')?.textContent || document.querySelector('.metric-value')?.textContent || 'not found', metrics: Array.from(document.querySelectorAll('.metric-item, [class*="metric"]')).slice(0, 5).map(el => el.textContent.trim()) }; return content; }); console.log('Initial metrics page:', JSON.stringify(initialMetrics, null, 2)); console.log('\n=== CONFIGURING SETTINGS FOR FASTER TEST ===\n'); // Navigate to settings page to configure iterations and questions await page.goto('http://127.0.0.1:5000/settings', { waitUntil: 'domcontentloaded' }); // Set iterations to 1 await page.evaluate(() => { const iterationsInput = document.querySelector('input[name="search.iterations"]') || document.querySelector('#search\\.iterations'); if (iterationsInput) { iterationsInput.value = '1'; iterationsInput.dispatchEvent(new Event('change', { bubbles: true })); console.log('Set iterations to 1'); } }); // Set questions per iteration to 1 await page.evaluate(() => { const questionsInput = document.querySelector('input[name="search.questions_per_iteration"]') || document.querySelector('#search\\.questions_per_iteration'); if (questionsInput) { questionsInput.value = '1'; questionsInput.dispatchEvent(new Event('change', { bubbles: true })); console.log('Set questions to 1'); } }); // Save settings const saveButton = await page.$('button[type="submit"]'); if (saveButton) { await saveButton.click(); console.log('Settings saved'); await new Promise(resolve => setTimeout(resolve, 1000)); } console.log('\n=== STARTING RESEARCH ===\n'); // Navigate to home await page.goto('http://127.0.0.1:5000/', { waitUntil: 'domcontentloaded' }); // Fill form with a simple query await page.type('#query', 'What is Node.js?'); // Setup the default model await setupDefaultModel(page); // Set search engine await page.evaluate(() => { const searchInput = document.querySelector('#search_engine'); if (searchInput) { searchInput.value = 'searxng'; const inputEvent = new Event('input', { bubbles: true }); searchInput.dispatchEvent(inputEvent); console.log('Set search engine to:', searchInput.value); } }); // Wait a bit for UI to update await new Promise(resolve => setTimeout(resolve, 500)); // Submit await page.click('button[type="submit"]'); // Wait for navigation await page.waitForNavigation({ waitUntil: 'domcontentloaded' }); // Check where we ended up const url = page.url(); console.log('After submission URL:', url); if (url.includes('/progress/')) { console.log('✅ Research submitted successfully!'); const researchId = url.split('/progress/')[1]; console.log('Research ID:', researchId); // Monitor progress for up to 90 seconds const startTime = Date.now(); const maxWaitTime = 90000; // 90 seconds for faster test let isComplete = false; while (!isComplete && (Date.now() - startTime) < maxWaitTime) { // Get progress page content const progressContent = await page.evaluate(() => { const content = { status: document.querySelector('.status')?.textContent.trim() || document.querySelector('[class*="status"]')?.textContent.trim() || 'not found', progressBar: document.querySelector('.progress-bar')?.style.width || document.querySelector('[class*="progress-bar"]')?.style.width || 'not found', currentTask: document.querySelector('.current-task')?.textContent.trim() || document.querySelector('[class*="current-task"]')?.textContent.trim() || 'not found' }; return content; }); console.log(`Progress: ${progressContent.progressBar} - ${progressContent.currentTask}`); // Check if complete if (progressContent.status.toLowerCase().includes('complete') || progressContent.status.toLowerCase().includes('finished') || progressContent.progressBar === '100%') { isComplete = true; console.log('\n✅ Research completed!'); } // Wait 3 seconds before checking again await new Promise(resolve => setTimeout(resolve, 3000)); } console.log('\n=== CHECKING METRICS AFTER RESEARCH ===\n'); // Navigate to metrics page await page.goto('http://127.0.0.1:5000/metrics', { waitUntil: 'domcontentloaded' }); // Wait for metrics to load await new Promise(resolve => setTimeout(resolve, 2000)); // Get detailed metrics const finalMetrics = await page.evaluate(() => { const getTextContent = (selector) => { const el = document.querySelector(selector); return el ? el.textContent.trim() : 'not found'; }; const content = { title: document.title, // Try different selectors for metrics totalTokens: getTextContent('[data-metric="total-tokens"]') || getTextContent('.total-tokens') || getTextContent('#total-tokens'), totalCalls: getTextContent('[data-metric="total-calls"]') || getTextContent('.total-calls') || getTextContent('#total-calls'), totalCost: getTextContent('[data-metric="total-cost"]') || getTextContent('.total-cost') || getTextContent('#total-cost'), // Get all metric cards metricCards: Array.from(document.querySelectorAll('.metric-card, .ldr-card')).map(card => { return { title: card.querySelector('.card-title, h5, h6')?.textContent.trim() || '', value: card.querySelector('.metric-value, .display-4, .h2')?.textContent.trim() || '', text: card.textContent.trim() }; }).filter(card => card.title || card.value), // Get any tables tables: Array.from(document.querySelectorAll('table')).map(table => { const headers = Array.from(table.querySelectorAll('th')).map(th => th.textContent.trim()); const firstRow = table.querySelector('tbody tr'); const firstRowData = firstRow ? Array.from(firstRow.querySelectorAll('td')).map(td => td.textContent.trim()) : []; return { headers, firstRow: firstRowData, rowCount: table.querySelectorAll('tbody tr').length }; }) }; return content; }); console.log('Final metrics page:', JSON.stringify(finalMetrics, null, 2)); // Check if metrics were recorded if (finalMetrics.metricCards.length > 0 || finalMetrics.tables.some(t => t.rowCount > 0)) { console.log('\n✅ Metrics are being tracked!'); // Try to get specific token counts const hasTokenData = finalMetrics.metricCards.some(card => card.text.toLowerCase().includes('token') && card.value && card.value !== '0' && card.value !== 'not found' ); if (hasTokenData) { console.log('✅ Token usage data found in metrics!'); } else { console.log('⚠️ No token usage data found in metrics cards'); } } else { console.log('\n❌ No metrics data found on page'); } // Also check the cost analytics page console.log('\n=== CHECKING COST ANALYTICS ===\n'); await page.goto('http://127.0.0.1:5000/metrics/costs', { waitUntil: 'domcontentloaded' }); const costMetrics = await page.evaluate(() => { const content = { title: document.title, hasData: document.querySelectorAll('table tbody tr').length > 0, tableRows: document.querySelectorAll('table tbody tr').length, firstRowData: (() => { const firstRow = document.querySelector('table tbody tr'); if (!firstRow) return null; return Array.from(firstRow.querySelectorAll('td')).map(td => td.textContent.trim()); })() }; return content; }); console.log('Cost analytics page:', JSON.stringify(costMetrics, null, 2)); if (costMetrics.hasData) { console.log('✅ Cost data is being tracked!'); } else { console.log('⚠️ No cost data found'); } } else { console.log('❌ Failed to submit research'); // Check for errors const errors = await page.evaluate(() => { return Array.from(document.querySelectorAll('.error, .alert')).map(el => el.textContent); }); console.log('Errors found:', errors); } } catch (error) { console.error('Test failed:', error); process.exitCode = 1; } finally { console.log('\n=== TEST COMPLETE ===\n'); await browser.close(); } })().catch(err => { console.error('Unhandled error:', err); process.exitCode = 1; });