plz fix and output only code (no need to change anything related to the llm we have it covered on llm.py, just fix the issues below they are not related to the llm api (dont add or touch anything related to llm api calls or models)


    on load err: (index):686 Uncaught SyntaxError: Unexpected token ':' (at (index):686:20) -> try:
    const response = await fetch('/compute', {
    rename label max tokens tfor chat to Tokens and alight to right the label, also reduce width to have margin 5px from data points input (+ img of data ponts input ---5px margin----- minus image of Tokens
    nothing wroks since we have the syntax err cant submit, plz fix.
    make sure the full screen plot works, i tried erlier i nothing loaded, check no bug
    output only perfect working code, nothing else



# 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;
            }
            .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, .max-tokens {
                display: flex;
                flex-direction: column;
                width: 48%;
            }
            .num-points label, .max-tokens label {
                color: #aecfff; /* Pastel Aqua Blue */
                font-weight: bold;
                margin-bottom: 5px;
            }
            .num-points .input-container, .max-tokens .input-container {
                display: flex;
                align-items: center;
                justify-content: flex-start;
            }
            .num-points button, .max-tokens 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, .max-tokens button:hover {
                background-color: #8fbfd6; /* Darker Pastel Aqua Blue */
            }
            .num-points input, .max-tokens 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 */
            .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 */
            }
            .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 */
            }
            .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;
            }
            @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 -->
        <div class="spinner-overlay" id="spinnerOverlay">
            <div class="spinner"></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 class="max-tokens">
                            <label for="maxTokens">Max Tokens for Chat</label>
                            <div class="input-container">
                                <button onclick="decrementMaxTokens()" title="Decrease max tokens">-</button>
                                <input type="number" id="maxTokens" name="maxTokens" value="200" min="100" max="50000" step="100" title="Max tokens for chat">
                                <button onclick="incrementMaxTokens()" title="Increase max tokens">+</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="plotType">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
            function showSpinner() {
                document.getElementById('spinnerOverlay').style.display = 'flex';
            }
        
            // 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;
                }
            }
        
            function decrementMaxTokens() {
                const maxTokensInput = document.getElementById('maxTokens');
                let currentValue = parseInt(maxTokensInput.value);
                if (currentValue > 100) { // Allow min 100
                    maxTokensInput.value = currentValue - 100;
                }
            }
        
            function incrementMaxTokens() {
                const maxTokensInput = document.getElementById('maxTokens');
                let currentValue = parseInt(maxTokensInput.value);
                if (currentValue < 50000) { // Allow max 50,000
                    maxTokensInput.value = currentValue + 100;
                }
            }
        
            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 maxTokens = parseInt(document.getElementById('maxTokens').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;
                }
        
                if (isNaN(maxTokens) || maxTokens < 100 || maxTokens > 50000) {
                    alert('Please enter a valid number of max tokens between 100 and 50000.');
                    return;
                }
        
                showSpinner();
                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,
                            max_tokens: maxTokens
                        })
                    });
        
                    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 = '';
        
                    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);
                                    }
        
                                    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');
                                    }
                                except Exception as 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 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 maxTokens = parseInt(document.getElementById('maxTokens').value);
                    const response = await fetch('/chat', {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify({ 
                            message: userInput, 
                            context: originalData,
                            max_tokens: maxTokens
                        })
                    });
        
                    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;
            }
        
            // 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
            document.getElementById('plotType').addEventListener('change', function() {
                if (!originalData) return;
                const selectedPlotType = this.value;
                document.getElementById('topPlotType').value = selectedPlotType;
                renderPlot(originalData, 'result', selectedPlotType);
                renderPlot(originalData, 'fullResult', selectedPlotType);
            });
        
        </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, number of data points, and max tokens for chat.
    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
    max_tokens = data.get('max_tokens', 200)  # Default to 200, used for chat
    
    def generate():
        logs = []
        
        # Validate inputs
        if not string1 or not string2 or not string3 or not string4:
            yield json.dumps({'error': 'All four input strings must be provided.', 'logs': logs}) + '\n'
            return
        
        if not isinstance(num_points, int) or num_points < 10 or num_points > 1000:
            yield json.dumps({'error': 'num_points must be an integer between 10 and 1000.', 'logs': logs}) + '\n'
            return
        
        try:
            # Set 1
            related_phrases1 = get_related_phrases((string1, string2), num_related=num_points, logs=logs)
            if not related_phrases1:
                yield json.dumps({'error': 'Failed to generate related phrases for Set 1.', 'logs': logs}) + '\n'
                return
            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
            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 json.dumps({'logs': logs}) + '\n'
        
            # Set 2
            related_phrases2 = get_related_phrases((string3, string4), num_related=num_points, logs=logs)
            if not related_phrases2:
                yield json.dumps({'error': 'Failed to generate related phrases for Set 2.', 'logs': logs}) + '\n'
                return
            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
            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 json.dumps({'logs': logs}) + '\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("Performed PCA on combined embeddings.")
            yield json.dumps({'logs': logs}) + '\n'
        
            # Generate axis names
            axis_names = generate_axis_names(
                strings=(string1, string2, string3, string4),
                pca_components=pca.components_,
                logs=logs
            )
            yield json.dumps({'logs': logs}) + '\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 json.dumps({'logs': logs}) + '\n'
        
            # Generate summary with max_tokens=50000
            prompt = generateSummaryPrompt(response_data)
            summary = analyze_data(prompt, logs)
            yield json.dumps({'summary': summary, 'logs': logs}) + '\n'
        
            # Final response
            response_payload = {
                'data': response_data,
                'summary': summary,
                'logs': logs
            }
            yield json.dumps({'data': response_payload, 'logs': logs}) + '\n'
        
        except Exception as e:
            logs.append(f"Error during computation: {e}")
            print(f"Error during computation: {e}")  # Debug statement
            yield json.dumps({'error': f"Error during computation: {e}", 'logs': logs}) + '\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: {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', {})
    max_tokens = data.get('max_tokens', 200)  # Default to 200 if not provided
    logs = []
    
    if not user_message:
        return jsonify({'error': 'No message provided.'}), 400
    
    try:
        reply = chat_response(user_message, context, logs, max_tokens)
        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)