stop button added. see #113. some info about the model are now dinamic
This commit is contained in:
parent
4db9f0c8a5
commit
4eba5a0eaf
9 changed files with 149 additions and 65 deletions
|
|
@ -432,7 +432,7 @@
|
|||
"description": ""
|
||||
},
|
||||
"chagpt_api_connecting": {
|
||||
"message": "Versucht, eine Verbindung zu OpenAI ChatGPT mit dem angegebenen API-Schlüssel herzustellen...",
|
||||
"message": "Versucht, eine Verbindung zu OpenAI ChatGPT mit dem angegebenen API-Schlüssel herzustellen",
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
|
|
@ -432,7 +432,7 @@
|
|||
"description": ""
|
||||
},
|
||||
"chagpt_api_connecting": {
|
||||
"message": "Will attempt to connect to OpenAI ChatGPT using the API key provided...",
|
||||
"message": "Will attempt to connect to OpenAI ChatGPT using the API key provided",
|
||||
"description": ""
|
||||
},
|
||||
"Debug": {
|
||||
|
|
@ -478,5 +478,9 @@
|
|||
"ollama_api_connecting": {
|
||||
"message": "Will attempt to connect to the Ollama Local Server using the host",
|
||||
"description": ""
|
||||
},
|
||||
"andModel": {
|
||||
"message": "and model",
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
|
|
@ -432,7 +432,7 @@
|
|||
"description": ""
|
||||
},
|
||||
"chagpt_api_connecting": {
|
||||
"message": "Tentative de connexion à OpenAI ChatGPT en utilisant la clé API fournie...",
|
||||
"message": "Tentative de connexion à OpenAI ChatGPT en utilisant la clé API fournie",
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
|
|
@ -432,7 +432,7 @@
|
|||
"description": ""
|
||||
},
|
||||
"chagpt_api_connecting": {
|
||||
"message": "Tentativo di connessione a OpenAI ChatGPT utilizzando la chiave API fornita...",
|
||||
"message": "Tentativo di connessione a OpenAI ChatGPT utilizzando la chiave API fornita",
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
|
|
@ -68,26 +68,27 @@ let promptData = null;
|
|||
// }
|
||||
// ============================== TESTING - END
|
||||
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
|
||||
switch (llm) {
|
||||
case "chatgpt_api":
|
||||
let prefs_api = await browser.storage.sync.get({chatgpt_api_key: '', chatgpt_model: ''});
|
||||
//console.log(">>>>>>>>>>> chatgpt_api_key: " + prefs_api_key.chatgpt_api_key);
|
||||
messageInput.setModel(prefs_api.chatgpt_model);
|
||||
messagesArea.setLLMName("ChatGPT");
|
||||
worker.postMessage({ type: 'init', chatgpt_api_key: prefs_api.chatgpt_api_key, chatgpt_model: prefs_api.chatgpt_model});
|
||||
messagesArea.appendUserMessage(browser.i18n.getMessage("chagpt_api_connecting"), "info");
|
||||
messagesArea.appendUserMessage(browser.i18n.getMessage("chagpt_api_connecting") + " " +browser.i18n.getMessage("AndModel") + " " + prefs_api.chatgpt_model + " ...", "info");
|
||||
break;
|
||||
case "ollama_api": {
|
||||
let prefs_api = await browser.storage.sync.get({ollama_host: '', ollama_model: ''});
|
||||
//console.log(">>>>>>>>>>> ollama_host: " + prefs_api_key.ollama_host);
|
||||
messageInput.setModel(prefs_api.ollama_model);
|
||||
messagesArea.setLLMName("Ollama Local");
|
||||
worker.postMessage({ type: 'init', ollama_host: prefs_api.ollama_host, ollama_model: prefs_api.ollama_model});
|
||||
messagesArea.appendUserMessage(browser.i18n.getMessage("ollama_api_connecting") + " " + prefs_api.ollama_host + " ...", "info");
|
||||
messagesArea.appendUserMessage(browser.i18n.getMessage("ollama_api_connecting") + " " + prefs_api.ollama_host + " " +browser.i18n.getMessage("AndModel") + " " + prefs_api.ollama_model + " ...", "info");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Event listeners for worker messages
|
||||
worker.onmessage = function(event) {
|
||||
const { type, payload } = event.data;
|
||||
|
|
|
|||
|
|
@ -50,6 +50,12 @@ messagesInputStyle .textContent = `
|
|||
cursor: pointer;
|
||||
border-radius: 10px;
|
||||
}
|
||||
#stopButton {
|
||||
width: 44px;
|
||||
height: 36px;
|
||||
cursor: pointer;
|
||||
border-radius: 10px;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
#messageInputField {
|
||||
background-color: #303030;
|
||||
|
|
@ -87,7 +93,32 @@ sendIcon.appendChild(sendPath);
|
|||
sendButton.appendChild(sendIcon);
|
||||
messageInputTemplate.content.appendChild(sendButton);
|
||||
|
||||
const stopButton = document.createElement('button');
|
||||
stopButton.id = 'stopButton';
|
||||
stopButton.style.display = 'none';
|
||||
|
||||
const stopIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
stopIcon.setAttribute('width', '24');
|
||||
stopIcon.setAttribute('height', '24');
|
||||
stopIcon.setAttribute('viewBox', '0 0 24 24');
|
||||
stopIcon.setAttribute('fill', 'none');
|
||||
stopIcon.classList.add('text-white', 'dark:text-black');
|
||||
|
||||
const stopRect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
||||
stopRect.setAttribute('x', '6');
|
||||
stopRect.setAttribute('y', '6');
|
||||
stopRect.setAttribute('width', '12');
|
||||
stopRect.setAttribute('height', '12');
|
||||
stopRect.setAttribute('fill', 'currentColor');
|
||||
|
||||
stopIcon.appendChild(stopRect);
|
||||
stopButton.appendChild(stopIcon);
|
||||
messageInputTemplate.content.appendChild(stopButton);
|
||||
|
||||
class MessageInput extends HTMLElement {
|
||||
|
||||
model = '';
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const shadowRoot = this.attachShadow({ mode: 'open' });
|
||||
|
|
@ -95,9 +126,11 @@ class MessageInput extends HTMLElement {
|
|||
|
||||
this._messageInputField = shadowRoot.querySelector('#messageInputField');
|
||||
this._sendButton = shadowRoot.querySelector('#sendButton');
|
||||
this._stopButton = shadowRoot.querySelector('#stopButton');
|
||||
|
||||
this._messageInputField.addEventListener('keydown', this._handleKeyDown.bind(this));
|
||||
this._sendButton.addEventListener('click', this._handleClick.bind(this));
|
||||
this._stopButton.addEventListener('click', this._handleStopClick.bind(this));
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
|
|
@ -107,14 +140,18 @@ class MessageInput extends HTMLElement {
|
|||
|
||||
async init(worker) {
|
||||
this.worker = worker;
|
||||
let prefs_api = await browser.storage.sync.get({chatgpt_model: ''});
|
||||
this._sendButton.title = await browser.i18n.getMessage("chagtp_api_send_button") + ": " + prefs_api.chatgpt_model;
|
||||
}
|
||||
|
||||
setMessagesArea(messagesAreaComponent) {
|
||||
this.messagesAreaComponent = messagesAreaComponent;
|
||||
}
|
||||
|
||||
setModel(model){
|
||||
this.model = model;
|
||||
this._sendButton.title = browser.i18n.getMessage("chagtp_api_send_button") + ": " + this.model;
|
||||
this._stopButton.title = browser.i18n.getMessage("chagtp_api_send_button") + ": " + this.model;
|
||||
}
|
||||
|
||||
handleMessageSent() {
|
||||
// console.log("[ThunderAI] handleMessageSent");
|
||||
this._messageInputField.value = '';
|
||||
|
|
@ -123,8 +160,12 @@ class MessageInput extends HTMLElement {
|
|||
enableInput() {
|
||||
// console.log("[ThunderAI] enableInput");
|
||||
this._messageInputField.value = '';
|
||||
this._sendButton.removeAttribute('disabled');
|
||||
this._messageInputField.removeAttribute('disabled');
|
||||
this._sendButton.removeAttribute('disabled');
|
||||
this._sendButton.style.display = 'block';
|
||||
this._stopButton.setAttribute('disabled', 'disabled');
|
||||
this._stopButton.style.display = 'none';
|
||||
this._stopButton.title = browser.i18n.getMessage("chagtp_api_send_button") + ": " + this.model;
|
||||
}
|
||||
|
||||
_handleKeyDown(event) {
|
||||
|
|
@ -137,6 +178,12 @@ class MessageInput extends HTMLElement {
|
|||
this._handleNewChatMessage();
|
||||
}
|
||||
|
||||
_handleStopClick() {
|
||||
this.worker.postMessage({ type: 'stop' });
|
||||
this._stopButton.setAttribute('disabled', 'disabled');
|
||||
this._stopButton.title = 'Stopping...';
|
||||
}
|
||||
|
||||
_handleNewChatMessage() {
|
||||
//do nothing if input is empty
|
||||
if ((!this._messageInputField.value)||(this._messageInputField.value.trim().length === 0)) {
|
||||
|
|
@ -144,6 +191,9 @@ class MessageInput extends HTMLElement {
|
|||
}
|
||||
// prevent user from interacting while we're waiting
|
||||
this._sendButton.setAttribute('disabled', 'disabled');
|
||||
this._sendButton.style.display = 'none';
|
||||
this._stopButton.removeAttribute('disabled');
|
||||
this._stopButton.style.display = 'block';
|
||||
this._messageInputField.setAttribute('disabled', 'disabled');
|
||||
let messageContent = this._messageInputField.value;
|
||||
this._messageInputField.value = '';
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ messagesAreaTemplate.content.appendChild(messagesDiv);
|
|||
class MessagesArea extends HTMLElement {
|
||||
|
||||
fullTextHTML = "";
|
||||
llmName = "LLM";
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
|
@ -117,7 +118,7 @@ class MessagesArea extends HTMLElement {
|
|||
|
||||
if (isLastMessageFromUser) {
|
||||
const header = document.createElement('h2');
|
||||
header.textContent = "ChatGTP";
|
||||
header.textContent = this.llmName;
|
||||
this.messages.appendChild(header);
|
||||
}
|
||||
|
||||
|
|
@ -130,6 +131,10 @@ class MessagesArea extends HTMLElement {
|
|||
this.worker = worker;
|
||||
}
|
||||
|
||||
setLLMName(llmName) {
|
||||
this.llmName = llmName;
|
||||
}
|
||||
|
||||
handleTokensDone(promptData = null) {
|
||||
this.flushAccumulatingMessage();
|
||||
this.addActionButtons(promptData);
|
||||
|
|
|
|||
|
|
@ -25,66 +25,80 @@ import { Ollama } from '../js/api/ollama.js';
|
|||
let ollama_host = null;
|
||||
let ollama_model = '';
|
||||
let ollama = null;
|
||||
let stopStreaming = false;
|
||||
|
||||
let conversationHistory = [];
|
||||
let assistantResponseAccumulator = '';
|
||||
|
||||
self.onmessage = async function(event) {
|
||||
if (event.data.type === 'init') {
|
||||
ollama_host = event.data.ollama_host;
|
||||
ollama_model = event.data.ollama_model;
|
||||
//console.log(">>>>>>>>>>> ollama_host: " + ollama_host);
|
||||
ollama = new Ollama(ollama_host, ollama_model, true);
|
||||
} else if (event.data.type === 'chatMessage') {
|
||||
conversationHistory.push({ role: 'user', content: event.data.message });
|
||||
//console.log(">>>>>>>>>>> conversationHistory: " + JSON.stringify(conversationHistory));
|
||||
const response = await ollama.fetchResponse(conversationHistory); //4096);
|
||||
postMessage({ type: 'messageSent' });
|
||||
switch (event.data.type) {
|
||||
case 'init':
|
||||
ollama_host = event.data.ollama_host;
|
||||
ollama_model = event.data.ollama_model;
|
||||
//console.log(">>>>>>>>>>> ollama_host: " + ollama_host);
|
||||
ollama = new Ollama(ollama_host, ollama_model, true);
|
||||
break; // init
|
||||
case 'chatMessage':
|
||||
conversationHistory.push({ role: 'user', content: event.data.message });
|
||||
//console.log(">>>>>>>>>>> conversationHistory: " + JSON.stringify(conversationHistory));
|
||||
const response = await ollama.fetchResponse(conversationHistory); //4096);
|
||||
postMessage({ type: 'messageSent' });
|
||||
|
||||
if (!response.ok) {
|
||||
let error_message = '';
|
||||
let errorDetail = '';
|
||||
if(response.is_exception === true){
|
||||
error_message = response.error;
|
||||
}else{
|
||||
const errorJSON = await response.json();
|
||||
errorDetail = JSON.stringify(errorJSON);
|
||||
error_message = errorJSON.error;
|
||||
//console.log(">>>>>>>>>>>>> errorJSON.error.message: " + JSON.stringify(errorJSON.error.message));
|
||||
if (!response.ok) {
|
||||
let error_message = '';
|
||||
let errorDetail = '';
|
||||
if(response.is_exception === true){
|
||||
error_message = response.error;
|
||||
}else{
|
||||
const errorJSON = await response.json();
|
||||
errorDetail = JSON.stringify(errorJSON);
|
||||
error_message = errorJSON.error;
|
||||
//console.log(">>>>>>>>>>>>> errorJSON.error.message: " + JSON.stringify(errorJSON.error.message));
|
||||
}
|
||||
postMessage({ type: 'error', payload: "Ollama API request failed: " + error_message });
|
||||
throw new Error("[ThunderAI] Ollama API request failed: " + response.status + " " + response.statusText + ", Detail: " + errorDetail);
|
||||
}
|
||||
postMessage({ type: 'error', payload: "Ollama API request failed: " + error_message });
|
||||
throw new Error("[ThunderAI] Ollama API request failed: " + response.status + " " + response.statusText + ", Detail: " + errorDetail);
|
||||
}
|
||||
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder("utf-8");
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) {
|
||||
conversationHistory.push({ role: 'assistant', content: assistantResponseAccumulator });
|
||||
assistantResponseAccumulator = '';
|
||||
postMessage({ type: 'tokensDone' });
|
||||
break;
|
||||
}
|
||||
// lots of low-level Ollama response parsing stuff
|
||||
const chunk = decoder.decode(value);
|
||||
//console.log(">>>>>>>>>>>>> chunk: " + chunk);
|
||||
const lines = chunk.split("\n");
|
||||
const parsedLines = lines
|
||||
.map((line) => line.replace(/^chunk: /, "").trim()) // Remove the "chunk: " prefix
|
||||
.filter((line) => line !== "" && line !== "[DONE]") // Remove empty lines and "[DONE]"
|
||||
.map((line) => JSON.parse(line)); // Parse the JSON string
|
||||
|
||||
for (const parsedLine of parsedLines) {
|
||||
const { message } = parsedLine;
|
||||
const { content } = message;
|
||||
// Update the UI with the new content
|
||||
if (content) {
|
||||
assistantResponseAccumulator += content;
|
||||
postMessage({ type: 'newToken', payload: { token: content } });
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder("utf-8");
|
||||
|
||||
while (true) {
|
||||
if (stopStreaming) {
|
||||
stopStreaming = false;
|
||||
conversationHistory.push({ role: 'assistant', content: assistantResponseAccumulator });
|
||||
assistantResponseAccumulator = '';
|
||||
postMessage({ type: 'tokensDone' });
|
||||
break;
|
||||
}
|
||||
const { done, value } = await reader.read();
|
||||
if (done) {
|
||||
conversationHistory.push({ role: 'assistant', content: assistantResponseAccumulator });
|
||||
assistantResponseAccumulator = '';
|
||||
postMessage({ type: 'tokensDone' });
|
||||
break;
|
||||
}
|
||||
// lots of low-level Ollama response parsing stuff
|
||||
const chunk = decoder.decode(value);
|
||||
//console.log(">>>>>>>>>>>>> chunk: " + chunk);
|
||||
const lines = chunk.split("\n");
|
||||
const parsedLines = lines
|
||||
.map((line) => line.replace(/^chunk: /, "").trim()) // Remove the "chunk: " prefix
|
||||
.filter((line) => line !== "" && line !== "[DONE]") // Remove empty lines and "[DONE]"
|
||||
.map((line) => JSON.parse(line)); // Parse the JSON string
|
||||
|
||||
for (const parsedLine of parsedLines) {
|
||||
const { message } = parsedLine;
|
||||
const { content } = message;
|
||||
// Update the UI with the new content
|
||||
if (content) {
|
||||
assistantResponseAccumulator += content;
|
||||
postMessage({ type: 'newToken', payload: { token: content } });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break; //chatMessage
|
||||
case 'stop':
|
||||
stopStreaming = true;
|
||||
break; //stop
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ import { OpenAI } from '../js/api/openai.js';
|
|||
let chatgpt_api_key = null;
|
||||
let chatgpt_model = '';
|
||||
let openai = null;
|
||||
let stopStreaming = false;
|
||||
|
||||
let conversationHistory = [];
|
||||
let assistantResponseAccumulator = '';
|
||||
|
|
@ -107,6 +108,13 @@ self.onmessage = async function(event) {
|
|||
const decoder = new TextDecoder("utf-8");
|
||||
|
||||
while (true) {
|
||||
if (stopStreaming) {
|
||||
stopStreaming = false;
|
||||
conversationHistory.push({ role: 'assistant', content: assistantResponseAccumulator });
|
||||
assistantResponseAccumulator = '';
|
||||
postMessage({ type: 'tokensDone' });
|
||||
break;
|
||||
}
|
||||
const { done, value } = await reader.read();
|
||||
if (done) {
|
||||
conversationHistory.push({ role: 'assistant', content: assistantResponseAccumulator });
|
||||
|
|
@ -133,5 +141,7 @@ self.onmessage = async function(event) {
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if (event.data.type === 'stop') {
|
||||
stopStreaming = true;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue