// Inspiration House JavaScript // Store API key and state in session storage let apiKey = sessionStorage.getItem('deepseekApiKey'); let modelProvider = sessionStorage.getItem('inspirationHouseProvider') || 'deepseek'; let modelName = sessionStorage.getItem('inspirationHouseModelName') || 'deepseek-chat'; let researchTarget = sessionStorage.getItem('inspirationHouseTarget') || ''; let currentExpression = sessionStorage.getItem('inspirationHouseExpression') || ''; let expressionContext = sessionStorage.getItem('inspirationHouseContext') || ''; let evaluationResults = JSON.parse(sessionStorage.getItem('inspirationHouseResults')) || []; let operatorsList = JSON.parse(sessionStorage.getItem('brainOperators')) || []; let isEvaluating = false; let evaluationProgress = 0; let totalOperators = 0; let batchSize = parseInt(sessionStorage.getItem('inspirationHouseBatchSize')) || 100; // Configurable batch size // DOM Elements const modelProviderSelect = document.getElementById('modelProvider'); const apiKeyInput = document.getElementById('apiKey'); const modelNameInput = document.getElementById('modelName'); const batchSizeInput = document.getElementById('batchSize'); const saveApiKeyBtn = document.getElementById('saveApiKey'); const apiConfigSection = document.getElementById('apiConfigSection'); const showApiConfigSection = document.getElementById('showApiConfigSection'); const showApiConfigBtn = document.getElementById('showApiConfig'); const editTargetBtn = document.getElementById('editTarget'); const targetDisplay = document.getElementById('targetDisplay'); const targetText = document.getElementById('targetText'); const targetInputGroup = document.getElementById('targetInputGroup'); const researchTargetInput = document.getElementById('researchTarget'); const saveTargetBtn = document.getElementById('saveTarget'); const cancelTargetBtn = document.getElementById('cancelTarget'); const currentExpressionInput = document.getElementById('currentExpression'); const expressionContextInput = document.getElementById('expressionContext'); const loadFromBRAINBtn = document.getElementById('loadFromBRAIN'); const startEvaluationBtn = document.getElementById('startEvaluation'); const refreshEvaluationBtn = document.getElementById('refreshEvaluation'); const exportResultsBtn = document.getElementById('exportResults'); const clearResultsBtn = document.getElementById('clearResults'); const evaluationTableBody = document.getElementById('evaluationTableBody'); const progressSection = document.getElementById('progressSection'); const progressFill = document.getElementById('progressFill'); const progressText = document.getElementById('progressText'); const progressCount = document.getElementById('progressCount'); const summaryStats = document.getElementById('summaryStats'); const highScoreCount = document.getElementById('highScoreCount'); const mediumScoreCount = document.getElementById('mediumScoreCount'); const lowScoreCount = document.getElementById('lowScoreCount'); const totalEvaluated = document.getElementById('totalEvaluated'); const minScoreFilter = document.getElementById('minScoreFilter'); const maxScoreFilter = document.getElementById('maxScoreFilter'); const minScoreValue = document.getElementById('minScoreValue'); const maxScoreValue = document.getElementById('maxScoreValue'); const showHighScores = document.getElementById('showHighScores'); const showMediumScores = document.getElementById('showMediumScores'); const showLowScores = document.getElementById('showLowScores'); const exportHighScoresBtn = document.getElementById('exportHighScores'); const exportAllResultsBtn = document.getElementById('exportAllResults'); const exportCSVBtn = document.getElementById('exportCSV'); const currentBatchSizeSpan = document.getElementById('currentBatchSize'); // Initialize API key if exists if (apiKey) { apiKeyInput.value = apiKey; } // Initialize model provider and name modelProviderSelect.value = modelProvider; modelNameInput.value = modelName; // Initialize batch size batchSizeInput.value = batchSize; currentBatchSizeSpan.textContent = operatorsList.length; // Update model name placeholder based on provider function updateModelNamePlaceholder() { const provider = modelProviderSelect.value; if (provider === 'kimi') { modelNameInput.placeholder = 'e.g., kimi-k2-0711-preview'; if (modelNameInput.value === 'deepseek-chat') { modelNameInput.value = 'kimi-k2-0711-preview'; } } else { modelNameInput.placeholder = 'e.g., deepseek-chat, deepseek-coder'; if (modelNameInput.value === 'kimi-k2-0711-preview') { modelNameInput.value = 'deepseek-chat'; } } } // Model provider change handler modelProviderSelect.addEventListener('change', () => { modelProvider = modelProviderSelect.value; sessionStorage.setItem('inspirationHouseProvider', modelProvider); updateModelNamePlaceholder(); }); // Model name change handler modelNameInput.addEventListener('input', () => { modelName = modelNameInput.value; sessionStorage.setItem('inspirationHouseModelName', modelName); }); // Save batch size when changed batchSizeInput.addEventListener('change', () => { batchSize = parseInt(batchSizeInput.value) || 100; sessionStorage.setItem('inspirationHouseBatchSize', batchSize.toString()); console.log(`Batch size updated to: ${batchSize}`); }); // Initialize placeholder on page load updateModelNamePlaceholder(); // Check if API is already configured and hide config section if so function checkApiConfigStatus() { if (apiKey && modelProvider && modelName) { apiConfigSection.style.display = 'none'; showApiConfigSection.style.display = 'block'; } else { apiConfigSection.style.display = 'block'; showApiConfigSection.style.display = 'none'; } } // Show API Config button event listener showApiConfigBtn.addEventListener('click', () => { apiConfigSection.style.display = 'block'; showApiConfigSection.style.display = 'none'; }); // Load existing state on page load window.addEventListener('DOMContentLoaded', () => { console.log('Loading Inspiration House state...'); console.log('Research target:', researchTarget); console.log('Current expression:', currentExpression); console.log('Evaluation results:', evaluationResults); console.log('Operators list length:', operatorsList.length); // Check API config status checkApiConfigStatus(); // Load saved state if (researchTarget) { targetText.textContent = researchTarget; } if (currentExpression) { currentExpressionInput.value = currentExpression; } if (expressionContext) { expressionContextInput.value = expressionContext; } // Check for operators in sessionStorage (from brain.js) refreshOperatorsFromSessionStorage(); // Update operator count display currentBatchSizeSpan.textContent = operatorsList.length; // Load operators if not already loaded if (operatorsList.length === 0) { loadOperatorsFromBRAIN(); } // Add a fallback message if no operators are available if (operatorsList.length === 0) { console.log('No operators loaded yet. Will load when BRAIN is connected.'); } // Display existing results if (evaluationResults.length > 0) { displayEvaluationResults(); updateSummaryStats(); } // Setup filter event listeners setupFilters(); }); // Function to refresh operators from sessionStorage (called after BRAIN login) function refreshOperatorsFromSessionStorage() { const storedOperators = sessionStorage.getItem('brainOperators'); if (storedOperators) { try { operatorsList = JSON.parse(storedOperators); currentBatchSizeSpan.textContent = operatorsList.length; console.log(`Refreshed ${operatorsList.length} operators from sessionStorage`); } catch (error) { console.error('Error parsing operators from sessionStorage:', error); } } } // Save API Key and Test Connection saveApiKeyBtn.addEventListener('click', async () => { const newApiKey = apiKeyInput.value.trim(); const newProvider = modelProviderSelect.value; const newModelName = modelNameInput.value.trim(); if (!newApiKey) { showNotification('Please enter a valid API key', 'error'); return; } if (!newModelName) { showNotification('Please enter a model name', 'error'); return; } try { showLoading('Testing API connection...'); const response = await fetch('/inspiration-house/api/test-deepseek', { method: 'POST', headers: { 'X-API-Key': newApiKey, 'Content-Type': 'application/json' }, body: JSON.stringify({ provider: newProvider, model_name: newModelName }) }); const data = await response.json(); if (response.ok && data.success) { sessionStorage.setItem('deepseekApiKey', newApiKey); sessionStorage.setItem('inspirationHouseProvider', newProvider); sessionStorage.setItem('inspirationHouseModelName', newModelName); apiKey = newApiKey; modelProvider = newProvider; modelName = newModelName; showNotification(`${newProvider.charAt(0).toUpperCase() + newProvider.slice(1)} API connection successful`, 'success'); // Hide API config section after successful configuration apiConfigSection.style.display = 'none'; showApiConfigSection.style.display = 'block'; } else { showNotification(`API Error: ${data.error || 'Unknown error'}`, 'error'); console.error('API Error Details:', data); // Provide specific guidance for Kimi API issues if (newProvider === 'kimi' && data.error) { console.log('Kimi API troubleshooting tips:'); console.log('1. Make sure you have a valid Kimi API key'); console.log('2. Check if your Kimi account has API access enabled'); console.log('3. Verify the model name is correct (e.g., kimi-k2-0711-preview)'); console.log('4. Ensure you have sufficient API credits/quota'); } } } catch (error) { showNotification('Error testing API connection: ' + error.message, 'error'); console.error('API Test Error:', error); } finally { hideLoading(); } }); // Target Management editTargetBtn.addEventListener('click', () => { targetDisplay.style.display = 'none'; targetInputGroup.style.display = 'block'; researchTargetInput.value = researchTarget; researchTargetInput.focus(); }); saveTargetBtn.addEventListener('click', () => { const newTarget = researchTargetInput.value.trim(); if (!newTarget) { showNotification('Please enter a research target', 'error'); return; } researchTarget = newTarget; targetText.textContent = researchTarget; sessionStorage.setItem('inspirationHouseTarget', researchTarget); targetDisplay.style.display = 'block'; targetInputGroup.style.display = 'none'; showNotification('Research target saved successfully', 'success'); }); cancelTargetBtn.addEventListener('click', () => { targetDisplay.style.display = 'block'; targetInputGroup.style.display = 'none'; researchTargetInput.value = researchTarget; }); // Expression Management currentExpressionInput.addEventListener('input', () => { currentExpression = currentExpressionInput.value; sessionStorage.setItem('inspirationHouseExpression', currentExpression); }); expressionContextInput.addEventListener('input', () => { expressionContext = expressionContextInput.value; sessionStorage.setItem('inspirationHouseContext', expressionContext); }); // Load from BRAIN loadFromBRAINBtn.addEventListener('click', () => { if (!sessionStorage.getItem('brain_session_id')) { // Use the same login modal as the main page if (window.brainAPI && window.brainAPI.openBrainLoginModal) { window.brainAPI.openBrainLoginModal(); } else { showNotification('BRAIN login not available. Please go to the main page to connect to BRAIN.', 'error'); return; } // Set up a listener for when operators are loaded (after successful login) const checkOperatorsInterval = setInterval(() => { const storedOperators = sessionStorage.getItem('brainOperators'); if (storedOperators) { clearInterval(checkOperatorsInterval); refreshOperatorsFromSessionStorage(); showNotification(`Loaded ${operatorsList.length} operators from BRAIN`, 'success'); } }, 1000); // Stop checking after 30 seconds setTimeout(() => { clearInterval(checkOperatorsInterval); }, 30000); } else { // Load current expression from BRAIN (placeholder for now) showNotification('Loading from BRAIN...', 'warning'); // TODO: Implement BRAIN expression loading } }); // Start AI Evaluation startEvaluationBtn.addEventListener('click', async () => { if (!apiKey) { showNotification('Please configure your Deepseek API key first', 'error'); return; } if (!researchTarget) { showNotification('Please set a research target first', 'error'); return; } if (!currentExpression) { showNotification('Please enter your current expression', 'error'); return; } if (operatorsList.length === 0) { showNotification('No operators available. Please connect to BRAIN first to load operators.', 'warning'); return; } if (isEvaluating) { showNotification('Evaluation already in progress', 'warning'); return; } try { isEvaluating = true; startEvaluationBtn.disabled = true; startEvaluationBtn.textContent = 'Evaluating...'; showProgressSection(); evaluationResults = []; // Update progress to show batch processing progressText.textContent = `Evaluating ${totalOperators} operators with batch size ${batchSize}...`; progressCount.textContent = `0 / ${totalOperators}`; progressFill.style.width = '0%'; progressFill.textContent = 'Starting...'; await evaluateAllOperators(); // Update progress to show completion progressFill.style.width = '100%'; progressFill.textContent = '100%'; progressCount.textContent = `${totalOperators} / ${totalOperators}`; progressText.textContent = 'Evaluation completed!'; showNotification('Evaluation completed successfully', 'success'); } catch (error) { showNotification('Error during evaluation: ' + error.message, 'error'); console.error('Evaluation Error:', error); } finally { isEvaluating = false; startEvaluationBtn.disabled = false; startEvaluationBtn.textContent = 'Start AI Evaluation'; hideProgressSection(); } }); // Evaluate all operators using multithreading async function evaluateAllOperators() { totalOperators = operatorsList.length; evaluationProgress = 0; console.log(`Starting evaluation of ${totalOperators} operators with ${totalOperators} workers...`); // Use the new batch evaluation endpoint for better performance try { const response = await fetch('/inspiration-house/api/batch-evaluate', { method: 'POST', headers: { 'X-API-Key': apiKey, 'Content-Type': 'application/json' }, body: JSON.stringify({ operators: operatorsList, research_target: researchTarget, current_expression: currentExpression, expression_context: expressionContext, batch_size: batchSize, provider: modelProvider, model_name: modelName }) }); const data = await response.json(); if (response.ok && data.success) { evaluationResults = data.results; console.log(`Batch evaluation completed. Processed ${evaluationResults.length} operators with ${data.workers_used} workers`); showNotification(`Evaluation completed! Processed ${evaluationResults.length} operators using ${data.workers_used} parallel workers`, 'success'); } else { console.error('Batch evaluation failed:', data.error); showNotification(`Evaluation failed: ${data.error}`, 'error'); return; } } catch (error) { console.error('Error in batch evaluation:', error); showNotification(`Evaluation error: ${error.message}`, 'error'); return; } // Save results saveEvaluationResults(); // Display results displayEvaluationResults(); updateSummaryStats(); console.log(`Evaluation completed. Processed ${evaluationResults.length} operators.`); } // Evaluate a single operator async function evaluateSingleOperator(operator) { try { const response = await fetch('/inspiration-house/api/evaluate-operator', { method: 'POST', headers: { 'X-API-Key': apiKey, 'Content-Type': 'application/json' }, body: JSON.stringify({ operator: operator, research_target: researchTarget, current_expression: currentExpression, expression_context: expressionContext, provider: modelProvider, model_name: modelName }) }); const data = await response.json(); if (response.ok && data.success) { return { operator: operator.name, category: operator.category || 'Unknown', score: data.score, reason: data.reason, timestamp: new Date().toISOString() }; } else { console.error(`API Error for operator ${operator.name}:`, data.error); return { operator: operator.name, category: operator.category || 'Unknown', score: 0, reason: `Error: ${data.error || 'Unknown error'}`, timestamp: new Date().toISOString() }; } } catch (error) { console.error(`Network error for operator ${operator.name}:`, error); return { operator: operator.name, category: operator.category || 'Unknown', score: 0, reason: `Network error: ${error.message}`, timestamp: new Date().toISOString() }; } } // Load operators from BRAIN async function loadOperatorsFromBRAIN() { try { const response = await fetch('/api/operators'); const data = await response.json(); if (response.ok && Array.isArray(data)) { operatorsList = data; sessionStorage.setItem('brainOperators', JSON.stringify(operatorsList)); currentBatchSizeSpan.textContent = operatorsList.length; console.log(`Loaded ${operatorsList.length} operators from BRAIN`); showNotification(`Loaded ${operatorsList.length} operators from BRAIN`, 'success'); } else { console.error('Failed to load operators:', data.error); if (data.error && data.error.includes('Invalid or expired session')) { showNotification('Please connect to BRAIN first to load operators', 'warning'); } else { showNotification('Failed to load operators from BRAIN', 'error'); } } } catch (error) { console.error('Error loading operators:', error); showNotification('Error connecting to BRAIN API', 'error'); } } // Convert markdown to HTML for better display function convertMarkdownToHTML(text) { if (!text) return ''; return text // Bold text: **text** -> text .replace(/\*\*(.*?)\*\*/g, '$1') // Italic text: *text* -> text .replace(/\*(.*?)\*/g, '$1') // Code: `text` -> text .replace(/`(.*?)`/g, '$1') // Headers: ### text ->

