there is a syntax issue in my code with the double qoutes around the html """ its single qoutes by mistakes and it breaks the python code (''') can you fix the syntax plz:
(dont change anything else all should be working, just output fixed syntax code and only code)
import os
from flask import Flask, request, jsonify, render_template_string
from openai import OpenAI
import json
import numpy as np
from sklearn.decomposition import PCA

app = Flask(__name__)

# Initialize OpenAI client (Ensure you have set the OPENAI_API_KEY environment variable)
client = OpenAI()

def get_related_phrases(string_pair, num_related=150, logs=[]):
    """
    Retrieve related phrases based on the input string pair using OpenAI's GPT-4o-mini model.
    """
    string1, string2 = string_pair
    prompt = (
        f"Generate {num_related} unique and relevant phrases that combine the concepts of '{string1}' and '{string2}'. "
        f"Each phrase should be meaningful and contextually appropriate. Provide them as a comma-separated list."
    )
    
    try:
        response = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[
                {"role": "system", "content": "You are a creative assistant that generates relevant phrases based on given concepts."},
                {"role": "user", "content": prompt}
            ],
            max_tokens=3000,
            temperature=0.7,
        )
        
        # Access the content using attribute access
        phrases_text = response.choices[0].message.content
        logs.append(f"Generated phrases for pair ({string1}, {string2}).")
        # Split phrases by commas and strip whitespace
        phrases = [phrase.strip() for phrase in phrases_text.split(',') if phrase.strip()]
        # Ensure exactly num_related phrases
        return phrases[:num_related]
    except Exception as e:
        logs.append(f"Error generating related phrases: {e}")
        print(f"Error generating related phrases: {e}")
        return []

def get_embedding(text, model="text-embedding-ada-002", logs=[]):
    """
    Retrieve the embedding for a given text using OpenAI's API.
    """
    text = text.replace("\n", " ")
    try:
        response = client.embeddings.create(
            input=[text],
            model=model
        )
        embedding = response.data[0].embedding
        logs.append(f"Retrieved embedding of length {len(embedding)} for text: '{text}'.")
        print(f"Retrieved embedding of length {len(embedding)} for text: '{text}'")  # Debug statement
        return embedding
    except Exception as e:
        logs.append(f"Error retrieving embedding for text '{text}': {e}")
        print(f"Error retrieving embedding for text '{text}': {e}")
        return [0]*1536  # Return a zero vector as fallback

def generate_axis_names(strings, pca_components, logs=[]):
    """
    Generate meaningful axis names using OpenAI's GPT-4o-mini based on the input strings and PCA components.
    """
    string1, string2, string3, string4 = strings
    prompt = (
        f"Given the following four concepts: '{string1}', '{string2}', '{string3}', and '{string4}', "
        f"and the PCA components: {pca_components.tolist()}, provide three meaningful and significant axis names for a 3D plot. "
        f"Each axis name should reflect the underlying data structure and the relationship between the concepts. "
        f"Respond exactly in JSON format as follows:\n"
        "{\n"
        '  "x_axis": "Axis Name 1",\n'
        '  "y_axis": "Axis Name 2",\n'
        '  "z_axis": "Axis Name 3"\n'
        "}"
    )
    
    try:
        response = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[
                {"role": "system", "content": "You are an assistant that creates meaningful axis names based on given concepts and PCA components."},
                {"role": "user", "content": prompt}
            ],
            max_tokens=150,
            temperature=0.3,
        )
        
        axis_names = response.choices[0].message.content
        axis_dict = json.loads(axis_names)
        logs.append("Generated axis names using GPT-4o-mini.")
        return axis_dict
    except Exception as e:
        logs.append(f"Error generating axis names: {e}")
        print(f"Error generating axis names: {e}")
        # Fallback axis names
        return {"x_axis": "Axis 1", "y_axis": "Axis 2", "z_axis": "Axis 3"}

@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: #000000; /* Black background */
                color: #ffffff; /* White text for contrast */
                margin: 0;
                padding: 0;
                display: flex;
                height: 100vh;
                overflow: hidden;
            }
            .sidebar {
                width: 300px;
                background-color: #1a1a1a;
                padding: 20px;
                box-sizing: border-box;
                overflow-y: auto;
            }
            .log-area {
                margin-top: 20px;
                background-color: #242424;
                padding: 10px;
                border-radius: 4px;
                height: 200px;
                overflow-y: auto;
                font-size: 0.9em;
            }
            .main-content {
                flex-grow: 1;
                display: flex;
                flex-direction: column;
            }
            .tabs {
                display: flex;
                margin-top: 20px;
                border-bottom: 1px solid #555555;
            }
            .tab {
                padding: 10px 20px;
                cursor: pointer;
                background-color: #1a1a1a;
                border: none;
                outline: none;
                transition: background-color 0.3s;
                color: #ffffff;
                font-size: 1em;
            }
            .tab:hover, .tab.active {
                background-color: #333333;
            }
            .tab-content {
                display: none;
                padding: 20px;
                flex-grow: 1;
                overflow-y: auto;
            }
            .tab-content.active {
                display: block;
            }
            .analysis-content {
                color: #ffffff;
                background-color: #1a1a1a;
                padding: 10px;
                border-radius: 4px;
                height: calc(100% - 60px);
                overflow-y: auto;
            }
            /* Chat Styles */
            .chat-container {
                display: flex;
                flex-direction: column;
                height: 100%;
            }
            .chat-messages {
                flex-grow: 1;
                background-color: #242424;
                padding: 10px;
                border-radius: 4px;
                overflow-y: auto;
            }
            .chat-input {
                display: flex;
                margin-top: 10px;
            }
            .chat-input input {
                flex-grow: 1;
                padding: 8px;
                border: none;
                border-radius: 4px;
                font-size: 1em;
            }
            .chat-input button {
                padding: 8px 16px;
                margin-left: 10px;
                background-color: #00ffff;
                border: none;
                border-radius: 4px;
                cursor: pointer;
                font-weight: bold;
            }
            .chat-input button:hover {
                background-color: #00cccc;
            }
            /* Plot Options */
            .plot-options {
                margin-top: 10px;
                display: flex;
                flex-direction: column;
            }
            .plot-options label {
                margin-bottom: 5px;
                font-size: 1em;
                color: #ffffff;
            }
            .plot-options select {
                padding: 8px;
                border: none;
                border-radius: 4px;
                font-size: 1em;
                background-color: #333333;
                color: #ffffff;
            }
            /* Input Fields */
            .inputs {
                display: flex;
                flex-direction: column;
                gap: 15px;
            }
            .input-box {
                display: flex;
                flex-direction: column;
            }
            .input-box label {
                margin-bottom: 5px;
                font-size: 1em;
                color: #ffffff;
            }
            .input-box input[type="text"] {
                padding: 8px;
                border: none;
                border-radius: 4px;
                font-size: 1em;
                background-color: #333333;
                color: #ffffff;
            }
            .num-points {
                display: flex;
                align-items: center;
                justify-content: center;
                margin-top: 10px;
            }
            .num-points button {
                width: 40px;
                height: 40px;
                background-color: #333333;
                border: none;
                color: #ffffff;
                font-size: 1.5em;
                cursor: pointer;
                border-radius: 4px;
                margin: 0 10px;
            }
            .num-points button:hover {
                background-color: #555555;
            }
            .num-points input[type="number"] {
                width: 80px;
                padding: 8px;
                border: none;
                border-radius: 4px;
                font-size: 1em;
                text-align: center;
                background-color: #1a1a1a;
                color: #ffffff;
            }
            button.submit-button {
                padding: 10px 20px;
                background-color: #00ffff; /* Neon Cyan */
                border: none;
                border-radius: 4px;
                cursor: pointer;
                font-weight: bold;
                margin-top: 20px;
                font-size: 1em;
            }
            button.submit-button:hover {
                background-color: #00cccc;
            }
            button.fullscreen-button {
                padding: 10px 20px;
                background-color: #ff5733; /* Vibrant color */
                border: none;
                border-radius: 4px;
                cursor: pointer;
                font-weight: bold;
                margin-top: 10px;
                font-size: 1em;
            }
            button.fullscreen-button:hover {
                background-color: #ff784e;
            }
            /* Legend Styling */
            #legend {
                width: 250px;
                background-color: #1a1a1a;
                padding: 10px;
                box-sizing: border-box;
                overflow-y: auto;
                margin-left: 20px;
                border-radius: 8px;
            }
            /* Dark scrollbar */
            #legend::-webkit-scrollbar {
                width: 8px;
            }
            #legend::-webkit-scrollbar-track {
                background: #1a1a1a; 
                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: #1a1a1a;
                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;
            }
            /* Modal Overlay Styles */
            .modal {
                display: none; /* Hidden by default */
                position: fixed;
                z-index: 1000;
                left: 0;
                top: 0;
                width: 100%;
                height: 100%;
                overflow: auto;
                background-color: rgba(0,0,0,0.9);
            }
            .modal-content {
                margin: 2% auto;
                width: 1200px;
                height: 1200px;
                background-color: #000000;
                position: relative;
                border-radius: 8px;
                overflow: hidden;
            }
            .close-button {
                position: absolute;
                top: 10px;
                right: 25px;
                color: #ffffff;
                font-size: 35px;
                font-weight: bold;
                cursor: pointer;
                z-index: 1001;
            }
            .close-button:hover,
            .close-button:focus {
                color: #bbb;
                text-decoration: none;
                cursor: pointer;
            }
            #fullResult {
                width: 100%;
                height: 100%;
            }
            /* Loading Spinner Styles */
            .spinner {
                border: 8px solid #f3f3f3; /* Light grey */
                border-top: 8px solid #00ffff; /* Neon Cyan */
                border-radius: 50%;
                width: 60px;
                height: 60px;
                animation: spin 1s linear infinite;
                position: absolute;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%);
                z-index: 1002;
                display: none; /* Hidden by default */
            }
            @keyframes spin {
                0% { transform: translate(-50%, -50%) rotate(0deg); }
                100% { transform: translate(-50%, -50%) rotate(360deg); }
            }
        </style>
        <!-- Include Plotly.js -->
        <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
    </head>
    <body>
    <div class="spinner" id="loadingSpinner"></div>
    <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">
                <button onclick="decrementPoints()" title="Decrease number of data points">-</button>
                <input type="number" id="numPoints" name="numPoints" value="150" min="2" max="1000" title="Number of data points">
                <button onclick="incrementPoints()" title="Increase number of data points">+</button>
                <label for="numPoints">Data Points</label>
            </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="main-content">
        <div class="tabs">
            <button class="tab active" onclick="openTab(event, 'Legend')">Legend</button>
            <button class="tab" onclick="openTab(event, 'Analysis')">Analysis</button>
            <button class="tab" onclick="openTab(event, 'I/O')">I/O</button>
        </div>
        <div id="Legend" class="tab-content active">
            <div id="result-container">
                <div id="result"></div>
                <div id="legend">
                    <ul id="legend-list"></ul>
                </div>
            </div>
        </div>
        <div id="Analysis" class="tab-content">
            <div class="analysis-content" id="analysisContent">
                <!-- Analysis results will be inserted here -->
            </div>
        </div>
        <div id="I/O" class="tab-content">
            <div class="chat-container">
                <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">Send</button>
                </div>
            </div>
        </div>
    </div>
    
    <!-- Directional Control Panel -->
    <div class="control-panel">
        <button class="control-button" onclick="rotateLeft()" title="Rotate left">
            <img src="https://img.icons8.com/ios-filled/50/ffffff/left.png" alt="Left">
        </button>
        <button class="control-button" onclick="rotateRight()" title="Rotate right">
            <img src="https://img.icons8.com/ios-filled/50/ffffff/right.png" alt="Right">
        </button>
        <button class="control-button" onclick="zoomIn()" title="Zoom in">
            <img src="https://img.icons8.com/ios-filled/50/ffffff/zoom-in.png" alt="Zoom In">
        </button>
        <button class="control-button" onclick="zoomOut()" title="Zoom out">
            <img src="https://img.icons8.com/ios-filled/50/ffffff/zoom-out.png" alt="Zoom Out">
        </button>
        <button class="control-button" onclick="panUp()" title="Pan up">
            <img src="https://img.icons8.com/ios-filled/50/ffffff/up-left.png" alt="Pan Up">
        </button>
        <button class="control-button" onclick="panDown()" title="Pan down">
            <img src="https://img.icons8.com/ios-filled/50/ffffff/down-left.png" alt="Pan Down">
        </button>
        <button class="control-button" onclick="changeView()" title="Change view perspective">
            <img src="https://img.icons8.com/ios-filled/50/ffffff/repeat.png" alt="Change View">
        </button>
    </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() {
            document.getElementById('loadingSpinner').style.display = 'block';
        }

        function hideSpinner() {
            document.getElementById('loadingSpinner').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 > 100) { // Adjusted step to 100
                numPointsInput.value = currentValue - 100;
            } else if (currentValue > 2) {
                numPointsInput.value = 2;
            }
        }

        function incrementPoints() {
            const numPointsInput = document.getElementById('numPoints');
            let currentValue = parseInt(numPointsInput.value);
            if (currentValue < 900) { // Adjusted step to 100
                numPointsInput.value = currentValue + 100;
            } else if (currentValue < 1000) {
                numPointsInput.value = 1000;
            }
        }

        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 < 2 || numPoints > 1000) {
                alert('Please enter a valid number of data points between 2 and 1000.');
                return;
            }

            showSpinner();
            clearLogs();
            document.getElementById('analysisContent').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.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');
                analyzeData(result.data);
                hideSpinner();
            } catch (error) {
                console.error('Error:', error);
                appendLog('An error occurred while processing your request.');
                hideSpinner();
            }
        }

        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: 'cyan',
                    line: {
                        color: 'white',
                        width: 0.5
                    }
                },
                line: {
                    color: 'magenta',
                    width: 1
                },
                hoverinfo: 'text',
                text: data.hover_text1,
                name: 'Set 1'
            };

            const trace2 = {
                x: data.x2,
                y: data.y2,
                z: data.z2,
                mode: mode,
                type: 'scatter3d',
                marker: {
                    size: 6,
                    color: 'orange',
                    line: {
                        color: 'white',
                        width: 0.5
                    }
                },
                line: {
                    color: 'blue',
                    width: 1
                },
                hoverinfo: 'text',
                text: data.hover_text2,
                name: 'Set 2'
            };

            const plotData = [trace1, trace2];

            const layout = {
                paper_bgcolor: 'black',
                plot_bgcolor: 'black',
                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 }
            };

            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.legend[0];
            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.legend[1];
            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";
                    }
                });
            }
        }

        function highlightTrace(setIndex, phraseIndex) {
            if (!originalData) return;
            let traceNumber = setIndex; // Set 0 for Set 1, Set 1 for Set 2

            // Define unique colors and sizes
            const baseColor = setIndex === 0 ? 'cyan' : 'orange';
            const highlightColor = 'red';
            const baseSize = 6;
            const highlightSize = 12;

            // Retrieve current plot data
            Plotly.restyle('result', {
                'marker.color': [baseColor, baseColor],
                'marker.size': [baseSize, baseSize],
                'line.color': [setIndex === 0 ? 'magenta' : 'blue', setIndex === 0 ? 'magenta' : 'blue']
            }, [0,1]);

            Plotly.restyle('fullResult', {
                'marker.color': [baseColor, baseColor],
                'marker.size': [baseSize, baseSize],
                'line.color': [setIndex === 0 ? 'magenta' : 'blue', setIndex === 0 ? 'magenta' : 'blue']
            }, [0,1]);

            // Highlight the specific point
            Plotly.restyle('result', {
                'marker.color[0]': setIndex === 0 ? mapColor(originalData.x1, phraseIndex, highlightColor) : mapColor(originalData.x2, phraseIndex, highlightColor),
                'marker.size[0]': setIndex === 0 ? incrementSize(originalData.x1, phraseIndex, highlightSize) : incrementSize(originalData.x2, phraseIndex, highlightSize)
            }, [traceNumber]);

            Plotly.restyle('fullResult', {
                'marker.color[0]': setIndex === 0 ? mapColor(originalData.x1, phraseIndex, highlightColor) : mapColor(originalData.x2, phraseIndex, highlightColor),
                'marker.size[0]': setIndex === 0 ? incrementSize(originalData.x1, phraseIndex, highlightSize) : incrementSize(originalData.x2, phraseIndex, highlightSize)
            }, [traceNumber]);
        }

        function mapColor(embeddings, index, highlightColor) {
            // Function to map a specific point's color to highlight
            const colors = embeddings.map((_, i) => (i === index ? highlightColor : (embeddings === originalData.x1 ? 'cyan' : 'orange')));
            return colors;
        }

        function incrementSize(embeddings, index, highlightSize) {
            // Function to map a specific point's size to highlight
            const sizes = embeddings.map((_, i) => (i === index ? highlightSize : 6));
            return sizes;
        }

        function resetTraces() {
            if (!originalData) return;
            const plotElementIds = ['result', 'fullResult'];
            plotElementIds.forEach(elementId => {
                for (let traceNumber = 0; traceNumber < 2; traceNumber++) {
                    const baseColor = traceNumber === 0 ? 'cyan' : 'orange';
                    const lineColor = traceNumber === 0 ? 'magenta' : 'blue';
                    Plotly.restyle(elementId, {
                        'marker.color': [baseColor, baseColor],
                        'marker.size': [6, 6],
                        'line.color': [lineColor, lineColor]
                    }, [0,1]);
                }
            });
        }

        function openTab(evt, tabName) {
            var i, tabcontent, tablinks;
            tabcontent = document.getElementsByClassName("tab-content");
            for (i = 0; i < tabcontent.length; i++) {
                tabcontent[i].style.display = "none";
            }
            tablinks = document.getElementsByClassName("tab");
            for (i = 0; i < tablinks.length; i++) {
                tablinks[i].classList.remove("active");
            }
            document.getElementById(tabName).style.display = "block";
            evt.currentTarget.classList.add("active");
        }

        function analyzeData(data) {
            if (!data) return;
            // Prepare analysis prompt
            const prompt = `
        Given the following data points from two sets:
        
        Set 1:
        ${data.labels1.map((label, i) => `Point ${i+1}: (${data.x1[i]}, ${data.y1[i]}, ${data.z1[i]}) - ${label}`).join('\n')}
        
        Set 2:
        ${data.labels2.map((label, i) => `Point ${i+1}: (${data.x2[i]}, ${data.y2[i]}, ${data.z2[i]}) - ${label}`).join('\n')}
        
        Analyze the intersections, clusters, and any interesting patterns in the vector space. Provide insights and conclusions based on the distribution of the points.
        
        Respond with the analysis in plain text.
            `;

            fetch('/analyze', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ prompt })
            })
            .then(response => response.json())
            .then(result => {
                if (result.error) {
                    appendLog(`Analysis Error: ${result.error}`);
                    document.getElementById('analysisContent').innerHTML = "<p>Error generating analysis.</p>";
                } else {
                    document.getElementById('analysisContent').innerHTML = `<p>${result.conclusions}</p>`;
                }
            })
            .catch(error => {
                console.error('Analysis Error:', error);
                appendLog('An error occurred during analysis.');
                document.getElementById('analysisContent').innerHTML = "<p>Error generating analysis.</p>";
            });
        }

        // Chat Functionality
        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;
        }
    
    </script>
    </body>
    </html>
    """
    return render_template_string(html)

