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": ""
|
"description": ""
|
||||||
},
|
},
|
||||||
"chagpt_api_connecting": {
|
"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": ""
|
"description": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -432,7 +432,7 @@
|
||||||
"description": ""
|
"description": ""
|
||||||
},
|
},
|
||||||
"chagpt_api_connecting": {
|
"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": ""
|
"description": ""
|
||||||
},
|
},
|
||||||
"Debug": {
|
"Debug": {
|
||||||
|
|
@ -478,5 +478,9 @@
|
||||||
"ollama_api_connecting": {
|
"ollama_api_connecting": {
|
||||||
"message": "Will attempt to connect to the Ollama Local Server using the host",
|
"message": "Will attempt to connect to the Ollama Local Server using the host",
|
||||||
"description": ""
|
"description": ""
|
||||||
|
},
|
||||||
|
"andModel": {
|
||||||
|
"message": "and model",
|
||||||
|
"description": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -432,7 +432,7 @@
|
||||||
"description": ""
|
"description": ""
|
||||||
},
|
},
|
||||||
"chagpt_api_connecting": {
|
"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": ""
|
"description": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -432,7 +432,7 @@
|
||||||
"description": ""
|
"description": ""
|
||||||
},
|
},
|
||||||
"chagpt_api_connecting": {
|
"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": ""
|
"description": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -68,26 +68,27 @@ let promptData = null;
|
||||||
// }
|
// }
|
||||||
// ============================== TESTING - END
|
// ============================== TESTING - END
|
||||||
|
|
||||||
const params = new URLSearchParams(window.location.search);
|
|
||||||
|
|
||||||
switch (llm) {
|
switch (llm) {
|
||||||
case "chatgpt_api":
|
case "chatgpt_api":
|
||||||
let prefs_api = await browser.storage.sync.get({chatgpt_api_key: '', chatgpt_model: ''});
|
let prefs_api = await browser.storage.sync.get({chatgpt_api_key: '', chatgpt_model: ''});
|
||||||
//console.log(">>>>>>>>>>> chatgpt_api_key: " + prefs_api_key.chatgpt_api_key);
|
//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});
|
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;
|
break;
|
||||||
case "ollama_api": {
|
case "ollama_api": {
|
||||||
let prefs_api = await browser.storage.sync.get({ollama_host: '', ollama_model: ''});
|
let prefs_api = await browser.storage.sync.get({ollama_host: '', ollama_model: ''});
|
||||||
//console.log(">>>>>>>>>>> ollama_host: " + prefs_api_key.ollama_host);
|
//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});
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Event listeners for worker messages
|
// Event listeners for worker messages
|
||||||
worker.onmessage = function(event) {
|
worker.onmessage = function(event) {
|
||||||
const { type, payload } = event.data;
|
const { type, payload } = event.data;
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,12 @@ messagesInputStyle .textContent = `
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
#stopButton {
|
||||||
|
width: 44px;
|
||||||
|
height: 36px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
#messageInputField {
|
#messageInputField {
|
||||||
background-color: #303030;
|
background-color: #303030;
|
||||||
|
|
@ -87,7 +93,32 @@ sendIcon.appendChild(sendPath);
|
||||||
sendButton.appendChild(sendIcon);
|
sendButton.appendChild(sendIcon);
|
||||||
messageInputTemplate.content.appendChild(sendButton);
|
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 {
|
class MessageInput extends HTMLElement {
|
||||||
|
|
||||||
|
model = '';
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
const shadowRoot = this.attachShadow({ mode: 'open' });
|
const shadowRoot = this.attachShadow({ mode: 'open' });
|
||||||
|
|
@ -95,9 +126,11 @@ class MessageInput extends HTMLElement {
|
||||||
|
|
||||||
this._messageInputField = shadowRoot.querySelector('#messageInputField');
|
this._messageInputField = shadowRoot.querySelector('#messageInputField');
|
||||||
this._sendButton = shadowRoot.querySelector('#sendButton');
|
this._sendButton = shadowRoot.querySelector('#sendButton');
|
||||||
|
this._stopButton = shadowRoot.querySelector('#stopButton');
|
||||||
|
|
||||||
this._messageInputField.addEventListener('keydown', this._handleKeyDown.bind(this));
|
this._messageInputField.addEventListener('keydown', this._handleKeyDown.bind(this));
|
||||||
this._sendButton.addEventListener('click', this._handleClick.bind(this));
|
this._sendButton.addEventListener('click', this._handleClick.bind(this));
|
||||||
|
this._stopButton.addEventListener('click', this._handleStopClick.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
|
|
@ -107,14 +140,18 @@ class MessageInput extends HTMLElement {
|
||||||
|
|
||||||
async init(worker) {
|
async init(worker) {
|
||||||
this.worker = 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) {
|
setMessagesArea(messagesAreaComponent) {
|
||||||
this.messagesAreaComponent = 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() {
|
handleMessageSent() {
|
||||||
// console.log("[ThunderAI] handleMessageSent");
|
// console.log("[ThunderAI] handleMessageSent");
|
||||||
this._messageInputField.value = '';
|
this._messageInputField.value = '';
|
||||||
|
|
@ -123,8 +160,12 @@ class MessageInput extends HTMLElement {
|
||||||
enableInput() {
|
enableInput() {
|
||||||
// console.log("[ThunderAI] enableInput");
|
// console.log("[ThunderAI] enableInput");
|
||||||
this._messageInputField.value = '';
|
this._messageInputField.value = '';
|
||||||
this._sendButton.removeAttribute('disabled');
|
|
||||||
this._messageInputField.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) {
|
_handleKeyDown(event) {
|
||||||
|
|
@ -137,6 +178,12 @@ class MessageInput extends HTMLElement {
|
||||||
this._handleNewChatMessage();
|
this._handleNewChatMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_handleStopClick() {
|
||||||
|
this.worker.postMessage({ type: 'stop' });
|
||||||
|
this._stopButton.setAttribute('disabled', 'disabled');
|
||||||
|
this._stopButton.title = 'Stopping...';
|
||||||
|
}
|
||||||
|
|
||||||
_handleNewChatMessage() {
|
_handleNewChatMessage() {
|
||||||
//do nothing if input is empty
|
//do nothing if input is empty
|
||||||
if ((!this._messageInputField.value)||(this._messageInputField.value.trim().length === 0)) {
|
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
|
// prevent user from interacting while we're waiting
|
||||||
this._sendButton.setAttribute('disabled', 'disabled');
|
this._sendButton.setAttribute('disabled', 'disabled');
|
||||||
|
this._sendButton.style.display = 'none';
|
||||||
|
this._stopButton.removeAttribute('disabled');
|
||||||
|
this._stopButton.style.display = 'block';
|
||||||
this._messageInputField.setAttribute('disabled', 'disabled');
|
this._messageInputField.setAttribute('disabled', 'disabled');
|
||||||
let messageContent = this._messageInputField.value;
|
let messageContent = this._messageInputField.value;
|
||||||
this._messageInputField.value = '';
|
this._messageInputField.value = '';
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@ messagesAreaTemplate.content.appendChild(messagesDiv);
|
||||||
class MessagesArea extends HTMLElement {
|
class MessagesArea extends HTMLElement {
|
||||||
|
|
||||||
fullTextHTML = "";
|
fullTextHTML = "";
|
||||||
|
llmName = "LLM";
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
@ -117,7 +118,7 @@ class MessagesArea extends HTMLElement {
|
||||||
|
|
||||||
if (isLastMessageFromUser) {
|
if (isLastMessageFromUser) {
|
||||||
const header = document.createElement('h2');
|
const header = document.createElement('h2');
|
||||||
header.textContent = "ChatGTP";
|
header.textContent = this.llmName;
|
||||||
this.messages.appendChild(header);
|
this.messages.appendChild(header);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,6 +131,10 @@ class MessagesArea extends HTMLElement {
|
||||||
this.worker = worker;
|
this.worker = worker;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setLLMName(llmName) {
|
||||||
|
this.llmName = llmName;
|
||||||
|
}
|
||||||
|
|
||||||
handleTokensDone(promptData = null) {
|
handleTokensDone(promptData = null) {
|
||||||
this.flushAccumulatingMessage();
|
this.flushAccumulatingMessage();
|
||||||
this.addActionButtons(promptData);
|
this.addActionButtons(promptData);
|
||||||
|
|
|
||||||
|
|
@ -25,66 +25,80 @@ import { Ollama } from '../js/api/ollama.js';
|
||||||
let ollama_host = null;
|
let ollama_host = null;
|
||||||
let ollama_model = '';
|
let ollama_model = '';
|
||||||
let ollama = null;
|
let ollama = null;
|
||||||
|
let stopStreaming = false;
|
||||||
|
|
||||||
let conversationHistory = [];
|
let conversationHistory = [];
|
||||||
let assistantResponseAccumulator = '';
|
let assistantResponseAccumulator = '';
|
||||||
|
|
||||||
self.onmessage = async function(event) {
|
self.onmessage = async function(event) {
|
||||||
if (event.data.type === 'init') {
|
switch (event.data.type) {
|
||||||
ollama_host = event.data.ollama_host;
|
case 'init':
|
||||||
ollama_model = event.data.ollama_model;
|
ollama_host = event.data.ollama_host;
|
||||||
//console.log(">>>>>>>>>>> ollama_host: " + ollama_host);
|
ollama_model = event.data.ollama_model;
|
||||||
ollama = new Ollama(ollama_host, ollama_model, true);
|
//console.log(">>>>>>>>>>> ollama_host: " + ollama_host);
|
||||||
} else if (event.data.type === 'chatMessage') {
|
ollama = new Ollama(ollama_host, ollama_model, true);
|
||||||
conversationHistory.push({ role: 'user', content: event.data.message });
|
break; // init
|
||||||
//console.log(">>>>>>>>>>> conversationHistory: " + JSON.stringify(conversationHistory));
|
case 'chatMessage':
|
||||||
const response = await ollama.fetchResponse(conversationHistory); //4096);
|
conversationHistory.push({ role: 'user', content: event.data.message });
|
||||||
postMessage({ type: 'messageSent' });
|
//console.log(">>>>>>>>>>> conversationHistory: " + JSON.stringify(conversationHistory));
|
||||||
|
const response = await ollama.fetchResponse(conversationHistory); //4096);
|
||||||
|
postMessage({ type: 'messageSent' });
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
let error_message = '';
|
let error_message = '';
|
||||||
let errorDetail = '';
|
let errorDetail = '';
|
||||||
if(response.is_exception === true){
|
if(response.is_exception === true){
|
||||||
error_message = response.error;
|
error_message = response.error;
|
||||||
}else{
|
}else{
|
||||||
const errorJSON = await response.json();
|
const errorJSON = await response.json();
|
||||||
errorDetail = JSON.stringify(errorJSON);
|
errorDetail = JSON.stringify(errorJSON);
|
||||||
error_message = errorJSON.error;
|
error_message = errorJSON.error;
|
||||||
//console.log(">>>>>>>>>>>>> errorJSON.error.message: " + JSON.stringify(errorJSON.error.message));
|
//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 reader = response.body.getReader();
|
||||||
const decoder = new TextDecoder("utf-8");
|
const decoder = new TextDecoder("utf-8");
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const { done, value } = await reader.read();
|
if (stopStreaming) {
|
||||||
if (done) {
|
stopStreaming = false;
|
||||||
conversationHistory.push({ role: 'assistant', content: assistantResponseAccumulator });
|
conversationHistory.push({ role: 'assistant', content: assistantResponseAccumulator });
|
||||||
assistantResponseAccumulator = '';
|
assistantResponseAccumulator = '';
|
||||||
postMessage({ type: 'tokensDone' });
|
postMessage({ type: 'tokensDone' });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// lots of low-level Ollama response parsing stuff
|
const { done, value } = await reader.read();
|
||||||
const chunk = decoder.decode(value);
|
if (done) {
|
||||||
//console.log(">>>>>>>>>>>>> chunk: " + chunk);
|
conversationHistory.push({ role: 'assistant', content: assistantResponseAccumulator });
|
||||||
const lines = chunk.split("\n");
|
assistantResponseAccumulator = '';
|
||||||
const parsedLines = lines
|
postMessage({ type: 'tokensDone' });
|
||||||
.map((line) => line.replace(/^chunk: /, "").trim()) // Remove the "chunk: " prefix
|
break;
|
||||||
.filter((line) => line !== "" && line !== "[DONE]") // Remove empty lines and "[DONE]"
|
}
|
||||||
.map((line) => JSON.parse(line)); // Parse the JSON string
|
// 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) {
|
for (const parsedLine of parsedLines) {
|
||||||
const { message } = parsedLine;
|
const { message } = parsedLine;
|
||||||
const { content } = message;
|
const { content } = message;
|
||||||
// Update the UI with the new content
|
// Update the UI with the new content
|
||||||
if (content) {
|
if (content) {
|
||||||
assistantResponseAccumulator += content;
|
assistantResponseAccumulator += content;
|
||||||
postMessage({ type: 'newToken', payload: { token: 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_api_key = null;
|
||||||
let chatgpt_model = '';
|
let chatgpt_model = '';
|
||||||
let openai = null;
|
let openai = null;
|
||||||
|
let stopStreaming = false;
|
||||||
|
|
||||||
let conversationHistory = [];
|
let conversationHistory = [];
|
||||||
let assistantResponseAccumulator = '';
|
let assistantResponseAccumulator = '';
|
||||||
|
|
@ -107,6 +108,13 @@ self.onmessage = async function(event) {
|
||||||
const decoder = new TextDecoder("utf-8");
|
const decoder = new TextDecoder("utf-8");
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
if (stopStreaming) {
|
||||||
|
stopStreaming = false;
|
||||||
|
conversationHistory.push({ role: 'assistant', content: assistantResponseAccumulator });
|
||||||
|
assistantResponseAccumulator = '';
|
||||||
|
postMessage({ type: 'tokensDone' });
|
||||||
|
break;
|
||||||
|
}
|
||||||
const { done, value } = await reader.read();
|
const { done, value } = await reader.read();
|
||||||
if (done) {
|
if (done) {
|
||||||
conversationHistory.push({ role: 'assistant', content: assistantResponseAccumulator });
|
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