text

.replace(/^### (.+)$/gm, '

$1

') .replace(/^## (.+)$/gm, '

$1

') .replace(/^# (.+)$/gm, '

$1

') // Lists: - item ->
  • item
  • .replace(/^- (.+)$/gm, '
  • $1
  • ') // Numbered lists: 1. item ->
  • item
  • .replace(/^(\d+)\. (.+)$/gm, '
  • $2
  • ') // Line breaks: \n ->
    .replace(/\n/g, '
    ') // Escape HTML characters .replace(/&/g, '&') .replace(//g, '>') // Restore our HTML tags .replace(/<strong>/g, '') .replace(/<\/strong>/g, '') .replace(/<em>/g, '') .replace(/<\/em>/g, '') .replace(/<code>/g, '') .replace(/<\/code>/g, '') .replace(/<br>/g, '
    ') .replace(/<h[234]>/g, '<$1>') .replace(/<\/h[234]>/g, '') .replace(/<li>/g, '
  • ') .replace(/<\/li>/g, '
  • '); } // Display evaluation results function displayEvaluationResults() { if (evaluationResults.length === 0) { evaluationTableBody.innerHTML = ` No evaluations yet. Set your target and expression, then click "Start AI Evaluation". `; return; } // Apply filters const filteredResults = applyFilters(evaluationResults); // Sort by score (highest first) filteredResults.sort((a, b) => b.score - a.score); evaluationTableBody.innerHTML = filteredResults.map(result => { // Find the operator details from the operators list const operatorDetails = operatorsList.find(op => op.name === result.operator); const description = operatorDetails ? operatorDetails.description || '' : ''; const definition = operatorDetails ? operatorDetails.definition || '' : ''; return `
    ${result.operator}
    ${result.category}
    ${description ? `
    ${convertMarkdownToHTML(description)}
    ` : ''} ${definition ? `
    Definition: ${convertMarkdownToHTML(definition)}
    ` : ''} ${result.category} ${result.score}/10
    ${convertMarkdownToHTML(result.reason)}
    `; }).join(''); } // Apply filters to results function applyFilters(results) { const minScore = parseInt(minScoreFilter.value); const maxScore = parseInt(maxScoreFilter.value); return results.filter(result => { const score = result.score; // Score range filter if (score < minScore || score > maxScore) { return false; } // Score category filters if (score >= 8 && !showHighScores.checked) return false; if (score >= 4 && score < 8 && !showMediumScores.checked) return false; if (score < 4 && !showLowScores.checked) return false; return true; }); } // Get CSS class for score function getScoreClass(score) { if (score >= 8) return 'score-high'; if (score >= 4) return 'score-medium'; return 'score-low'; } // Setup filter event listeners function setupFilters() { minScoreFilter.addEventListener('input', () => { minScoreValue.textContent = minScoreFilter.value; displayEvaluationResults(); }); maxScoreFilter.addEventListener('input', () => { maxScoreValue.textContent = maxScoreFilter.value; displayEvaluationResults(); }); showHighScores.addEventListener('change', displayEvaluationResults); showMediumScores.addEventListener('change', displayEvaluationResults); showLowScores.addEventListener('change', displayEvaluationResults); } // Update progress display function updateProgress() { const percentage = Math.round((evaluationProgress / totalOperators) * 100); progressFill.style.width = `${percentage}%`; progressFill.textContent = `${percentage}%`; progressCount.textContent = `${evaluationProgress} / ${totalOperators}`; progressText.textContent = `Evaluating operators with ${totalOperators} parallel workers... (${percentage}% complete)`; } // Show/hide progress section function showProgressSection() { progressSection.style.display = 'block'; } function hideProgressSection() { progressSection.style.display = 'none'; } // Update summary statistics function updateSummaryStats() { if (evaluationResults.length === 0) { summaryStats.style.display = 'none'; return; } const highScores = evaluationResults.filter(r => r.score >= 8).length; const mediumScores = evaluationResults.filter(r => r.score >= 4 && r.score < 8).length; const lowScores = evaluationResults.filter(r => r.score < 4).length; highScoreCount.textContent = highScores; mediumScoreCount.textContent = mediumScores; lowScoreCount.textContent = lowScores; totalEvaluated.textContent = evaluationResults.length; summaryStats.style.display = 'grid'; } // Save evaluation results function saveEvaluationResults() { sessionStorage.setItem('inspirationHouseResults', JSON.stringify(evaluationResults)); } // Refresh evaluation results refreshEvaluationBtn.addEventListener('click', () => { displayEvaluationResults(); updateSummaryStats(); }); // Clear results clearResultsBtn.addEventListener('click', () => { if (confirm('Are you sure you want to clear all evaluation results?')) { evaluationResults = []; sessionStorage.removeItem('inspirationHouseResults'); displayEvaluationResults(); updateSummaryStats(); showNotification('Results cleared', 'success'); } }); // Export functions exportResultsBtn.addEventListener('click', () => { exportResults('all'); }); exportHighScoresBtn.addEventListener('click', () => { exportResults('high'); }); exportAllResultsBtn.addEventListener('click', () => { exportResults('all'); }); exportCSVBtn.addEventListener('click', () => { exportResults('csv'); }); function exportResults(type) { let dataToExport = evaluationResults; if (type === 'high') { dataToExport = evaluationResults.filter(r => r.score >= 8); } if (dataToExport.length === 0) { showNotification('No results to export', 'warning'); return; } if (type === 'csv') { exportAsCSV(dataToExport); } else { exportAsJSON(dataToExport); } } function exportAsJSON(data) { const exportData = { timestamp: new Date().toISOString(), research_target: researchTarget, current_expression: currentExpression, expression_context: expressionContext, results: data }; const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `inspiration_house_results_${new Date().toISOString().split('T')[0]}.json`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); showNotification('Results exported as JSON', 'success'); } function exportAsCSV(data) { const headers = ['Operator', 'Category', 'Score', 'Reason', 'Timestamp']; const csvContent = [ headers.join(','), ...data.map(result => [ `"${result.operator}"`, `"${result.category}"`, result.score, `"${result.reason.replace(/"/g, '""')}"`, `"${result.timestamp}"` ].join(',')) ].join('\n'); const blob = new Blob([csvContent], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `inspiration_house_results_${new Date().toISOString().split('T')[0]}.csv`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); showNotification('Results exported as CSV', 'success'); } // Re-evaluate a single operator async function reevaluateOperator(operatorName) { const operator = operatorsList.find(op => op.name === operatorName); if (!operator) { showNotification('Operator not found', 'error'); return; } try { showLoading(`Re-evaluating ${operatorName}...`); const result = await evaluateSingleOperator(operator); // Update existing result const index = evaluationResults.findIndex(r => r.operator === operatorName); if (index !== -1) { evaluationResults[index] = result; } else { evaluationResults.push(result); } saveEvaluationResults(); displayEvaluationResults(); updateSummaryStats(); showNotification(`${operatorName} re-evaluated successfully`, 'success'); } catch (error) { showNotification(`Error re-evaluating ${operatorName}: ${error.message}`, 'error'); } finally { hideLoading(); } } // Make function global window.reevaluateOperator = reevaluateOperator; // Utility Functions function showNotification(message, type) { const notification = document.createElement('div'); notification.className = `notification ${type}`; notification.textContent = message; document.body.appendChild(notification); setTimeout(() => { notification.remove(); }, 8000); } let loadingElement = null; function showLoading(message) { loadingElement = document.createElement('div'); loadingElement.className = 'loading-overlay'; loadingElement.innerHTML = `
    ${message}
    `; document.body.appendChild(loadingElement); } function hideLoading() { if (loadingElement) { loadingElement.remove(); loadingElement = null; } } // BRAIN Login Modal Functions are now handled by brain.js // The modal and authentication functions are accessed via window.brainAPI