diff --git a/.gitignore b/.gitignore index b30cf7c..f6f0715 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,8 @@ templates/style.css .gitignore /python .vscode/settings.json +static/resource/20241126011143_Notification-and-Programme-for-BTech-RIST-Odd-Semester-December-2024.pdf +static/resource/an-old-hope.min.css +/.gitignore +from ollama import chat.py +demo.html diff --git a/index.py b/index.py index 09bd970..296d1f6 100644 --- a/index.py +++ b/index.py @@ -6,52 +6,86 @@ import datetime import threading from queue import Queue - +import uuid con_path = "" -app = Flask(__name__, template_folder='templates') - +date_str = "" conversation = [] clients = [] chat_history = [] clients_lock = threading.Lock() stream_flow = False -date_time = datetime.datetime.now() - -# Save conversation in a new file -def save_conversation(): - global con_path # Use the global variable to store the file path across calls +chat_title = "Starting New chat" +app = Flask(__name__, template_folder='templates') +def getId(): + global date_str + current_time = datetime.datetime.now() + formatted_time = current_time.strftime("%Y%m%d_%H%M%S") + date_str = formatted_time + print(date_str) +getId() +def save_conversation(create_new_file=False): + global con_path, date_str,chat_title # Use the global variables to store the file path and date_str across calls # Define the folder where logs are stored folder_name = 'log_data/conversation_logs' if not os.path.exists(folder_name): os.makedirs(folder_name) - - # Get the current date (without time) to use as part of the filename - date_str = datetime.datetime.now().strftime("%Y-%m-%d-%h-%m") - - # If `con_path` is None or if it doesn't match today's date, create a new file path - if not con_path or date_str not in con_path: + if create_new_file or not con_path or date_str not in con_path: filename = f'conversation_{date_str}.json' con_path = os.path.join(folder_name, filename) + elif not create_new_file and con_path: + pass + if not date_str: + date_str = str(uuid.uuid4()) # Generate a unique date_str for the conversation + + # Prepare the data structure with only the conversation array for subsequent saves + conversation_data = { + "chat_title" : chat_title, + "chat_id": date_str, + "conversation": conversation # Assume `conversation` is a list holding conversation data + } # Save or update the conversation data in the JSON file if os.path.exists(con_path): - # Load existing data and append new data to it + # Load existing data with open(con_path, 'r+') as f: try: existing_data = json.load(f) except json.JSONDecodeError: existing_data = [] - existing_data.extend(conversation) + + # Check if the file already contains the conversation with the same chat_id + existing_chat_ids = [entry["chat_id"] for entry in existing_data] + if date_str in existing_chat_ids: + # If the chat_id already exists, just update the existing conversation array + for entry in existing_data: + if entry["chat_id"] == date_str: + entry["conversation"] = conversation # Overwrite the conversation data + break + else: + # Add new conversation if chat_id doesn't exist + existing_data.append(conversation_data) + + # Save the updated conversation data back to the file f.seek(0) json.dump(existing_data, f, indent=4) + else: # Create new file if it doesn't exist with open(con_path, 'w') as f: - json.dump(conversation, f, indent=4) + json.dump([conversation_data], f, indent=4) # Wrap in a list to store multiple conversations print(f"Conversation saved to {con_path}") +def new_chat(title,create_new_file=False): + print("Request for new chat.") + global conversation, con_path, chat_title + conversation = [] + con_path = "" + chat_title = title + getId() + save_conversation(create_new_file=True) + def client(data): """Send data to all connected clients.""" with clients_lock: @@ -59,10 +93,14 @@ def client(data): client.put(data) # Generate LLM response -def generate_response(prompt, model): - print(prompt, model) +def generate_response(prompt, model,chat_request): + print(prompt, model,chat_request) if prompt == "": return + if chat_request == True: + print("true") + new_chat(prompt[:20]) + user_rep_st = "[|/__USER_START__/|]" client(user_rep_st) client(f"{prompt}") @@ -98,18 +136,17 @@ def generate_response(prompt, model): save_conversation() client(done_marker) - def process(message_chunk): - match message_chunk: - case _ if "\n" in message_chunk or message_chunk == "": - message_chunk = "
" - case _ if "\n\n\n" in message_chunk: - message_chunk = "


