plz fix my code the plot doesnt load and there is no err msg, all comm with the llm are ok, we get the emb, we get the summary and get the axis lables and chat works, issue not with llm issu is on our draw plot:
# main.py
import os
from flask import Flask, request, jsonify, render_template_string, Response
from llm import get_related_phrases, get_embedding, generate_axis_names, analyze_data, chat_response
import json
import numpy as np
from sklearn.decomposition import PCA

app = Flask(__name__)

def generateSummaryPrompt(data):
    """
    Generate a summary prompt based on the plot data.
    """
    prompt = f"""Analyze the following 3D plot data and provide insightful observations:

Set 1 ({data['string1']} X {data['string2']}):
"""
    for i, label in enumerate(data['labels1']):
        prompt += f"Point {i+1}: ({data['x1'][i]:.4f}, {data['y1'][i]:.4f}, {data['z1'][i]:.4f}) - {label}\n"

    prompt += f"\nSet 2 ({data['string3']} X {data['string4']}):\n"
    for i, label in enumerate(data['labels2']):
        prompt += f"Point {i+1}: ({data['x2'][i]:.4f}, {data['y2'][i]:.4f}, {data['z2'][i]:.4f}) - {label}\n"

    prompt += """\nProvide a comprehensive summary including any interesting points, overlapping groups, or geometric insights derived from the data. For example, identify words from Set 1 that overlap with Set 2, new concepts discovered through intersections, or any 3D geometric patterns and their implications."""
    return prompt