@app.route('/compute', methods=['POST'])
def compute():
    """
    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', 150)

    # 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 < 2 or num_points > 1000:
        return jsonify({'error': 'num_points must be an integer between 2 and 1000.', 'logs': logs}), 400

    try:
        # Prepare datasets
        datasets = []
        colors = ['cyan', 'orange']  # Colors for the two datasets
        labels_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, logs=logs) for phrase in related_phrases1]
        labels1 = related_phrases1

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

        # Combine all data for PCA
        all_embeddings = np.vstack([
            np.column_stack((ds['x1'], ds['y1'], ds['z1'])) for ds in datasets
        ] + [
            np.column_stack((ds['x2'], ds['y2'], ds['z2'])) for ds in datasets
        ])

        # Perform PCA
        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
        start = 0
        for dataset in datasets:
            end = start + len(dataset['x1'])
            dataset['x1'] = reduced_embeddings[start:end, 0].tolist()
            dataset['y1'] = reduced_embeddings[start:end, 1].tolist()
            dataset['z1'] = reduced_embeddings[start:end, 2].tolist()
            start = end
        for dataset in datasets:
            end = start + len(dataset['x2'])
            dataset['x2'] = reduced_embeddings[start:end, 0].tolist()
            dataset['y2'] = reduced_embeddings[start:end, 1].tolist()
            dataset['z2'] = reduced_embeddings[start:end, 2].tolist()
            start = end

        # 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': labels_group,
            'labels1': datasets[0]['labels1'],
            'labels2': datasets[1]['labels2']
        }

        return jsonify({'data': response_data, 'logs': logs})
    except Exception as e:
        logs.append(f"Error during computation: {e}")
        print(f"Error during computation: {e}")  # Debug statement
        return jsonify({'error': 'An internal error occurred. Please try again later.', 'logs': logs}), 500

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

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

    try:
        response = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[
                {"role": "system", "content": "You are a data analyst that provides insightful analysis based on provided data points."},
                {"role": "user", "content": prompt}
            ],
            max_tokens=500,
            temperature=0.5,
        )

        analysis = response.choices[0].message.content
        logs.append("Generated analysis using GPT-4o-mini.")
        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

    # Prepare context for the chat
    context_text = ""
    if context:
        context_text += "Current Data Points:\n\nSet 1:\n"
        for i, label in enumerate(context.get('labels1', [])[:100]):
            context_text += f"Point {i+1}: ({context['x1'][i]}, {context['y1'][i]}, {context['z1'][i]}) - {label}\n"
        context_text += "\nSet 2:\n"
        for i, label in enumerate(context.get('labels2', [])[:100]):
            context_text += f"Point {i+1}: ({context['x2'][i]}, {context['y2'][i]}, {context['z2'][i]}) - {label}\n"

    prompt = f"{context_text}\nUser Question: {user_message}\nProvide a detailed and insightful answer based on the data above."

    try:
        response = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[
                {"role": "system", "content": "You are an assistant that provides detailed and insightful answers based on provided data."},
                {"role": "user", "content": prompt}
            ],
            max_tokens=500,
            temperature=0.5,
        )

        reply = response.choices[0].message.content
        logs.append("Generated chat response using GPT-4o-mini.")
        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)