this is working code and below it is not working code but with additional features, can you output a working version of the none working code based on the working code or implement the features in the non working code into the working code?
output only a full code

working code without features:
# main.py
import os
from flask import Flask, request, jsonify, render_template_string
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;
            }
            .input-box input {
                width: 100%;
                padding: 8px;
                border: none;
                border-radius: 4px;
                font-size: 1em;
                background-color: #2e2e2e;
                color: #ffffff;
            }
            .num-points, .plot-options {
                margin-bottom: 15px;
            }
            .num-points label {
                display: block;
                margin-bottom: 5px;
                color: #aecfff; /* Pastel Aqua Blue */
                font-weight: bold;
            }
            .num-points .input-container {
                display: flex;
                align-items: center;
                justify-content: center;
            }
            .num-points button, .plot-options select {
                padding: 8px;
                border: none;
                border-radius: 4px;
                font-size: 1em;
                cursor: pointer;
                background-color: #aecfff; /* Pastel Aqua Blue */
                color: #000000;
            }
            .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: 0 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;
            }
            /* 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 */
            .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 */
            .top-bar {
                width: 100%;
                background-color: #1e1e1e;
                padding: 10px;
                box-sizing: border-box;
                display: flex;
                justify-content: center;
                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 */
            }
            /* 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 */
            }
            .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 */
            }
            /* Resizable Panels */
            .resizer1, .resizer2 {
                width: 5px;
                background-color: #333333;
                cursor: ew-resize;
            }
            /* Ensure Legend and I/O Panel are resizable */
            .right-panel {
                resize: horizontal;
                min-width: 200px;
            }
            /* 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>
    <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="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 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>
            <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

        function showSpinner() {
            // Implement a spinner if needed
        }

        function hideSpinner() {
            // Hide the spinner if implemented
        }

        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('plotType').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();
            clearLogs();

            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.ok) {
                    const errorData = await response.json();
                    appendLog(`Error: ${errorData.error}`);
                    hideSpinner();
                    return;
                }

                const result = await response.json();
                originalData = result.data; // Deep copy not needed as we don't modify it
                appendLogs(result.logs);
                renderPlot(result.data, 'result', plotType); // Render in main view
                renderPlot(result.data, 'fullResult', plotType); // Also render in full screen
                populateLegend(result.data, 'legend-list'); // Populate the legend
                generateSummary(result.data); // Generate and display summary
                hideSpinner();
            } catch (error) {
                console.error('Error:', error);
                appendLog('An error occurred while processing your request.');
                hideSpinner();
            }
        }

        async function generateSummary(data) {
            const summaryDiv = document.getElementById('chatSummary');
            summaryDiv.innerHTML = 'Generating summary...';

            try {
                const response = await fetch('/analyze', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ 
                        prompt: generateSummaryPrompt(data)
                    })
                });

                if (!response.ok) {
                    const errorData = await response.json();
                    summaryDiv.innerHTML = `Error: ${errorData.error}`;
                    return;
                }

                const result = await response.json();
                if(result.conclusions){
                    summaryDiv.innerHTML = `<strong>Summary:</strong> ${result.conclusions}`;
                } else {
                    summaryDiv.innerHTML = 'No summary available.';
                }
            } catch (error) {
                console.error('Error generating summary:', error);
                summaryDiv.innerHTML = 'An error occurred while generating the summary.';
            }
        }

        function generateSummaryPrompt(data) {
            return generateSummaryPrompt(data);
        }

        function renderPlot(data, elementId, plotType) {
            // Define plot modes
            let mode;
            if (plotType === 'markers') {
                mode = 'markers';
            } else if (plotType === 'lines') {
                mode = 'lines';
            } else {
                mode = 'markers+lines';
            }

            const trace1 = {
                x: data.x1,
                y: data.y1,
                z: data.z1,
                mode: mode,
                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: data.hover_text1,
                name: `${data.string1} X ${data.string2}`
            };

            const trace2 = {
                x: data.x2,
                y: data.y2,
                z: data.z2,
                mode: mode,
                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: data.hover_text2,
                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 generateSummary(data) {
            const summaryDiv = document.getElementById('chatSummary');
            summaryDiv.innerHTML = 'Generating summary...';

            try {
                const response = await fetch('/analyze', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ 
                        prompt: generateSummaryPrompt(data)
                    })
                });

                if (!response.ok) {
                    const errorData = await response.json();
                    summaryDiv.innerHTML = `Error: ${errorData.error}`;
                    return;
                }

                const result = await response.json();
                if(result.conclusions){
                    summaryDiv.innerHTML = `<strong>Summary:</strong> ${result.conclusions}`;
                } else {
                    summaryDiv.innerHTML = 'No summary available.';
                }
            } catch (error) {
                console.error('Error generating summary:', error);
                summaryDiv.innerHTML = 'An error occurred while generating the summary.';
            }
        }

        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();
                appendChatMessage('Assistant', result.reply);
            } 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;
        }
    </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 the number of data points.
    """
    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

    # Initialize logs
    logs = []

    # Ensure all four strings are provided
    if not string1 or not string2 or not string3 or not string4:
        return jsonify({'error': 'All four input strings must be provided.', 'logs': logs}), 400

    if not isinstance(num_points, int) or num_points < 10 or num_points > 1000:
        return jsonify({'error': 'num_points must be an integer between 10 and 1000.', 'logs': logs}), 400

    try:
        # Prepare datasets
        datasets = []
        legend_group = ['Set 1', 'Set 2']

        # Set 1: string1 and string2
        related_phrases1 = get_related_phrases((string1, string2), num_related=num_points, logs=logs)
        if not related_phrases1:
            return jsonify({'error': 'Failed to generate related phrases for Set 1.', 'logs': logs}), 500
        if len(related_phrases1) > 100:
            related_phrases1 = related_phrases1[:100]
            logs.append('Limited Set 1 phrases to 100 to optimize performance.')
        embeddings1 = [get_embedding(phrase, model="text-embedding-ada-002", logs=logs) for phrase in related_phrases1]
        labels1 = related_phrases1

        # Validate embeddings1
        for emb in embeddings1:
            if len(emb) < 3:
                raise ValueError("Each embedding must have at least three dimensions.")

        datasets.append({
            '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
        })

        # Set 2: string3 and string4
        related_phrases2 = get_related_phrases((string3, string4), num_related=num_points, logs=logs)
        if not related_phrases2:
            return jsonify({'error': 'Failed to generate related phrases for Set 2.', 'logs': logs}), 500
        if len(related_phrases2) > 100:
            related_phrases2 = related_phrases2[:100]
            logs.append('Limited Set 2 phrases to 100 to optimize performance.')
        embeddings2 = [get_embedding(phrase, model="text-embedding-ada-002", logs=logs) for phrase in related_phrases2]
        labels2 = related_phrases2

        # Validate embeddings2
        for emb in embeddings2:
            if len(emb) < 3:
                raise ValueError("Each embedding must have at least three dimensions.")

        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
        })

        # 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("Performed PCA on combined embeddings.")

        # Generate axis names using LLM
        axis_names = generate_axis_names(
            strings=(string1, string2, string3, string4),
            pca_components=pca.components_,
            logs=logs
        )

        # 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': legend_group,
            '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
            }
        }

        # Generate summary
        summary = analyze_data(generateSummaryPrompt(response_data), logs)

        return jsonify({'data': response_data, 'summary': summary, 'logs': logs})
    except Exception as e:
        logs.append(f"Error during computation: {e}")
        print(f"Error during computation: {e}")  # Debug statement
        return jsonify({'error': f"Error during computation: {e}", 'logs': logs}), 500

@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: {e}")
        print(f"Error generating analysis: {e}")
        return jsonify({'error': 'Failed to generate analysis.', 'logs': logs}), 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: {e}")
        print(f"Error in chat: {e}")
        return jsonify({'error': 'Failed to generate chat response.', 'logs': logs}), 500

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

non working code with features we need in the final version:
# 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)