@app.route('/')
def index():
    """
    Serve the HTML page for the application.
    """
    html = """
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Interactive 3D Vector Visualizer</title>
        <style>
            body {
                font-family: Arial, sans-serif;
                background-color: #121212; /* Dark background */
                color: #ffffff; /* White text for contrast */
                margin: 0;
                padding: 0;
                display: flex;
                height: 100vh;
                overflow: hidden;
            }
            /* Container for resizable panels */
            .container {
                display: flex;
                width: 100%;
                height: 100%;
            }
            /* Sidebar on the Left */
            .sidebar {
                width: 300px;
                background-color: #1e1e1e;
                padding: 20px;
                box-sizing: border-box;
                overflow-y: auto;
                resize: horizontal;
                min-width: 200px;
                border-right: 2px solid #333333;
            }
            .sidebar h1 {
                color: #aecfff; /* Pastel Aqua Blue */
                text-align: center;
                margin-bottom: 20px;
            }
            .input-box {
                margin-bottom: 15px;
            }
            .input-box label {
                display: block;
                margin-bottom: 5px;
                color: #aecfff; /* Pastel Aqua Blue */
                font-weight: bold;
                text-align: left; /* Align labels to the left */
            }
            .input-box input {
                width: 100%;
                padding: 8px;
                border: none;
                border-radius: 4px;
                font-size: 1em;
                background-color: #2e2e2e;
                color: #ffffff;
            }
            .controls {
                display: flex;
                justify-content: space-between;
                margin-bottom: 15px;
            }
            .num-points {
                display: flex;
                flex-direction: column;
                width: 48%;
            }
            .num-points label {
                color: #aecfff; /* Pastel Aqua Blue */
                font-weight: bold;
                margin-bottom: 5px;
                text-align: left; /* Align labels to the left */
            }
            .num-points .input-container {
                display: flex;
                align-items: center;
                justify-content: flex-start;
                margin-bottom: 5px; /* Add margin for spacing */
            }
            .num-points button {
                padding: 8px;
                border: none;
                border-radius: 4px;
                font-size: 1em;
                cursor: pointer;
                background-color: #aecfff; /* Pastel Aqua Blue */
                color: #000000;
                margin-right: 5px;
            }
            .num-points button:hover {
                background-color: #8fbfd6; /* Darker Pastel Aqua Blue */
            }
            .num-points input {
                width: 60px;
                text-align: center;
                padding: 8px;
                border: none;
                border-radius: 4px;
                background-color: #2e2e2e;
                color: #ffffff;
                font-size: 1em;
                margin-right: 5px;
            }
            .plot-options label {
                display: block;
                margin-bottom: 5px;
                color: #aecfff; /* Pastel Aqua Blue */
                font-weight: bold;
            }
            .plot-options select {
                width: 100%;
                padding: 8px;
                border: none;
                border-radius: 4px;
                background-color: #2e2e2e;
                color: #ffffff;
                font-size: 1em;
                cursor: pointer;
            }
            /* Buttons */
            .submit-button, .fullscreen-button {
                width: 100%;
                padding: 10px;
                margin-top: 10px;
                background-color: #aecfff; /* Pastel Aqua Blue */
                border: none;
                border-radius: 4px;
                cursor: pointer;
                color: #000000;
                font-size: 1em;
                transition: background-color 0.3s;
                font-weight: bold;
            }
            .submit-button:hover, .fullscreen-button:hover {
                background-color: #8fbfd6; /* Darker Pastel Aqua Blue */
            }
            /* Log Area */
            .log-area {
                background-color: #1e1e1e;
                padding: 10px;
                border-radius: 4px;
                margin-top: 20px;
                max-height: 200px;
                overflow-y: auto;
            }
            .log-area h2 {
                color: #aecfff; /* Pastel Aqua Blue */
                text-align: center;
                margin-top: 0;
            }
            .log-area p {
                margin: 5px 0;
                color: #ffffff;
                font-size: 0.9em;
            }
            /* Resizer between sidebar and main-content */
            .resizer1 {
                width: 5px;
                background-color: #333333;
                cursor: ew-resize;
            }
            /* Main Content in the Center */
            .main-content {
                flex-grow: 1;
                display: flex;
                flex-direction: column;
                align-items: center;
                justify-content: center;
                position: relative;
                background-color: #000000;
            }
            /* Top Bar with Action Buttons and Plot Type */
            .top-bar {
                width: 100%;
                background-color: #1e1e1e;
                padding: 10px;
                box-sizing: border-box;
                display: flex;
                justify-content: flex-start;
                gap: 10px;
                position: absolute;
                top: 0;
                left: 0;
            }
            .top-bar button {
                padding: 8px 12px;
                background-color: #aecfff; /* Pastel Aqua Blue */
                border: none;
                border-radius: 4px;
                cursor: pointer;
                color: #000000;
                font-size: 1em;
                transition: background-color 0.3s;
                display: flex;
                align-items: center;
                justify-content: center;
            }
            .top-bar button:hover {
                background-color: #8fbfd6; /* Darker Pastel Aqua Blue */
            }
            .top-bar .plot-type-container {
                margin-left: auto;
                display: flex;
                align-items: center;
                gap: 5px;
            }
            .top-bar .plot-type-container label {
                color: #aecfff; /* Pastel Aqua Blue */
                font-weight: bold;
            }
            .top-bar .plot-type-container select {
                padding: 8px;
                border: none;
                border-radius: 4px;
                background-color: #2e2e2e;
                color: #ffffff;
                font-size: 1em;
                cursor: pointer;
            }
            /* Plot Area */
            #result {
                width: 80%;
                height: 80%;
                margin-top: 60px; /* To prevent overlap with top bar */
                background-color: #121212;
                border-radius: 8px;
                box-shadow: 0 0 10px #aecfff;
            }
            /* Resizer between main-content and right-panel */
            .resizer2 {
                width: 5px;
                background-color: #333333;
                cursor: ew-resize;
            }
            /* Right Panel with Legend and Chat */
            .right-panel {
                width: 300px;
                background-color: #1e1e1e;
                padding: 20px;
                box-sizing: border-box;
                overflow-y: auto;
                resize: horizontal;
                min-width: 200px;
                display: flex;
                flex-direction: column;
                gap: 20px;
                border-left: 2px solid #333333;
            }
            /* Tabs */
            .tabs {
                display: flex;
                border-bottom: 1px solid #555555;
            }
            .tab {
                flex: 1;
                padding: 10px 0;
                cursor: pointer;
                background-color: #1e1e1e;
                border: none;
                outline: none;
                transition: background-color 0.3s;
                color: #ffffff;
                font-size: 1em;
                text-align: center;
            }
            .tab:hover, .tab.active {
                background-color: #333333;
                color: #aecfff; /* Pastel Aqua Blue */
            }
            .tab-content {
                display: none;
                flex-grow: 1;
                overflow-y: auto;
            }
            .tab-content.active {
                display: block;
            }
            /* Legend Styling */
            #legend {
                width: 100%;
                background-color: #1e1e1e;
                padding: 10px;
                box-sizing: border-box;
                overflow-y: auto;
                border-radius: 8px;
            }
            /* Dark scrollbar */
            #legend::-webkit-scrollbar {
                width: 8px;
            }
            #legend::-webkit-scrollbar-track {
                background: #1e1e1e; 
                border-radius: 4px;
            }
            #legend::-webkit-scrollbar-thumb {
                background: #555555; 
                border-radius: 4px;
            }
            #legend::-webkit-scrollbar-thumb:hover {
                background: #777777; 
            }
            #legend-list {
                list-style: none; /* Remove bullets */
                padding: 0;
                margin: 0;
            }
            #legend-list li {
                margin-bottom: 5px;
                cursor: pointer;
                padding: 5px;
                border-radius: 4px;
                transition: background-color 0.3s;
                color: #ffffff;
            }
            #legend-list li:hover {
                background-color: #333333;
            }
            .collapsible {
                background-color: #1e1e1e;
                color: #ffffff;
                cursor: pointer;
                padding: 10px;
                width: 100%;
                border: none;
                text-align: left;
                outline: none;
                font-size: 16px;
                border-radius: 4px;
                transition: background-color 0.3s;
            }
            .active, .collapsible:hover {
                background-color: #333333;
            }
            .content {
                padding-left: 15px;
                display: none;
                overflow: hidden;
                background-color: #242424;
                border-radius: 4px;
            }
            .content li {
                padding: 5px;
                margin: 3px 0;
                border-radius: 4px;
                transition: background-color 0.3s;
                color: #dddddd;
                font-size: 14px;
            }
            .content li:hover {
                background-color: #555555;
                color: #ffffff;
            }
            /* Chat Styles */
            .chat-container {
                display: flex;
                flex-direction: column;
                height: 100%;
            }
            .chat-summary {
                background-color: #2e2e2e;
                padding: 10px;
                border-radius: 4px;
                margin-bottom: 10px;
                color: #aecfff; /* Pastel Aqua Blue */
                text-align: left;
            }
            .chat-messages {
                flex-grow: 1;
                background-color: #242424;
                padding: 10px;
                border-radius: 4px;
                overflow-y: auto;
                margin-bottom: 10px;
            }
            .chat-input {
                display: flex;
                align-items: center;
                width: 100%;
            }
            .chat-input input {
                flex-grow: 1;
                padding: 8px;
                border: none;
                border-radius: 4px 0 0 4px;
                font-size: 1em;
                background-color: #2e2e2e;
                color: #ffffff;
            }
            .chat-input button {
                padding: 8px;
                background-color: #aecfff; /* Pastel Aqua Blue */
                border: none;
                border-radius: 0 4px 4px 0;
                cursor: pointer;
                color: white;
                display: flex;
                align-items: center;
                justify-content: center;
            }
            .chat-input button:hover {
                background-color: #8fbfd6; /* Darker Pastel Aqua Blue */
            }
            /* Spinner Styles */
            .spinner-overlay {
                position: fixed;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                background: rgba(0, 0, 0, 0.5);
                display: flex;
                align-items: center;
                justify-content: center;
                z-index: 1000;
                display: none; /* Hidden by default */
                flex-direction: column;
                color: #aecfff;
                font-size: 1.2em;
            }
            .spinner {
                border: 8px solid #f3f3f3;
                border-top: 8px solid #aecfff; /* Pastel Aqua Blue */
                border-radius: 50%;
                width: 60px;
                height: 60px;
                animation: spin 1s linear infinite;
                margin-bottom: 20px;
            }
            @keyframes spin {
                0% { transform: rotate(0deg); }
                100% { transform: rotate(360deg); }
            }
            /* Modal Styles */
            .modal {
                display: none; /* Hidden by default */
                position: fixed; /* Stay in place */
                z-index: 1; /* Sit on top */
                padding-top: 60px; /* Location of the box */
                left: 0;
                top: 0;
                width: 100%; /* Full width */
                height: 100%; /* Full height */
                overflow: auto; /* Enable scroll if needed */
                background-color: rgba(0,0,0,0.9); /* Black w/ opacity */
            }
            .close-button {
                position: absolute;
                top: 20px;
                right: 35px;
                color: #ffffff;
                font-size: 40px;
                font-weight: bold;
                transition: color 0.3s;
                cursor: pointer;
            }
            .close-button:hover,
            .close-button:focus {
                color: #aecfff; /* Pastel Aqua Blue */
                text-decoration: none;
                cursor: pointer;
            }
            .modal-content {
                margin: auto;
                display: block;
                width: 80%;
                max-width: 700px;
            }
            @media only screen and (max-width: 700px){
                .modal-content {
                    width: 100%;
                }
            }
        </style>
        <!-- Include Plotly.js -->
        <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
        <!-- Include FontAwesome for icons -->
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    </head>
    <body>
        <!-- Loading Spinner with Status -->
        <div class="spinner-overlay" id="spinnerOverlay">
            <div class="spinner"></div>
            <div id="loadingStatus">Initializing...</div>
        </div>
        <div class="container">
            <!-- Sidebar on the Left -->
            <div class="sidebar">
                <h1>3D Vector Inputs</h1>
                <div class="inputs">
                    <div class="input-box">
                        <label for="string1">Input String 1:</label>
                        <input type="text" id="string1" name="string1" placeholder="e.g., financial river bank institute">
                    </div>
                    <div class="input-box">
                        <label for="string2">Input String 2:</label>
                        <input type="text" id="string2" name="string2" placeholder="e.g., river bank place by the river">
                    </div>
                    <div class="input-box">
                        <label for="string3">Input String 3:</label>
                        <input type="text" id="string3" name="string3" placeholder="e.g., sustainability initiatives">
                    </div>
                    <div class="input-box">
                        <label for="string4">Input String 4:</label>
                        <input type="text" id="string4" name="string4" placeholder="e.g., community outreach programs">
                    </div>
                    <div class="controls">
                        <div class="num-points">
                            <label for="numPoints">Data Points</label>
                            <div class="input-container">
                                <button onclick="decrementPoints()" title="Decrease number of data points">-</button>
                                <input type="number" id="numPoints" name="numPoints" value="10" min="10" max="1000" step="10" title="Number of data points">
                                <button onclick="incrementPoints()" title="Increase number of data points">+</button>
                            </div>
                        </div>
                    </div>
                    <div class="plot-options">
                        <label for="plotType">Plot Type:</label>
                        <select id="plotType">
                            <option value="markers">Markers</option>
                            <option value="lines">Lines</option>
                            <option value="markers+lines">Markers + Lines</option>
                        </select>
                    </div>
                    <button class="submit-button" onclick="submitStrings()" title="Generate and visualize data points">Submit</button>
                    <button class="fullscreen-button" onclick="openModal()" title="View plot in full screen">View Full Screen</button>
                </div>
                <div class="log-area" id="logArea">
                    <h2>Logs</h2>
                    <div id="logs"></div>
                </div>
            </div>
            
            <div class="resizer1"></div>
            
            <!-- Main Content in the Center -->
            <div class="main-content">
                <div class="top-bar">
                    <button onclick="rotateLeft()" title="Rotate left"><i class="fas fa-rotate-left"></i></button>
                    <button onclick="rotateRight()" title="Rotate right"><i class="fas fa-rotate-right"></i></button>
                    <button onclick="zoomIn()" title="Zoom in"><i class="fas fa-search-plus"></i></button>
                    <button onclick="zoomOut()" title="Zoom out"><i class="fas fa-search-minus"></i></button>
                    <button onclick="panUp()" title="Pan up"><i class="fas fa-arrow-up"></i></button>
                    <button onclick="panDown()" title="Pan down"><i class="fas fa-arrow-down"></i></button>
                    <button onclick="changeView()" title="Change view perspective"><i class="fas fa-sync-alt"></i></button>
                    <div class="plot-type-container">
                        <label for="topPlotType">Plot Type:</label>
                        <select id="topPlotType" onchange="changePlotType()">
                            <option value="markers">Markers</option>
                            <option value="lines">Lines</option>
                            <option value="markers+lines">Markers + Lines</option>
                        </select>
                    </div>
                </div>
                <div id="result"></div>
            </div>
            
            <div class="resizer2"></div>
            
            <!-- Right Panel with Legend and Chat -->
            <div class="right-panel">
                <div class="tabs">
                    <button class="tab active" onclick="openTab(event, 'Legend')">Legend</button>
                    <button class="tab" onclick="openTab(event, 'I/O')">I/O</button>
                </div>
                <div id="Legend" class="tab-content active">
                    <div id="legend">
                        <ul id="legend-list"></ul>
                    </div>
                </div>
                <div id="I/O" class="tab-content">
                    <div class="chat-container">
                        <div class="chat-summary" id="chatSummary">
                            <!-- Summary will appear here -->
                        </div>
                        <div class="chat-messages" id="chatMessages">
                            <!-- Chat messages will appear here -->
                        </div>
                        <div class="chat-input">
                            <input type="text" id="chatInput" placeholder="Type your question here...">
                            <button onclick="sendChat()" title="Send"><i class="fas fa-paper-plane"></i></button>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        
        <!-- Modal Overlay for Full Screen View -->
        <div id="myModal" class="modal">
            <span class="close-button" onclick="closeModal()">&times;</span>
            <div class="modal-content">
                <div id="fullResult"></div>
            </div>
        </div>
        
        <script>
            let originalData = null;
            let currentView = 0; // 0: Original, 1: Swap X and Y, 2: Swap X and Z

            // Show spinner with status
            function showSpinner(status) {
                const spinnerOverlay = document.getElementById('spinnerOverlay');
                const loadingStatus = document.getElementById('loadingStatus');
                spinnerOverlay.style.display = 'flex';
                loadingStatus.textContent = status || 'Loading...';
            }

            // Update spinner status
            function updateSpinnerStatus(status) {
                const loadingStatus = document.getElementById('loadingStatus');
                loadingStatus.textContent = status;
            }

            // Hide spinner
            function hideSpinner() {
                document.getElementById('spinnerOverlay').style.display = 'none';
            }

            function appendLog(message) {
                const logDiv = document.getElementById('logs');
                const p = document.createElement('p');
                p.textContent = message;
                logDiv.appendChild(p);
                logDiv.scrollTop = logDiv.scrollHeight;
            }

            function appendLogs(logs) {
                logs.forEach(log => appendLog(log));
            }

            function clearLogs() {
                document.getElementById('logs').innerHTML = '';
            }

            function decrementPoints() {
                const numPointsInput = document.getElementById('numPoints');
                let currentValue = parseInt(numPointsInput.value);
                if (currentValue > 10) { // Allow min 10
                    numPointsInput.value = currentValue - 10;
                }
            }

            function incrementPoints() {
                const numPointsInput = document.getElementById('numPoints');
                let currentValue = parseInt(numPointsInput.value);
                if (currentValue < 1000) { // Step set to 10
                    numPointsInput.value = currentValue + 10;
                }
            }

            async function submitStrings() {
                const string1 = document.getElementById('string1').value.trim();
                const string2 = document.getElementById('string2').value.trim();
                const string3 = document.getElementById('string3').value.trim();
                const string4 = document.getElementById('string4').value.trim();
                const numPoints = parseInt(document.getElementById('numPoints').value);
                const plotType = document.getElementById('topPlotType').value;

                if (!string1 || !string2 || !string3 || !string4) {
                    alert('Please provide all four input strings.');
                    return;
                }

                if (isNaN(numPoints) || numPoints < 10 || numPoints > 1000) {
                    alert('Please enter a valid number of data points between 10 and 1000.');
                    return;
                }

                showSpinner('Sending embeddings to server...');
                clearLogs();
                document.getElementById('chatSummary').innerHTML = '';

                try {
                    const response = await fetch('/compute', {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify({ 
                            string1, 
                            string2, 
                            string3, 
                            string4, 
                            num_points: numPoints
                        })
                    });

                    if (!response.body) {
                        appendLog('Error: Server did not provide a response body.');
                        hideSpinner();
                        return;
                    }

                    const reader = response.body.getReader();
                    const decoder = new TextDecoder();
                    let buffer = '';
                    let stage = 'Sending embeddings to server...';
                    updateSpinnerStatus(stage);

                    while (true) {
                        const { done, value } = await reader.read();
                        if (done) break;
                        buffer += decoder.decode(value, { stream: true });
                        let boundary = buffer.indexOf('\\n');
                        while (boundary !== -1) {
                            const line = buffer.substring(0, boundary);
                            buffer = buffer.substring(boundary + 1);
                            if (line.startsWith('data: ')) {
                                const jsonData = line.replace('data: ', '');
                                if (!jsonData) continue;
                                try {
                                    const parsed = JSON.parse(jsonData);
                                    if(parsed.error){
                                        appendLog(`Error: ${parsed.error}`);
                                        hideSpinner();
                                        return;
                                    }

                                    if(parsed.logs){
                                        appendLogs(parsed.logs);
                                        // Update spinner status based on logs
                                        parsed.logs.forEach(log => {
                                            if (log.includes('Retrieving related phrases for Set 1')) {
                                                updateSpinnerStatus('Retrieving related phrases for Set 1...');
                                            } else if (log.includes('Retrieving embeddings for Set 1')) {
                                                updateSpinnerStatus('Retrieving embeddings for Set 1...');
                                            } else if (log.includes('Retrieving related phrases for Set 2')) {
                                                updateSpinnerStatus('Retrieving related phrases for Set 2...');
                                            } else if (log.includes('Retrieving embeddings for Set 2')) {
                                                updateSpinnerStatus('Retrieving embeddings for Set 2...');
                                            } else if (log.includes('Performing PCA')) {
                                                updateSpinnerStatus('Performing PCA...');
                                            } else if (log.includes('PCA completed')) {
                                                updateSpinnerStatus('PCA completed. Generating summary...');
                                            } else if (log.includes('Generating summary')) {
                                                updateSpinnerStatus('Generating summary...');
                                            }
                                        });
                                    }

                                    if(parsed.summary){
                                        document.getElementById('chatSummary').innerHTML = `<strong>Summary:</strong> ${parsed.summary}`;
                                    }

                                    if(parsed.data){
                                        originalData = parsed.data;
                                        renderPlot(parsed.data, 'result', plotType);
                                        renderPlot(parsed.data, 'fullResult', plotType);
                                        populateLegend(parsed.data, 'legend-list');
                                        hideSpinner();
                                    }
                                } catch (e) {
                                    appendLog('Error parsing server response.');
                                    console.error('Parsing Error:', e);
                                }
                            }
                            boundary = buffer.indexOf('\\n');
                        }
                    }

                    hideSpinner();
                } catch (error) {
                    console.error('Error:', error);
                    appendLog('An error occurred while processing your request.');
                    hideSpinner();
                }
            }

            function renderPlot(data, elementId, plotType) {
                const hoverTexts1 = data.x1.map((x, i) => `X: ${x.toFixed(4)}<br>Y: ${data.y1[i].toFixed(4)}<br>Z: ${data.z1[i].toFixed(4)}<br>Content: ${data.labels1[i]}`);
                const hoverTexts2 = data.x2.map((x, i) => `X: ${x.toFixed(4)}<br>Y: ${data.y2[i].toFixed(4)}<br>Z: ${data.z2[i].toFixed(4)}<br>Content: ${data.labels2[i]}`);

                const trace1 = {
                    x: data.x1,
                    y: data.y1,
                    z: data.z1,
                    mode: plotType,
                    type: 'scatter3d',
                    marker: {
                        size: 6,
                        color: 'cornflowerblue', /* Pastel Blue */
                        line: {
                            color: 'white',
                            width: 0.5
                        }
                    },
                    line: {
                        color: 'plum', /* Pastel Purple */
                        width: 1
                    },
                    hoverinfo: 'text',
                    text: hoverTexts1,
                    name: `${data.string1} X ${data.string2}`
                };

                const trace2 = {
                    x: data.x2,
                    y: data.y2,
                    z: data.z2,
                    mode: plotType,
                    type: 'scatter3d',
                    marker: {
                        size: 6,
                        color: 'lightcoral', /* Pastel Red */
                        line: {
                            color: 'white',
                            width: 0.5
                        }
                    },
                    line: {
                        color: 'skyblue', /* Pastel Light Blue */
                        width: 1
                    },
                    hoverinfo: 'text',
                    text: hoverTexts2,
                    name: `${data.string3} X ${data.string4}`
                };

                const plotData = [trace1, trace2];

                const layout = {
                    paper_bgcolor: '#121212',
                    plot_bgcolor: '#121212',
                    scene: {
                        xaxis: { color: 'white', title: data.axis_names.x_axis },
                        yaxis: { color: 'white', title: data.axis_names.y_axis },
                        zaxis: { color: 'white', title: data.axis_names.z_axis },
                    },
                    margin: { l: 0, r: 20, b: 0, t: 0 },
                    camera: {
                        eye: data.camera_eye || {x: 1.75, y: 1.75, z: 1.75}
                    }
                };

                Plotly.newPlot(elementId, plotData, layout, {responsive: true, displayModeBar: false});
            }

            function populateLegend(data, legendListId) {
                const legendList = document.getElementById(legendListId);
                legendList.innerHTML = '';

                // Create legend for Set 1
                const set1Title = document.createElement('li');
                set1Title.textContent = `${data.string1} X ${data.string2}`;
                set1Title.classList.add('collapsible');
                set1Title.dataset.setIndex = 0;

                const set1Phrases = document.createElement('ul');
                set1Phrases.classList.add('content');

                data.labels1.forEach((phrase, i) => {
                    if (i >= 100) return; // Limit to 100 phrases
                    const phraseItem = document.createElement('li');
                    phraseItem.textContent = phrase;
                    phraseItem.dataset.setIndex = 0;
                    phraseItem.dataset.phraseIndex = i;
                    phraseItem.addEventListener('mouseover', () => highlightTrace(0, i));
                    phraseItem.addEventListener('mouseout', () => resetTraces());
                    set1Phrases.appendChild(phraseItem);
                });

                set1Title.appendChild(set1Phrases);
                legendList.appendChild(set1Title);

                // Create legend for Set 2
                const set2Title = document.createElement('li');
                set2Title.textContent = `${data.string3} X ${data.string4}`;
                set2Title.classList.add('collapsible');
                set2Title.dataset.setIndex = 1;

                const set2Phrases = document.createElement('ul');
                set2Phrases.classList.add('content');

                data.labels2.forEach((phrase, i) => {
                    if (i >= 100) return; // Limit to 100 phrases
                    const phraseItem = document.createElement('li');
                    phraseItem.textContent = phrase;
                    phraseItem.dataset.setIndex = 1;
                    phraseItem.dataset.phraseIndex = i;
                    phraseItem.addEventListener('mouseover', () => highlightTrace(1, i));
                    phraseItem.addEventListener('mouseout', () => resetTraces());
                    set2Phrases.appendChild(phraseItem);
                });

                set2Title.appendChild(set2Phrases);
                legendList.appendChild(set2Title);

                // Add event listeners to toggle collapse
                const collapsibles = document.getElementsByClassName("collapsible");
                for (let i = 0; i < collapsibles.length; i++) {
                    collapsibles[i].addEventListener("click", function(event) {
                        // Prevent toggling when clicking on child elements
                        if (event.target !== this) return;
                        this.classList.toggle("active");
                        const content = this.querySelector(".content");
                        if (content.style.display === "block") {
                            content.style.display = "none";
                        } else {
                            content.style.display = "block";
                        }
                    });
                }
            }

            async function sendChat() {
                const userInput = document.getElementById('chatInput').value.trim();
                if (!userInput) return;

                appendChatMessage('You', userInput);
                document.getElementById('chatInput').value = '';

                try {
                    const response = await fetch('/chat', {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify({ 
                            message: userInput, 
                            context: originalData
                        })
                    });

                    if (!response.ok) {
                        const errorData = await response.json();
                        appendChatMessage('Error', errorData.error);
                        return;
                    }

                    const result = await response.json();
                    if(result.reply){
                        appendChatMessage('Assistant', result.reply);
                    } else if(result.error){
                        appendChatMessage('Error', result.error);
                    } else {
                        appendChatMessage('Error', 'Unknown error occurred.');
                    }
                } catch (error) {
                    console.error('Chat Error:', error);
                    appendChatMessage('Error', 'An error occurred while processing your request.');
                }
            }

            function appendChatMessage(sender, message) {
                const chatMessages = document.getElementById('chatMessages');
                const msgDiv = document.createElement('div');
                msgDiv.innerHTML = `<strong>${sender}:</strong> ${message}`;
                chatMessages.appendChild(msgDiv);
                chatMessages.scrollTop = chatMessages.scrollHeight;
            }

            // Modal Functions
            function openModal() {
                document.getElementById('myModal').style.display = 'block';
                const fullResult = document.getElementById('fullResult');
                const resultContent = document.getElementById('result').innerHTML;
                fullResult.innerHTML = resultContent;
            }

            function closeModal() {
                document.getElementById('myModal').style.display = 'none';
            }

            // Plot Manipulation Functions with 0.001 Sensitivity
            function rotateLeft() {
                if (!originalData) return;
                const layout = document.getElementById('result')._fullLayout;
                const camera = layout.scene.camera.eye;
                const newX = camera.x - 0.001;
                const newY = camera.y;
                const newZ = camera.z;
                Plotly.relayout('result', {
                    'scene.camera.eye': {x: newX, y: newY, z: newZ}
                });
                // Update camera_eye in originalData
                originalData.camera_eye = {x: newX, y: newY, z: newZ};
            }

            function rotateRight() {
                if (!originalData) return;
                const layout = document.getElementById('result')._fullLayout;
                const camera = layout.scene.camera.eye;
                const newX = camera.x + 0.001;
                const newY = camera.y;
                const newZ = camera.z;
                Plotly.relayout('result', {
                    'scene.camera.eye': {x: newX, y: newY, z: newZ}
                });
                // Update camera_eye in originalData
                originalData.camera_eye = {x: newX, y: newY, z: newZ};
            }

            function zoomIn() {
                if (!originalData) return;
                const layout = document.getElementById('result')._fullLayout;
                const camera = layout.scene.camera.eye;
                const scale = 0.999;
                const newEye = {
                    x: camera.x * scale,
                    y: camera.y * scale,
                    z: camera.z * scale
                };
                Plotly.relayout('result', {
                    'scene.camera.eye': newEye
                });
                // Update camera_eye in originalData
                originalData.camera_eye = newEye;
            }

            function zoomOut() {
                if (!originalData) return;
                const layout = document.getElementById('result')._fullLayout;
                const camera = layout.scene.camera.eye;
                const scale = 1.001;
                const newEye = {
                    x: camera.x * scale,
                    y: camera.y * scale,
                    z: camera.z * scale
                };
                Plotly.relayout('result', {
                    'scene.camera.eye': newEye
                });
                // Update camera_eye in originalData
                originalData.camera_eye = newEye;
            }

            function panUp() {
                if (!originalData) return;
                const layout = document.getElementById('result')._fullLayout;
                const camera = layout.scene.camera.eye;
                const newY = camera.y + 0.001;
                Plotly.relayout('result', {
                    'scene.camera.eye.y': newY
                });
                // Update camera_eye in originalData
                originalData.camera_eye.y = newY;
            }

            function panDown() {
                if (!originalData) return;
                const layout = document.getElementById('result')._fullLayout;
                const camera = layout.scene.camera.eye;
                const newY = camera.y - 0.001;
                Plotly.relayout('result', {
                    'scene.camera.eye.y': newY
                });
                // Update camera_eye in originalData
                originalData.camera_eye.y = newY;
            }

            function changeView() {
                if (!originalData) return;
                const views = [
                    {x: 1.75, y: 1.75, z: 1.75},
                    {x: -1.75, y: 1.75, z: 1.75},
                    {x: 1.75, y: -1.75, z: 1.75},
                    {x: 1.75, y: 1.75, z: -1.75}
                ];
                currentView = (currentView + 1) % views.length;
                const newEye = views[currentView];
                Plotly.relayout('result', {
                    'scene.camera.eye': newEye
                });
                // Update camera_eye in originalData
                originalData.camera_eye = newEye;
            }

            // Handle plotType change from topPlotType
            function changePlotType() {
                if (!originalData) return;
                const selectedPlotType = document.getElementById('topPlotType').value;
                renderPlot(originalData, 'result', selectedPlotType);
                renderPlot(originalData, 'fullResult', selectedPlotType);
            }

            // Handle plotType change from plotOptions
            // Removed since plotType is no longer in the sidebar

            function openTab(event, tabName) {
                const tabContents = document.getElementsByClassName('tab-content');
                for (let content of tabContents) {
                    content.classList.remove('active');
                }
                const tabs = document.getElementsByClassName('tab');
                for (let tab of tabs) {
                    tab.classList.remove('active');
                }
                document.getElementById(tabName).classList.add('active');
                event.currentTarget.classList.add('active');
            }

            function highlightTrace(setIndex, phraseIndex) {
                // Placeholder function to highlight traces
                // Implement as needed
            }

            function resetTraces() {
                // Placeholder function to reset traces
                // Implement as needed
            }

        </script>
    </body>
    </html>
    """
    return render_template_string(html)

