// application/static/js/chat.js import requests from "./request.js"; class Chat{ constructor(uiManager){ this.uiManager = uiManager; } async chat(){ let payload = { "model": this.uiManager.initializer.model, "prompt": this.uiManager.userP.innerText.trim(), "convId": this.uiManager.initializer.convId, "system": this.uiManager.initializer.systemPrompt, "temperature": 0.7, "top_p": 0.9, "webSearch": this.uiManager.webSearch }; if (this.uiManager.imageMode) { payload.image = this.uiManager.userDiv.querySelector('img')?.src || null; } try { if(this.uiManager.initializer.convId==null){ // Use the new createConv method in Initialize const newConvId = await this.uiManager.initializer.createConv(); if (!newConvId) { // Check for error console.error("Failed to create new conversation."); return; // Exit if creation failed } payload["convId"] = newConvId; // Use the returned convId } this.uiManager.textBox.value=''; this.uiManager.sendBtn.disabled = true; // Display "Reasoning..." and get the reasoning display element. const reasoningDisplay = this.uiManager.showReasoning(); const response = await requests.request('POST','/completions',{"Content-Type": "application/json"},JSON.stringify(payload),true); let fullResponse = ""; // Accumulate the full response for await (const chunk of response){ fullResponse += chunk; // Check for commands *within* the streamed response let commandMatch; if ((commandMatch = fullResponse.match(/\/(\w+)\s*\(([^)]*)\)/))) { const command = commandMatch[1]; const argument = commandMatch[2]; if (command === "search") { // Remove the command from the displayed text fullResponse = fullResponse.replace(commandMatch[0], ""); this.uiManager.aiP.innerHTML = fullResponse; // Update with cleaned text this.uiManager.renderSymbols.renderAll(this.uiManager.aiP) // Perform web search const searchData = await requests.request('POST', '/websearch', { "Content-Type": "application/json" }, JSON.stringify({ query: argument }), false); const searchResults = await searchData.json(); // Display search results, possibly in a dedicated container this.uiManager.appendAiMsg(searchResults.result); } else if (command === "image") { // Remove the command from the displayed text fullResponse = fullResponse.replace(commandMatch[0], ""); this.uiManager.aiP.innerHTML = fullResponse; this.uiManager.renderSymbols.renderAll(this.uiManager.aiP); const imageData = await requests.request('POST', '/generate_image', { "Content-Type": "application/json" }, JSON.stringify({ prompt: argument }), false); const imageResult = await imageData.json(); if (imageResult && imageResult.image) { this.uiManager.appendAiMsg(`Generated Image`); } else { this.uiManager.appendAiMsg("Error generating image."); } } else if (command === "memsave") { fullResponse = fullResponse.replace(commandMatch[0], ""); //remove command this.uiManager.aiP.innerHTML = fullResponse; this.uiManager.renderSymbols.renderAll(this.uiManager.aiP) await requests.request('POST', '/save_memory', { "Content-Type": "application/json" }, JSON.stringify({ memory: argument, convId: this.uiManager.initializer.convId }), false); this.uiManager.displayMemory(argument); // Display the saved memory } //Remove the command and only display the rest this.uiManager.aiP.innerHTML = fullResponse; } else { // No command found, display the chunk normally this.uiManager.aiP.innerHTML = fullResponse; // accumulate } this.uiManager.renderSymbols.renderAll(this.uiManager.aiP) }; if (reasoningDisplay) { this.uiManager.updateReasoningTime(reasoningDisplay); // Update with the final time } } catch (error) { this.uiManager.sendBtn.disabled = false; this.uiManager.aiP.innerHTML+= `${error}`; return } this.uiManager.renderSymbols.renderCode(this.uiManager.aiP); if(this.uiManager.initializer.convTitle==null){ this.uiManager.initializer.convTitle = this.uiManager.userP.innerText.substring(0,23); this.uiManager.addChat(); } // Add read-aloud button *after* the response is complete this.uiManager.addReadAloudButton(this.uiManager.aiDiv); this.uiManager.sendBtn.disabled = false; } } export default Chat