diff --git a/.gitea/workflows/datadog-sca.yml b/.gitea/workflows/datadog-sca.yml
new file mode 100644
index 0000000..2c99cb9
--- /dev/null
+++ b/.gitea/workflows/datadog-sca.yml
@@ -0,0 +1,20 @@
+on: [push]
+
+name: Datadog Software Composition Analysis
+
+jobs:
+  software-composition-analysis:
+    runs-on: ubuntu-latest
+    name: Datadog SBOM Generation and Upload
+    steps:
+    - name: Checkout
+      uses: actions/checkout@v3
+    - name: Check imported libraries are secure and compliant
+      id: datadog-software-composition-analysis
+      uses: DataDog/datadog-sca-github-action@main
+      with:
+        dd_api_key: ${{ secrets.DD_API_KEY }}
+        dd_app_key: ${{ secrets.DD_APP_KEY }}
+        dd_service: jarvis
+        dd_env: ci
+        dd_site: us5.datadoghq.com
diff --git a/.gitea/workflows/datadog-static-analysis.yml b/.gitea/workflows/datadog-static-analysis.yml
new file mode 100644
index 0000000..f183ff3
--- /dev/null
+++ b/.gitea/workflows/datadog-static-analysis.yml
@@ -0,0 +1,21 @@
+on: [push]
+
+name: Datadog Static Analysis
+
+jobs:
+  static-analysis:
+    runs-on: ubuntu-latest
+    name: Datadog Static Analyzer
+    steps:
+    - name: Checkout
+      uses: actions/checkout@v3
+    - name: Check code meets quality and security standards
+      id: datadog-static-analysis
+      uses: DataDog/datadog-static-analyzer-github-action@v1
+      with:
+        dd_api_key: ${{ secrets.DD_API_KEY }}
+        dd_app_key: ${{ secrets.DD_APP_KEY }}
+        dd_service: jarvis
+        dd_env: ci
+        dd_site: us5.datadoghq.com
+        cpu_count: 2
diff --git a/.github/workflows/datadog-sca.yml b/.github/workflows/datadog-sca.yml
new file mode 100644
index 0000000..2c99cb9
--- /dev/null
+++ b/.github/workflows/datadog-sca.yml
@@ -0,0 +1,20 @@
+on: [push]
+
+name: Datadog Software Composition Analysis
+
+jobs:
+  software-composition-analysis:
+    runs-on: ubuntu-latest
+    name: Datadog SBOM Generation and Upload
+    steps:
+    - name: Checkout
+      uses: actions/checkout@v3
+    - name: Check imported libraries are secure and compliant
+      id: datadog-software-composition-analysis
+      uses: DataDog/datadog-sca-github-action@main
+      with:
+        dd_api_key: ${{ secrets.DD_API_KEY }}
+        dd_app_key: ${{ secrets.DD_APP_KEY }}
+        dd_service: jarvis
+        dd_env: ci
+        dd_site: us5.datadoghq.com
diff --git a/.github/workflows/datadog-static-analysis.yml b/.github/workflows/datadog-static-analysis.yml
new file mode 100644
index 0000000..f183ff3
--- /dev/null
+++ b/.github/workflows/datadog-static-analysis.yml
@@ -0,0 +1,21 @@
+on: [push]
+
+name: Datadog Static Analysis
+
+jobs:
+  static-analysis:
+    runs-on: ubuntu-latest
+    name: Datadog Static Analyzer
+    steps:
+    - name: Checkout
+      uses: actions/checkout@v3
+    - name: Check code meets quality and security standards
+      id: datadog-static-analysis
+      uses: DataDog/datadog-static-analyzer-github-action@v1
+      with:
+        dd_api_key: ${{ secrets.DD_API_KEY }}
+        dd_app_key: ${{ secrets.DD_APP_KEY }}
+        dd_service: jarvis
+        dd_env: ci
+        dd_site: us5.datadoghq.com
+        cpu_count: 2
diff --git a/.gitignore b/.gitignore
index 80abc65..3dea82b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -174,3 +174,46 @@ cython_debug/
 pyvenv.cfg
 .venv
 pip-selfcheck.json