@app.route('/compute', methods=['POST'])
def compute_route():
    """
    Handle computation and return the processed data for plotting.
    Expecting four input strings and number of data points.
    Streams logs and summary in real-time.
    """
    data = request.json
    string1 = data.get('string1', '').strip()
    string2 = data.get('string2', '').strip()
    string3 = data.get('string3', '').strip()
    string4 = data.get('string4', '').strip()
    num_points = data.get('num_points', 10)  # Default to 10

    def generate():
        logs = []

        # Validate inputs
        if not string1 or not string2 or not string3 or not string4:
            error_payload = {'error': 'All four input strings must be provided.', 'logs': logs}
            yield f"data: {json.dumps(error_payload)}\\n\\n"
            return

        if not isinstance(num_points, int) or num_points < 10 or num_points > 1000:
            error_payload = {'error': 'num_points must be an integer between 10 and 1000.', 'logs': logs}
            yield f"data: {json.dumps(error_payload)}\\n\\n"
            return

        try:
            # Stage 1: Retrieving related phrases for Set 1
            logs.append('Retrieving related phrases for Set 1...')
            yield f"data: {json.dumps({'logs': logs})}\\n\\n"

            related_phrases1 = get_related_phrases((string1, string2), num_related=num_points, logs=logs)
            if not related_phrases1:
                error_payload = {'error': 'Failed to generate related phrases for Set 1.', 'logs': logs}
                yield f"data: {json.dumps(error_payload)}\\n\\n"
                return
            if len(related_phrases1) > 100:
                related_phrases1 = related_phrases1[:100]
                logs.append('Limited Set 1 phrases to 100 to optimize performance.')
                yield f"data: {json.dumps({'logs': logs})}\\n\\n"

            # Stage 2: Retrieving embeddings for Set 1
            logs.append('Retrieving embeddings for Set 1...')
            yield f"data: {json.dumps({'logs': logs})}\\n\\n"

            embeddings1 = [get_embedding(phrase, model="text-embedding-ada-002", logs=logs) for phrase in related_phrases1]
            labels1 = related_phrases1
            datasets = [{
                'x1': [emb[0] for emb in embeddings1],
                'y1': [emb[1] for emb in embeddings1],
                'z1': [emb[2] for emb in embeddings1],
                'hover_text1': [f"Content: {phrase}" for phrase in labels1],
                'labels1': labels1
            }]
            yield f"data: {json.dumps({'logs': logs})}\\n\\n"

            # Stage 3: Retrieving related phrases for Set 2
            logs.append('Retrieving related phrases for Set 2...')
            yield f"data: {json.dumps({'logs': logs})}\\n\\n"

            related_phrases2 = get_related_phrases((string3, string4), num_related=num_points, logs=logs)
            if not related_phrases2:
                error_payload = {'error': 'Failed to generate related phrases for Set 2.', 'logs': logs}
                yield f"data: {json.dumps(error_payload)}\\n\\n"
                return
            if len(related_phrases2) > 100:
                related_phrases2 = related_phrases2[:100]
                logs.append('Limited Set 2 phrases to 100 to optimize performance.')
                yield f"data: {json.dumps({'logs': logs})}\\n\\n"

            # Stage 4: Retrieving embeddings for Set 2
            logs.append('Retrieving embeddings for Set 2...')
            yield f"data: {json.dumps({'logs': logs})}\\n\\n"

            embeddings2 = [get_embedding(phrase, model="text-embedding-ada-002", logs=logs) for phrase in related_phrases2]
            labels2 = related_phrases2
            datasets.append({
                'x2': [emb[0] for emb in embeddings2],
                'y2': [emb[1] for emb in embeddings2],
                'z2': [emb[2] for emb in embeddings2],
                'hover_text2': [f"Content: {phrase}" for phrase in labels2],
                'labels2': labels2
            })
            yield f"data: {json.dumps({'logs': logs})}\\n\\n"

            # Stage 5: Performing PCA
            logs.append('Performing PCA on combined embeddings...')
            yield f"data: {json.dumps({'logs': logs})}\\n\\n"

            # Combine all embeddings for PCA
            embeddings_set1 = np.vstack([datasets[0]['x1'], datasets[0]['y1'], datasets[0]['z1']]).T
            embeddings_set2 = np.vstack([datasets[1]['x2'], datasets[1]['y2'], datasets[1]['z2']]).T
            all_embeddings = np.vstack([embeddings_set1, embeddings_set2])

            if all_embeddings.shape[1] < 3:
                raise ValueError("Embeddings must have at least three dimensions for PCA.")

            # Perform PCA with fixed 3 components
            pca = PCA(n_components=3)
            reduced_embeddings = pca.fit_transform(all_embeddings)
            logs.append("PCA completed.")
            yield f"data: {json.dumps({'logs': logs})}\\n\\n"

            # Stage 6: Generating axis names
            logs.append('Generating axis names based on PCA components...')
            yield f"data: {json.dumps({'logs': logs})}\\n\\n"

            axis_names = generate_axis_names(
                strings=(string1, string2, string3, string4),
                pca_components=pca.components_,
                logs=logs
            )
            yield f"data: {json.dumps({'logs': logs})}\\n\\n"

            # Assign the reduced embeddings back to datasets
            total_set1 = len(datasets[0]['x1'])
            total_set2 = len(datasets[1]['x2'])

            if reduced_embeddings.shape[0] < (total_set1 + total_set2):
                raise ValueError("PCA reduced embeddings do not match the combined dataset size.")

            datasets[0]['x1'] = reduced_embeddings[:total_set1, 0].tolist()
            datasets[0]['y1'] = reduced_embeddings[:total_set1, 1].tolist()
            datasets[0]['z1'] = reduced_embeddings[:total_set1, 2].tolist()

            datasets[1]['x2'] = reduced_embeddings[total_set1:total_set1 + total_set2, 0].tolist()
            datasets[1]['y2'] = reduced_embeddings[total_set1:total_set1 + total_set2, 1].tolist()
            datasets[1]['z2'] = reduced_embeddings[total_set1:total_set1 + total_set2, 2].tolist()

            # Prepare response data
            response_data = {
                'axis_names': axis_names,
                'x1': datasets[0]['x1'],
                'y1': datasets[0]['y1'],
                'z1': datasets[0]['z1'],
                'hover_text1': datasets[0]['hover_text1'],
                'x2': datasets[1]['x2'],
                'y2': datasets[1]['y2'],
                'z2': datasets[1]['z2'],
                'hover_text2': datasets[1]['hover_text2'],
                'legend': [f"{string1} X {string2}", f"{string3} X {string4}"],
                'labels1': datasets[0]['labels1'],
                'labels2': datasets[1]['labels2'],
                'string1': string1,
                'string2': string2,
                'string3': string3,
                'string4': string4,
                'camera_eye': {  # Store camera position for zoom and rotate
                    'x': 1.75,
                    'y': 1.75,
                    'z': 1.75
                }
            }
            yield f"data: {json.dumps({'logs': logs})}\\n\\n"

            # Stage 7: Analyzing data for summary
            logs.append('Generating summary...')
            yield f"data: {json.dumps({'logs': logs})}\\n\\n"

            prompt = generateSummaryPrompt(response_data)
            summary = analyze_data(prompt, logs)
            yield f"data: {json.dumps({'summary': summary, 'logs': logs})}\\n\\n"

            # Final response
            response_payload = {
                'data': response_data,
                'summary': summary,
                'logs': logs
            }
            yield f"data: {json.dumps({'data': response_payload, 'logs': logs})}\\n\\n"

        except Exception as e:
            logs.append(f"Error during computation: {str(e)}")
            print(f"Error during computation: {str(e)}")  # Debug statement
            error_payload = {'error': f"Error during computation: {str(e)}", 'logs': logs}
            yield f"data: {json.dumps(error_payload)}\\n\\n"

    return Response(generate(), mimetype='text/event-stream')

