diff --git a/src/api.js b/src/api.js index 938b6a24..946f4e09 100644 --- a/src/api.js +++ b/src/api.js @@ -12,9 +12,10 @@ const { generate, stop, serve, + list, } = require("./service/ollama/ollama.js"); -let model = "mistral"; +let model = "mistral:latest"; function debugLog(msg) { if (global.debug) { @@ -36,7 +37,7 @@ async function runOllamaModel(event, msg) { await run(model, (json) => { // status will be set if the model is downloading if (json.status) { - if (json.status.includes("downloading")) { + if (json.status.includes("downloading") || json.status.includes("pulling")) { const percent = Math.round((json.completed / json.total) * 100); const content = isNaN(percent) ? "Downloading AI model..." @@ -151,6 +152,15 @@ async function serveOllama(event) { } } +async function listLocalModels(event) { + try { + modelList= await list() + event.reply("ollama:list", { success: true, content: modelList }); + } catch (err) { + event.reply("ollama:list", { success: false, content: err.message }); + } +} + function stopOllama(event) { stop(); } @@ -164,4 +174,5 @@ module.exports = { serveOllama, runOllamaModel, stopOllama, + listLocalModels, }; diff --git a/src/client.js b/src/client.js index ad462f25..38900eb7 100644 --- a/src/client.js +++ b/src/client.js @@ -18,17 +18,21 @@ const settingsView = document.getElementById("settings-view"); const settingsCancelBtn = document.getElementById("cancel-btn"); const settingsCloseBtn = document.getElementById("settings-close-btn"); const settingsSaveBtn = document.getElementById("save-btn"); +const settingsDownloadBtn = document.getElementById("download-btn"); const modelSelectInput = document.getElementById("model-select"); +const modelSelectDownloadInput = document.getElementById("model-select-download"); +const downloadEmptyWarning = document.getElementById("download-empty-warning"); let responseElem; /** * This is the initial chain of events that must run on start-up. - * 1. Start the Ollama server. + * 1. Start the Ollama server * 2. Run the model. This will load the model into memory so that first chat is not slow. * This step will also download the model if it is not already downloaded. * 3. Monitor the run status * 4. Load the chat + * 5. List locally avaliable models */ // 1. Start the Ollama server @@ -66,6 +70,21 @@ window.electronAPI.onOllamaRun((event, data) => { statusMsg.textContent = data.content; }); +// 5. List avaliable models +window.electronAPI.listLocalModels((event, data) => { + + let models=data.content.models + for(let i = 0; i < models.length; i++) { + var opt = document.createElement('option'); + opt.value = models[i].name; + opt.innerHTML = models[i].name; + modelSelectInput.appendChild(opt); + } + + +} + ); + // Update the display when a document is loaded window.electronAPI.onDocumentLoaded((event, data) => { document.getElementById("file-spinner").style.display = "none"; @@ -190,7 +209,6 @@ window.electronAPI.onChatReply((event, data) => { historyContainer.scrollTop = historyContainer.scrollHeight; } }); - // Open file dialog openFileButton.addEventListener("click", () => { document.getElementById("file-open-icon").style.display = "none"; @@ -253,3 +271,27 @@ userInput.addEventListener("input", function () { this.style.height = "auto"; this.style.height = this.scrollHeight + "px"; }); +//Download button in the settings menu +settingsDownloadBtn.addEventListener("click", () => { + let selectedModel=modelSelectDownloadInput.value + + if (selectedModel.length==0){ + alert("Input the model name from ollama.com/library to download a new model!") + return; + } + if (!selectedModel.includes(":")){ + //select latest model version if none is specified + selectedModel=selectedModel.concat(":latest") + } + var opt = document.createElement('option'); + opt.value = selectedModel; + opt.innerHTML = selectedModel; + modelSelectInput.appendChild(opt); + window.electronAPI.setModel(selectedModel); + window.electronAPI.runOllama(); + modelSelectDownloadInput.value="" + chatView.style.display = "none"; + settingsView.style.display = "none"; + document.getElementById("initial-view").style.display = "flex"; + +}); \ No newline at end of file diff --git a/src/index.css b/src/index.css index b86adcfd..13096267 100644 --- a/src/index.css +++ b/src/index.css @@ -219,6 +219,15 @@ button:focus { font-size: 1rem; width: 100%; } +#model-select-download { + padding: 10px; + background-color: var(--response-background); + border: 1px solid var(--primary-text); + color: var(--primary-text); + border-radius: 4px; + font-size: 1rem; + width: 100%; +} #model-select:focus { outline: none; @@ -460,4 +469,7 @@ button:focus { #user-input-text { flex-grow: 1; margin-right: 10px; +} +#download-empty-warning{ + display: none; } \ No newline at end of file diff --git a/src/index.html b/src/index.html index b3985b76..09247e8e 100644 --- a/src/index.html +++ b/src/index.html @@ -89,12 +89,18 @@