+
+
+# Logs
+logs
+*.log
+npm-debug.log*
+
+# Runtime data
+pids
+*.pid
+*.seed
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (http://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules
+jspm_packages
+
+# Optional npm cache directory
+.npm
+
+# Optional REPL history
+.node_repl_history
+.next
+
+config.ini
+*.db
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..8a58831
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,20 @@
+# Use an official Python runtime as a parent image
+FROM python:3.9-slim
+
+# Set the working directory in the container
+WORKDIR /app
+
+# Copy the current directory contents into the container at /app
+COPY . /app
+
+# Install any needed packages specified in requirements.txt
+RUN pip install --no-cache-dir -r requirements.txt
+
+# Make port 5001 available to the world outside this container
+EXPOSE 5001
+
+# Define environment variable
+ENV FLASK_APP=main.py
+
+# Run app.py when the container launches
+CMD ["python", "main.py"]
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2887b5f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,5 @@
+# Jarvis
+
+it's actually not that smart!
+
+
diff --git a/client.py b/client.py
new file mode 100644
index 0000000..4613500
--- /dev/null
+++ b/client.py
@@ -0,0 +1,138 @@
+import time
+
+import requests
+
+
+class LLMChatClient:
+    def __init__(self, base_url, api_key):
+        self.base_url = base_url.rstrip("/")
+        self.api_key = api_key
+        self.headers = {"X-API-Key": api_key, "Content-Type": "application/json"}
+
+    def submit_query(self, message):
+        """
+        Submit a query to the LLM Chat Server.
+
+        Args:
+            message (str): The message to send to the server.
+
+        Returns:
+            str: The query ID for the submitted query.
+
+        Raises:
+            requests.RequestException: If the request fails.
+
+        Example:
+            client = LLMChatClient('http://localhost:5001', 'your-api-key')
+            query_id = client.submit_query('What is the capital of France?')
+            print(f"Query ID: {query_id}")
+
+        cURL equivalent:
+            curl -X POST http://localhost:5001/api/v1/query \
+                 -H "Content-Type: application/json" \
+                 -H "X-API-Key: your-api-key" \
+                 -d '{"message": "What is the capital of France?"}'
+        """
+        url = f"{self.base_url}/api/v1/query"
+        data = {"message": message}
+        response = requests.post(url, json=data, headers=self.headers)
+        response.raise_for_status()
+        return response.json()["query_id"]
+
+    def get_query_status(self, query_id):
+        """
+        Get the status of a submitted query.
+
+        Args:
+            query_id (str): The ID of the query to check.
+
+        Returns:
+            dict: A dictionary containing the status and conversation history (if completed).
+
+        Raises:
+            requests.RequestException: If the request fails.
+
+        Example:
+            client = LLMChatClient('http://localhost:5001', 'your-api-key')
+            status = client.get_query_status('query-id-here')
+            print(f"Query status: {status['status']}")
+            if status['status'] == 'completed':
+                print(f"Conversation history: {status['conversation_history']}")
+
+        cURL equivalent:
+            curl -X GET http://localhost:5001/api/v1/query_status/query-id-here \
+                 -H "X-API-Key: your-api-key"
+        """
+        url = f"{self.base_url}/api/v1/query_status/{query_id}"
+        response = requests.get(url, headers=self.headers)
+        response.raise_for_status()
+        return response.json()
+
+    def submit_query_and_wait(self, message, max_wait_time=300, poll_interval=2):
+        """
+        Submit a query and wait for the result.
+
+        Args:
+            message (str): The message to send to the server.
+            max_wait_time (int): Maximum time to wait for the result in seconds.
+            poll_interval (int): Time between status checks in seconds.
+
+        Returns:
+            dict: The completed conversation history.
+
+        Raises:
+            requests.RequestException: If the request fails.
+            TimeoutError: If the query doesn't complete within max_wait_time.
+
+        Example:
+            client = LLMChatClient('http://localhost:5001', 'your-api-key')
+            result = client.submit_query_and_wait('What is the capital of France?')
+            print(f"Conversation history: {result}")
+        """
+        query_id = self.submit_query(message)
+        start_time = time.time()
+
+        while time.time() - start_time < max_wait_time:
+            status = self.get_query_status(query_id)
+            if status["status"] == "completed":
+                return status["conversation_history"]
+            time.sleep(poll_interval)
+
+        raise TimeoutError(f"Query did not complete within {max_wait_time} seconds")
+
+
+class LLMChatAdminClient:
+    def __init__(self, base_url, admin_key):
+        self.base_url = base_url.rstrip("/")
+        self.admin_key = admin_key
+        self.headers = {"X-Admin-Key": admin_key, "Content-Type": "application/json"}
+
+    def generate_api_key(self, username):
+        """
+        Generate a new API key for a user.
+
+        Args:
+            username (str): The username to generate the API key for.
+
+        Returns:
+            dict: A dictionary containing the username and generated API key.
+
+        Raises:
+            requests.RequestException: If the request fails.
+
+        Example:
+            admin_client = LLMChatAdminClient('http://localhost:5001', 'your-admin-key')
+            result = admin_client.generate_api_key('new_user')
+            print(f"Generated API key for {result['username']}: {result['api_key']}")
+
+        cURL equivalent:
+            curl -X POST http://localhost:5001/admin/generate_key \
+                 -H "Content-Type: application/json" \
+                 -H "X-Admin-Key: your-admin-key" \
+                 -d '{"username": "new_user"}'
+        """
+        url = f"{self.base_url}/admin/generate_key"
+        data = {"username": username}
+        response = requests.post(url, json=data, headers=self.headers)
+        response.raise_for_status()
+        return response.json()
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..e11985b
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,16 @@
+version: '3.8'
+
+services:
+  llm-chat-server:
+    build: .
+    ports:
+      - "5001:5001"
+    volumes:
+      - ./llm_chat_server.db:/app/llm_chat_server.db
+      - ./config.ini:/app/config.ini
+    environment:
+      - FLASK_ENV=production
+    restart: unless-stopped
+
+volumes:
+  llm_chat_server_db:
\ No newline at end of file
diff --git a/index.html b/index.html
index 654a579..8ebd44d 100644
--- a/index.html
+++ b/index.html
@@ -9,6 +9,8 @@
     
     
     
+    
+    
     
     
 
 
     
         
+            
             
             
+            
+            
+            
+                
Conversation History
+                
+            
 
 
@@ -280,10 +394,80 @@
         const chatContainer = document.getElementById('chat-container');
         const userInput = document.getElementById('user-input');
         const sendButton = document.getElementById('send-button');
+        const chatTabs = document.getElementById('chat-tabs');
 
-        let thinkingElement = null;
-        let thinkingDetails = null;
-        let thinkingStartTime = null;
+        let currentChatId = null;
+        let chats = {};
+
+        function createNewChat() {
+            const chatId = Date.now().toString();
+            chats[chatId] = {
+                messages: [],
+                thinkingSections: []
+            };
+            addChatTab(chatId);
+            switchToChat(chatId);
+            saveChats();
+        }
+
+        function addChatTab(chatId) {
+            const tab = document.createElement('button');
+            tab.classList.add('chat-tab');
+            tab.textContent = `Chat ${Object.keys(chats).length}`;
+            tab.onclick = () => switchToChat(chatId);
+
+            const closeButton = document.createElement('span');
+            closeButton.classList.add('close-tab');
+            closeButton.textContent = '×';
+            closeButton.onclick = (e) => {
+                e.stopPropagation();
+                closeChat(chatId);
+            };
+
+            tab.appendChild(closeButton);
+            chatTabs.insertBefore(tab, chatTabs.lastElementChild);
+        }
+
+        function switchToChat(chatId) {
+            currentChatId = chatId;
+            document.querySelectorAll('.chat-tab').forEach(tab => tab.classList.remove('active'));
+            document.querySelector(`.chat-tab:nth-child(${Object.keys(chats).indexOf(chatId) + 1})`).classList.add('active');
+            renderChat(chatId);
+        }
+
+        function closeChat(chatId) {
+            delete chats[chatId];
+            saveChats();
+            const tabToRemove = Array.from(chatTabs.children).find(tab => tab.textContent.includes(`Chat ${Object.keys(chats).indexOf(chatId) + 1}`));
+            if (tabToRemove) {
+                chatTabs.removeChild(tabToRemove);
+            }
+            if (currentChatId === chatId) {
+                const remainingChatIds = Object.keys(chats);
+                if (remainingChatIds.length > 0) {
+                    switchToChat(remainingChatIds[0]);
+                } else {
+                    createNewChat();
+                }
+            }
+        }
+
+        function renderChat(chatId) {
+            chatContainer.innerHTML = '';
+            const chat = chats[chatId];
+            chat.messages.forEach(message => addMessage(message.content, message.isUser));
+            chat.thinkingSections.forEach(section => {
+                const thinkingSection = createThinkingSection();
+                section.thoughts.forEach(thought => addThought(thought.type, thought.content, thought.details, thinkingSection));
+            });
+        }
+
+        function createThinkingSection() {
+            const section = document.createElement('div');
+            section.classList.add('thinking-section');
+            chatContainer.appendChild(section);
+            return section;
+        }
 
         function addMessage(message, isUser) {
             const messageElement = document.createElement('div');
@@ -292,65 +476,40 @@
             messageElement.innerHTML = isUser ? message : marked.parse(message);
             chatContainer.appendChild(messageElement);
             chatContainer.scrollTop = chatContainer.scrollHeight;
+
+            if (currentChatId) {
+                chats[currentChatId].messages.push({ content: message, isUser: isUser });
+                saveChats();
+            }
         }
 
-        function startThinking() {
-            thinkingElement = document.createElement('div');
-            thinkingElement.classList.add('thought-summary', 'collapsible');
+        function addThought(type, content, details = '', thinkingSection) {
+            const stepElement = document.createElement('div');
+            stepElement.classList.add('thought-summary', 'collapsible', type);
+            stepElement.textContent = type.charAt(0).toUpperCase() + type.slice(1).replace('_', ' ') + ':';
+            stepElement.onclick = toggleStepDetails;
+
+            const stepDetails = document.createElement('div');
+            stepDetails.classList.add('thought-details');
             
-            const led = document.createElement('div');
-            led.classList.add('led', 'blinking');
-            
-            const textNode = document.createTextNode('Thinking...');
-            
-            thinkingElement.appendChild(led);
-            thinkingElement.appendChild(textNode);
-            thinkingElement.onclick = toggleThinkingDetails;
-            
-            thinkingDetails = document.createElement('div');
-            thinkingDetails.classList.add('thought-details');
-            
-            chatContainer.appendChild(thinkingElement);
-            chatContainer.appendChild(thinkingDetails);
-            
-            thinkingStartTime = Date.now();
+            if (type === 'error') {
+                stepElement.classList.add('error-message');
+                if (content.includes('retrying')) {
+                    stepElement.classList.add('retrying');
+                }
+                stepDetails.innerHTML = marked.parse(content + '\n\nDetails:\n```\n' + details + '\n```');
+            } else {
+                stepDetails.innerHTML = marked.parse(content);
+            }
+
+            thinkingSection.appendChild(stepElement);
+            thinkingSection.appendChild(stepDetails);
             chatContainer.scrollTop = chatContainer.scrollHeight;
-        }
 
-        function addThought(step, content) {
-            if (thinkingDetails) {
-                const stepElement = document.createElement('div');
-                stepElement.classList.add('thought-summary', 'collapsible');
-                stepElement.textContent = step;
-                stepElement.onclick = toggleStepDetails;
-
-                const stepDetails = document.createElement('div');
-                stepDetails.classList.add('thought-details');
-                stepDetails.innerHTML = content;
-
-                thinkingDetails.appendChild(stepElement);
-                thinkingDetails.appendChild(stepDetails);
-                chatContainer.scrollTop = chatContainer.scrollHeight;
-            }
-        }
-
-        function endThinking(thinkingTime) {
-            if (thinkingElement) {
-                const textNode = thinkingElement.childNodes[1];
-                textNode.nodeValue = `Thinking... (${thinkingTime}s)`;
-                const led = thinkingElement.querySelector('.led');
-                led.classList.remove('blinking');
-                led.style.backgroundColor = '#0f0';
-                led.style.boxShadow = '0 0 10px #0f0';
-                thinkingStartTime = null;
-            }
-        }
-
-        function toggleThinkingDetails() {
-            this.classList.toggle('open');
-            const details = this.nextElementSibling;
-            if (details) {
-                details.style.display = details.style.display === 'none' ? 'block' : 'none';
+            if (currentChatId) {
+                const currentThinkingSection = chats[currentChatId].thinkingSections[chats[currentChatId].thinkingSections.length - 1];
+                currentThinkingSection.thoughts.push({ type, content, details });
+                saveChats();
             }
         }
 
@@ -362,34 +521,71 @@
             }
         }
 
-        socket.on('thinking', (data) => {
-            if (!thinkingElement) startThinking();
-            addThought(data.step, 'Started');
-        });
+        function saveChats() {
+            localStorage.setItem('chats', JSON.stringify(chats));
+        }
 
-        socket.on('thought', (data) => {
-            addThought('Result', data.content);
-        });
-
-        socket.on('chat_response', (data) => {
-            endThinking(data.thinking_time);
-            addMessage(data.response, false);
-        });
-
-        socket.on('error', (data) => {
-            endThinking(data.thinking_time);
-            addMessage(`Error: ${data.message}`, false);
-        });
+        function loadChats() {
+            const storedChats = localStorage.getItem('chats');
+            if (storedChats) {
+                chats = JSON.parse(storedChats);
+                Object.keys(chats).forEach(chatId => addChatTab(chatId));
+                if (Object.keys(chats).length > 0) {
+                    switchToChat(Object.keys(chats)[0]);
+                } else {
+                    createNewChat();
+                }
+            } else {
+                createNewChat();
+            }
+        }
 
         function sendMessage() {
             const message = userInput.value.trim();
-            if (message) {
+            if (message && currentChatId) {
                 addMessage(message, true);
-                socket.emit('chat_request', { message: message });
+                chats[currentChatId].thinkingSections.push({ thoughts: [] });
+                socket.emit('chat_request', { 
+                    message: message,
+                    conversation_history: chats[currentChatId].messages.filter(m => !m.isUser).map(m => ({ role: 'assistant', content: m.content }))
+                        .concat(chats[currentChatId].messages.filter(m => m.isUser).map(m => ({ role: 'user', content: m.content })))
+                });
                 userInput.value = '';
             }
         }
 
+        socket.on('thinking', (data) => {
+            if (currentChatId) {
+                const newThinkingSection = createThinkingSection();
+                chats[currentChatId].thinkingSections.push({ thoughts: [] });
+                addThought(data.step, 'Started', '', newThinkingSection);
+            }
+        });
+
+        socket.on('thought', (data) => {
+            if (currentChatId) {
+                const currentThinkingSection = chatContainer.querySelector('.thinking-section:last-child');
+                addThought(data.type, data.content, data.details, currentThinkingSection);
+            }
+        });
+
+        socket.on('chat_response', (data) => {
+            if (currentChatId) {
+                addMessage(data.response, false);
+            }
+        });
+
+        socket.on('error', (data) => {
+            if (currentChatId) {
+                const currentThinkingSection = chatContainer.querySelector('.thinking-section:last-child');
+                if (data.type === 'retrying') {
+                    addThought('error', data.content, '', currentThinkingSection);
+                } else {
+                    addThought('error', data.message, '', currentThinkingSection);
+                }
+            }
+        });
+
         sendButton.addEventListener('click', sendMessage);
         userInput.addEventListener('keypress', function(e) {
             if (e.key === 'Enter' && !e.shiftKey) {
@@ -398,6 +594,16 @@
             }
         });
 
+        // Add new chat button
+        const newChatButton = document.createElement('button');
+        newChatButton.id = 'new-chat-button';
+        newChatButton.textContent = '+ New Chat';
+        newChatButton.onclick = createNewChat;
+        chatTabs.appendChild(newChatButton);
+
+        // Load chats when the page loads
+        loadChats();
+
         const chartOptions = {
             type: 'line',
             options: {
@@ -570,6 +776,41 @@
 
         window.addEventListener('resize', checkWindowSize);
         checkWindowSize(); // Initial check
+
+        // Add this new function to update the conversation history
+        function updateConversationHistory(history) {
+            const conversationHistoryElement = document.getElementById('conversation-history');
+            conversationHistoryElement.innerHTML = '';
+            
+            history.forEach(item => {
+                const card = document.createElement('div');
+                card.classList.add('history-card');
+                
+                const role = document.createElement('div');
+                role.classList.add('history-role');
+                role.textContent = item.role.charAt(0).toUpperCase() + item.role.slice(1);
+                
+                const content = document.createElement('pre');
+                content.classList.add('history-content');
+                content.innerHTML = hljs.highlightAuto(item.content).value;
+                
+                card.appendChild(role);
+                card.appendChild(content);
+                conversationHistoryElement.appendChild(card);
+            });
+        }
+
+        // Add this new socket listener
+        socket.on('conversation_history', (data) => {
+            updateConversationHistory(data.history);
+        });
+
+        // Add event listener for the clear history button
+        clearHistoryButton.addEventListener('click', () => {
+            if (confirm('Are you sure you want to clear the conversation history?')) {
+                clearConversationHistory();
+            }
+        });