@app.route('/analyze', methods=['POST'])
def analyze_route():
    """
    Analyze the PCA components and return conclusions.
    """
    data = request.json
    prompt = data.get('prompt', '').strip()
    logs = []

    if not prompt:
        return jsonify({'error': 'No prompt provided.'}), 400

    try:
        analysis = analyze_data(prompt, logs)
        return jsonify({'conclusions': analysis, 'logs': logs})
    except Exception as e:
        logs.append(f"Error generating analysis: {str(e)}")
        print(f"Error generating analysis: {str(e)}")
        return jsonify({'error': 'Failed to generate analysis.', 'logs': logs, 'details': str(e)}), 500

@app.route('/chat', methods=['POST'])
def chat():
    """
    Handle chat messages and return responses from the LLM.
    """
    data = request.json
    user_message = data.get('message', '').strip()
    context = data.get('context', {})
    logs = []

    if not user_message:
        return jsonify({'error': 'No message provided.'}), 400

    try:
        reply = chat_response(user_message, context, logs)
        return jsonify({'reply': reply, 'logs': logs})
    except Exception as e:
        logs.append(f"Error in chat: {str(e)}")
        print(f"Error in chat: {str(e)}")
        return jsonify({'error': f'Failed to generate chat response: {str(e)}', 'logs': logs}), 500

if __name__ == '__main__':
    # Run the Flask app
    app.run(port=5005, debug=True)

output only perfect fixed code