" - case _: - pass - + # Replace patterns in the correct order + if "\n\n\n" in message_chunk: # Triple line breaks + message_chunk = message_chunk.replace("\n\n\n", "


") + if "\n\n" in message_chunk: # Double line breaks + message_chunk = message_chunk.replace("\n\n", "

") + if "\n" in message_chunk: # Single line breaks + message_chunk = message_chunk.replace("\n", "
") client(message_chunk) + @app.route('/') def index(): return render_template('index.html') @@ -120,7 +157,8 @@ def get_response(): data = request.get_json() prompt = data.get('prompt') model = data.get('model_need') - response = generate_response(prompt, model) + chat_request = data.get("new_chat") + response = generate_response(prompt, model,chat_request) return jsonify({'response': 'Success'}) @app.route("/get_cmd", methods=["POST"]) @@ -158,6 +196,40 @@ def list_llm(): if request.method == 'POST': list = ollama.list() return jsonify({"list": list}) +@app.route('/get_chats', methods=['POST']) +def get_chats(): + folder_name = 'log_data/conversation_logs' + if not os.path.exists(folder_name): + return jsonify({"cat": "empty", "response": "Start chat"}) + files = os.listdir(folder_name) + + json_files = [file for file in files if file.endswith('.json')] + if not json_files: + return jsonify({"cat": "empty", "response": "No chats found"}) + + chat_titles = [] + + # Iterate through each .json file and extract the chat_title + for json_file in json_files: + file_path = os.path.join(folder_name, json_file) + + try: + with open(file_path, 'r') as f: + data = json.load(f) + + # Check if data is a list (list of chats) + if isinstance(data, list): + for chat in data: + # Ensure 'chat_title' exists in each chat + if "chat_title" in chat: + chat_titles.append(chat["chat_title"]) + except json.JSONDecodeError: + continue + if not chat_titles: + return jsonify({"cat": "empty", "response": "No valid chat titles found"}) + + # Return the list of chat titles + return jsonify({"cat": "success", "response": chat_titles}) if __name__ == '__main__': app.run(debug=True) diff --git a/others/imga.png b/others/imga.png index 0f73e85..6a518cd 100644 Binary files a/others/imga.png and b/others/imga.png differ diff --git a/others/imgb.png b/others/imgb.png index 2b0cf81..4758ff1 100644 Binary files a/others/imgb.png and b/others/imgb.png differ diff --git a/others/imgc.png b/others/imgc.png index e80d08c..931ed74 100644 Binary files a/others/imgc.png and b/others/imgc.png differ diff --git a/static/extra.css b/static/extra.css index 903ce05..4b8fe78 100644 --- a/static/extra.css +++ b/static/extra.css @@ -236,6 +236,7 @@ body { /* Style the tab container */ .tab { + /* display: flex; */ float: left; background-color: rgb(42, 36, 61); width: 25%; @@ -249,7 +250,7 @@ body { background-color: inherit; color: white; background-color: rgb(42, 36, 61); - padding: 16px; + /* padding: 16px; */ width: 100%; border: none; outline: none; diff --git a/static/feature.js b/static/feature.js index f49e953..8303520 100644 --- a/static/feature.js +++ b/static/feature.js @@ -138,17 +138,6 @@ function send_mic_input(userPrompt) { } } -const STT_source = new EventSource("/sst_event"); -STT_source.onmessage = function (event) { - if(allow_user_mic){ - const resp = event.data; - console.log(resp); - prompt += resp; - sst_content.innerHTML += resp; - }else - console.log("User not allowed this") - -}; // Handeling tab is setting modal function openTab(evt, tabName) { var i, tabcontent, tablinks; diff --git a/static/prefrence.css b/static/prefrence.css index 9f9f778..25ec722 100644 --- a/static/prefrence.css +++ b/static/prefrence.css @@ -1,84 +1,162 @@ /* Container for the modal list */ .modal-list { - display: flex; - flex-direction: column; - padding: 10px; - max-width: 100%; - margin: 0 auto; - } - - /* Each item in the list */ - .modal-list-item { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px; + display: flex; + flex-direction: column; + padding: 10px; + max-width: 100%; + margin: 0 auto; +} + +/* Each item in the list */ +.modal-list-item { + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-between; + padding: 10px; + color: white; + border-radius: 4px; + background-color: rgba(240, 248, 255, 0.178); +} + +/* Model name */ +.model-name { + font-size: 16px; + font-weight: bold; + /* color: #333; */ + cursor: pointer; +} + +/* Buttons container (hidden by default) */ +.buttons-container { + gap: 10px; + align-items: center; +} + + +/* Show the buttons container when the model name is clicked */ +.modal-list:hover .buttons-container { + display: block; +} + +.modal-list-item:hover { + background-color: rgba(204, 204, 204, 0.178); +} + +/* Optional: A little margin for the whole modal container */ +.modal-container { + margin: 20px; +} + +.none { + display: none; +} + + +.model-name-container { + font-size: 16px; + font-weight: bold; + display: flex; + justify-content: space-between; + cursor: pointer; +} + +.details-container { + overflow-y: auto; + width: 100%; + display: flex; + margin-top: 10px; + font-size: 14px; + border-radius: 4px; + align-content: stretch; + align-items: center; + flex-direction: column; +} + +.model-details-table { + width: 100%; + border-collapse: collapse; + margin-top: 10px; +} + +.details-label { + padding: 8px; + font-weight: bold; + text-align: left; + width: 40%; + /* Adjust to suit your layout */ +} + +.details-value { + padding: 8px; + text-align: left; + width: 60%; + /* Adjust to suit your layout */ +} + +.model-details-table tr:nth-child(odd) { + background-color: #f9f9f911; +} + +.model-details-table tr:nth-child(even) { + background-color: #ffffff4f; +} + +/* Code block container */ +.code_div { + background: #2a243d; + padding: 2px 5px; + border-radius: 10px; + box-shadow: 7px 7px 15px 0 #000; + +} + +pre { + counter-reset: line-numbering; + background-color: black; + color: #ecf0f1; + line-height: 140%; + + .line::before { + content: counter(line-numbering); + counter-increment: line-numbering; + padding-right: 1em; + /* space after numbers */ + padding-left: 8px; + width: 1.5em; + text-align: right; + opacity: 0.5; color: white; - border-radius: 20px; - border: 1px solid #ddd; - } - - /* Model name */ - .model-name { - font-size: 16px; - font-weight: bold; - /* color: #333; */ - cursor: pointer; - } - - /* Buttons container (hidden by default) */ - .buttons-container { - display: none; - gap: 10px; - align-items: center; } - - /* Style for the icons */ - .buttons-container i { - font-size: 18px; - cursor: pointer; - transition: transform 0.3s ease, color 0.3s ease; - } - - /* Hover effects for icons */ - .buttons-container i:hover { - color: #007bff; - transform: scale(1.2); - } - - /* Style for the "delete" icon */ - .buttons-container .fa-trash { - padding: 3px 10px; - color: #e74c3c; - } - - /* Style for the "view info" icon */ - .buttons-container .fa-info { - padding: 3px 10px; +} - color: #3498db; - } - - /* Style for the "manage" icon */ - .buttons-container .fa-greater-than-equal { - padding: 3px 10px; - color: #2ecc71; - } - - /* Show the buttons container when the model name is clicked */ - .modal-list:hover .buttons-container { - display: block; - } - - .modal-list-item:hover{ - background-color: rgb(204, 204, 204); - color: black; - } - - /* Optional: A little margin for the whole modal container */ - .modal-container { - margin: 20px; - } - .none{ - display: none; - } \ No newline at end of file +/* Copy text styling */ +.copy-text { + font-size: 12px; + color: #007bff; + cursor: pointer; + margin-right: 10px; +} + +/* Copy button styling */ +.copy-button { + + position: relative; + float: right; + padding: 5px 10px; + font-size: 12px; + color: white; + border: none; + border-radius: 0px; + cursor: pointer; + +} + +.copy-button:hover { + background-color: #218838; +} + +/* Change copy text to 'Copied!' */ +.copy-text.copied { + color: #28a745; +} \ No newline at end of file diff --git a/static/prefrence.js b/static/prefrence.js index 78d70e1..f4613b3 100644 --- a/static/prefrence.js +++ b/static/prefrence.js @@ -18,68 +18,124 @@ function saveProfile() { function cancelChanges() { document.getElementById("profileForm").reset(); alert("Changes canceled."); +} const modal_container = document.getElementById("modal_container"); + +function createModalList(models, model, size, parameter_size, family, format, quantization_level) { + + const sizeInGB = (size / 1e9).toFixed(2) + " GB"; // Decimal + // const sizeInGiB = (size / 1024 / 1024 / 1024).toFixed(2) + " GiB"; // Binary + const listContainer = document.createElement("div"); + listContainer.classList.add("modal-list"); + + // Create the row for the model + const row = document.createElement("div"); + row.classList.add("modal-list-item"); + + // Create a clickable container for the model name and toggle icon + const modelNameContainer = document.createElement("div"); + modelNameContainer.classList.add("model-name-container"); + modelNameContainer.style.display = "flex"; + modelNameContainer.style.alignItems = "center"; + modelNameContainer.style.cursor = "pointer"; + + const modelName = document.createElement("span"); + modelName.textContent = models; + modelName.classList.add("model-name"); + + // Create the toggle icon + const toggleIcon = document.createElement("i"); + toggleIcon.classList.add("fa-solid", "fa-chevron-down"); // Default icon + toggleIcon.style.marginLeft = "10px"; + + // Create a dropdown div for detailed info, initially hidden + const detailsContainer = document.createElement("div"); + detailsContainer.classList.add("details-container"); + detailsContainer.style.display = "none"; // Hidden by default + detailsContainer.style.paddingLeft = "20px"; // Indent for better visibility + // Add buttons below the details + const buttonsContainer = document.createElement("div"); + buttonsContainer.classList.add("buttons-container"); + buttonsContainer.style.marginTop = "10px"; + + const deleteButton = document.createElement("button"); + deleteButton.textContent = "Delete"; + deleteButton.onclick = () => { + row.remove(); // Remove the row from the list + console.log(`Deleted model: ${model}`); + }; + + const manageButton = document.createElement("button"); + manageButton.textContent = "Manage"; + manageButton.onclick = () => alert(`Managing model: ${model}`); + + // Append buttons to the container + buttonsContainer.appendChild(deleteButton); + buttonsContainer.appendChild(manageButton); + // Add detailed information about the model + const details = ` + + + + + + + + + + + + + + + + + + + + + + + + + +
Model:${model}
Size (Decimal):${sizeInGB}
Parameter Size:${parameter_size}
Family:${family}
Format:${format}
Quantization Level:${quantization_level}
+`; + detailsContainer.innerHTML = details; + + + // Add event listener to toggle details visibility + modelNameContainer.onclick = () => { + const isVisible = detailsContainer.style.display === "block"; + detailsContainer.style.display = isVisible ? "none" : "block"; // Toggle visibility + toggleIcon.className = isVisible + ? "fa-solid fa-chevron-down" + : "fa-solid fa-angle-up"; // Change icon dynamically + }; + + // Append model name and toggle icon to the container + modelNameContainer.appendChild(modelName); + modelNameContainer.appendChild(toggleIcon); + + // Append model name container and details container to the row + row.appendChild(modelNameContainer); + row.appendChild(detailsContainer); + detailsContainer.appendChild(buttonsContainer); + // Add row to the list container + listContainer.appendChild(row); + + // Append the list container to the modal + modal_container.appendChild(listContainer); +} + +// Placeholder functions for button actions +function viewInfo(model) { + alert(`Viewing info about ${model}`); +} + +function deleteModel(model) { + console.log(`Deleted ${model}`); +} + +function manageModel(model) { + alert(`Managing ${model}`); } -const modal_container = document.getElementById("modal_container"); -function createModalList(models) { - // Create a container for the list - const listContainer = document.createElement("div"); - listContainer.classList.add("modal-list"); - - // Create the row for each model - const row = document.createElement("div"); - row.classList.add("modal-list-item"); - - // Create model name (which is clickable) - const modelName = document.createElement("span"); - modelName.textContent = models; // Assuming model has a 'name' property - modelName.classList.add("model-name"); - modelName.style.cursor = 'pointer'; // Make it clear that it's clickable - - // Create a container for the buttons, initially hidden - const buttonsContainer = document.createElement("div"); - buttonsContainer.classList.add("buttons-container"); - buttonsContainer.classList.add("none"); - // buttonsContainer.style.display = 'none'; // Hide buttons by default - - // Create View Info button (>) - const viewButton = document.createElement("i"); - viewButton.classList.add("fa-solid", "fa-info"); - viewButton.onclick = () => alert(`Info about ${models}`); // Show info in alert - - // Create Delete button (trash icon) - const deleteButton = document.createElement("i"); - deleteButton.classList.add("fa-solid", "fa-trash"); - deleteButton.onclick = () => { - row.remove(); // Remove this model from the list - console.log(`Deleted ${models}`); - }; - - // Create Manage button (greater-than-or-equal icon) - const manageButton = document.createElement("i"); - manageButton.classList.add("fa-solid", "fa-greater-than-equal"); - manageButton.onclick = () => { - // Add functionality to manage the model, e.g., open edit dialog - alert(`Managing ${models}`); - }; - - // Append buttons to the buttons container - buttonsContainer.appendChild(viewButton); - buttonsContainer.appendChild(deleteButton); - buttonsContainer.appendChild(manageButton); - - // Add event listener to model name to toggle the visibility of buttons - modelName.onclick = () => { - const isVisible = buttonsContainer.style.display === 'block'; - buttonsContainer.style.display = isVisible ? 'none' : 'block'; // Toggle visibility - }; - - // Append model name and buttons container to the row - row.appendChild(modelName); - row.appendChild(buttonsContainer); - - // Add row to the list container - listContainer.appendChild(row); - - // Append the list container to the modal - modal_container.appendChild(listContainer); - } diff --git a/static/resource/markdown.js b/static/resource/markdown.js new file mode 100644 index 0000000..0a76e86 --- /dev/null +++ b/static/resource/markdown.js @@ -0,0 +1,112 @@ +function convertMarkdownToHTML(md) { + let inCodeBlock = false; // Track if inside a code block + let result = ''; + // Replace
tags with a placeholder, split by newlines, and restore
tags + const modifiedMd = md.replace(/
/g, '\n'); + const lines = modifiedMd.split('\n'); + // Split input into lines for processing + const linesWithNewLine = lines.map(line => line + '\n'); + // line = linesWithNewLine + linesWithNewLine.forEach((line) => { + if (line.startsWith('```')) { + if (!inCodeBlock) { + // Beginning of a code block + inCodeBlock = true; + const language = line.slice(3).trim(); // Extract the language (if any) + console.log(language); + + result += ` +
${ language || 'code'}
`;
+      } else {
+        // End of a code block
+        inCodeBlock = false;
+        result += '
'; + } + } else if (inCodeBlock) { + result += `${line.replace(//g, '>')}\n`; + // Inside a code block, add line as-is + } else { + // Outside of code blocks, process Markdown formatting + result += line + .replace(/^###### (.*$)/gm, '
$1
') // H6 headers + .replace(/^##### (.*$)/gm, '
$1
') // H5 headers + .replace(/^#### (.*$)/gm, '

$1

') // H4 headers + .replace(/^### (.*$)/gm, '

$1

') // H3 headers + .replace(/^## (.*$)/gm, '

$1

') // H2 headers + .replace(/^# (.*$)/g, '

$1

') // H1 headers + .replace(/\*\*(.*?)\*\*/g, '$1') // Bold + .replace(/\*(.*?)\*/g, '$1') // Italics + .replace(/!\[(.*?)\]\((.*?)\)/g, '$1') // Images + .replace(/\[(.*?)\]\((.*?)\)/g, '$1') // Links + .replace(/^\s*\n\* (.*)/gm, '') // Unordered lists + .replace(/<\/ul>\s*