mirror of
https://github.com/open-webui/assistant
synced 2025-05-11 23:40:33 +00:00
feat: model select
This commit is contained in:
parent
51d339b8a7
commit
847d47e17c
@ -10,6 +10,7 @@ import { FuseV1Options, FuseVersion } from "@electron/fuses";
|
|||||||
const config: ForgeConfig = {
|
const config: ForgeConfig = {
|
||||||
packagerConfig: {
|
packagerConfig: {
|
||||||
asar: true,
|
asar: true,
|
||||||
|
icon: "/src/assets/images/icon.png", // no file extension required
|
||||||
},
|
},
|
||||||
rebuildConfig: {},
|
rebuildConfig: {},
|
||||||
makers: [
|
makers: [
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<title>Open WebUI Assistant</title>
|
<title>Open WebUI Assistant</title>
|
||||||
|
<link rel="icon" type="image/png" href="./src/assets/images/icon.png" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
69
src/App.vue
69
src/App.vue
@ -6,25 +6,38 @@ import { ref, onBeforeMount } from 'vue';
|
|||||||
const url = ref('http://localhost:3000')
|
const url = ref('http://localhost:3000')
|
||||||
const token = ref('your_jwt')
|
const token = ref('your_jwt')
|
||||||
|
|
||||||
const submitHandler = async () => {
|
const models = ref([])
|
||||||
console.log(url.value, token.value)
|
const selectedModel = ref('')
|
||||||
|
|
||||||
|
const saveHandler = async () => {
|
||||||
|
console.log(url.value, token.value)
|
||||||
|
|
||||||
if (url.value.endsWith('/')) {
|
if (url.value.endsWith('/')) {
|
||||||
url.value = url.value.substring(0, url.value.length - 1);
|
url.value = url.value.substring(0, url.value.length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
window.electron.saveConfig({
|
window.electron.saveConfig({
|
||||||
url: url.value,
|
url: url.value,
|
||||||
token: token.value
|
token: token.value
|
||||||
})
|
})
|
||||||
|
|
||||||
const res = await window.electron.checkConnection()
|
const res = await window.electron.checkConnection()
|
||||||
|
|
||||||
new Notification("Open WebUI", { body: res ? 'Server Connection Verified' : 'Server Connection Failed' })
|
new Notification("Open WebUI", { body: res ? 'Server Connection Verified' : 'Server Connection Failed' })
|
||||||
|
|
||||||
console.log(res)
|
if (res) {
|
||||||
|
models.value = await window.electron.getModels()
|
||||||
|
|
||||||
|
console.log(models.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectModelHandler = async () => {
|
||||||
|
console.log(selectedModel.value)
|
||||||
|
|
||||||
|
if (selectedModel.value) {
|
||||||
|
selectedModel.value = await window.electron.selectModel(selectedModel.value)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,33 +49,53 @@ onBeforeMount(async () => {
|
|||||||
url.value = res.url
|
url.value = res.url
|
||||||
token.value = res.token
|
token.value = res.token
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class=" h-screen w-screen px-3 flex justify-center">
|
<div class=" h-screen w-screen p-3 flex justify-center">
|
||||||
<div class=" my-auto w-full flex flex-col gap-2">
|
<div class=" my-auto w-full flex flex-col gap-2">
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
<div class=" text-sm font-semibold">Open WebUI Assistant</div>
|
<div class=" text-sm font-semibold">Open WebUI Assistant</div>
|
||||||
|
|
||||||
<button class="bg-neutral-700 hover:bg-neutral-800 transition text-white text-xs px-3 py-1 rounded-lg"
|
<button class="bg-neutral-700 hover:bg-neutral-800 transition text-white text-xs px-3 py-1 rounded-lg"
|
||||||
@click="submitHandler">Save</button>
|
@click="saveHandler">Save</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-1.5">
|
<div class="flex flex-col gap-1.5">
|
||||||
|
|
||||||
|
|
||||||
<input v-model="url"
|
<input v-model="url"
|
||||||
class=" w-full bg-gray-100 hover:bg-gray-200 transition rounded-lg py-1 px-2 text-sm outline-none"
|
class=" w-full bg-gray-100 hover:bg-gray-200 transition rounded-lg py-1 px-2 text-xs outline-none"
|
||||||
placeholder="Open WebUI URL" />
|
placeholder="Open WebUI URL" />
|
||||||
<input v-model="token"
|
<input v-model="token"
|
||||||
class=" w-full bg-gray-100 hover:bg-gray-200 transition rounded-lg py-1 px-2 text-sm outline-none"
|
class=" w-full bg-gray-100 hover:bg-gray-200 transition rounded-lg py-1 px-2 text-xs outline-none"
|
||||||
placeholder="Open WebUI Token" />
|
placeholder="Open WebUI Token" />
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<div class="flex gap-1">
|
||||||
|
|
||||||
|
|
||||||
|
<select v-model="selectedModel"
|
||||||
|
class=" w-full bg-gray-100 hover:bg-gray-200 text-xs text-gray-700 transition rounded-lg py-1 px-2 outline-none">
|
||||||
|
<option value="" disabled class="text-xs text-gray-200">Select a model</option>
|
||||||
|
<option v-for="model in models" v-bind:value="model.name">{{ model.name }}</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<button class="p-1 bg-gray-100 hover:bg-gray-200 transition rounded-lg" @click="selectModelHandler">
|
||||||
|
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4">
|
||||||
|
<path fill-rule="evenodd"
|
||||||
|
d="M12.416 3.376a.75.75 0 0 1 .208 1.04l-5 7.5a.75.75 0 0 1-1.154.114l-3-3a.75.75 0 0 1 1.06-1.06l2.353 2.353 4.493-6.74a.75.75 0 0 1 1.04-.207Z"
|
||||||
|
clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@ -76,4 +109,16 @@ onBeforeMount(async () => {
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
@import './index.css';
|
@import './index.css';
|
||||||
|
|
||||||
|
select {
|
||||||
|
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236B7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3E");
|
||||||
|
background-position: right 0.5rem center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 1.5em 1.5em;
|
||||||
|
padding-right: 2.5rem;
|
||||||
|
-webkit-print-color-adjust: exact;
|
||||||
|
print-color-adjust: exact;
|
||||||
|
/* for Chrome */
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
BIN
src/assets/images/icon.png
Normal file
BIN
src/assets/images/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
59
src/main.ts
59
src/main.ts
@ -14,11 +14,12 @@ import { splitStream, sleep } from "./utils";
|
|||||||
keyboard.config.autoDelayMs = 0;
|
keyboard.config.autoDelayMs = 0;
|
||||||
|
|
||||||
let WEBUI_VERSION: string | null = null;
|
let WEBUI_VERSION: string | null = null;
|
||||||
|
let models: object[] = [];
|
||||||
|
|
||||||
|
let selectedModel = "";
|
||||||
let config = {
|
let config = {
|
||||||
url: "",
|
url: "",
|
||||||
token: "",
|
token: "",
|
||||||
model: "",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
|
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
|
||||||
@ -29,8 +30,9 @@ if (require("electron-squirrel-startup")) {
|
|||||||
const createWindow = () => {
|
const createWindow = () => {
|
||||||
// Create the browser window.
|
// Create the browser window.
|
||||||
const mainWindow = new BrowserWindow({
|
const mainWindow = new BrowserWindow({
|
||||||
|
icon: "/src/assets/images/icon.png",
|
||||||
width: 300,
|
width: 300,
|
||||||
height: 150,
|
height: 180,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
preload: path.join(__dirname, "preload.js"),
|
preload: path.join(__dirname, "preload.js"),
|
||||||
},
|
},
|
||||||
@ -74,7 +76,7 @@ const generateResponse = async (prompt: string) => {
|
|||||||
Authorization: `Bearer ${config.token}`,
|
Authorization: `Bearer ${config.token}`,
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
model: "mistral:latest",
|
model: selectedModel,
|
||||||
messages: [
|
messages: [
|
||||||
{
|
{
|
||||||
role: "user",
|
role: "user",
|
||||||
@ -148,7 +150,7 @@ const shortcutHandler = async () => {
|
|||||||
const prompt = await clipboard.readText();
|
const prompt = await clipboard.readText();
|
||||||
console.log(prompt);
|
console.log(prompt);
|
||||||
|
|
||||||
if (config.url !== "" && config.token !== "") {
|
if (config.url !== "" && config.token !== "" && selectedModel !== "") {
|
||||||
keyboard.config.autoDelayMs = 0;
|
keyboard.config.autoDelayMs = 0;
|
||||||
|
|
||||||
await generateResponse(prompt);
|
await generateResponse(prompt);
|
||||||
@ -189,6 +191,46 @@ const getVersion = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getModels = async () => {
|
||||||
|
if (config.url) {
|
||||||
|
const res = await fetch(`${config.url}/ollama/api/tags`, {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: `Bearer ${config.token}`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(async (res) => {
|
||||||
|
if (!res.ok) throw await res.json();
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(res);
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
return res.models;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectModel = async (modelId) => {
|
||||||
|
console.log(modelId);
|
||||||
|
selectedModel = modelId;
|
||||||
|
|
||||||
|
new Notification({
|
||||||
|
title: "Open WebUI",
|
||||||
|
body: `'${modelId}' selected.`,
|
||||||
|
}).show();
|
||||||
|
|
||||||
|
return selectedModel;
|
||||||
|
};
|
||||||
|
|
||||||
// This method will be called when Electron has finished
|
// This method will be called when Electron has finished
|
||||||
// initialization and is ready to create browser windows.
|
// initialization and is ready to create browser windows.
|
||||||
// Some APIs can only be used after this event occurs.
|
// Some APIs can only be used after this event occurs.
|
||||||
@ -200,7 +242,14 @@ app
|
|||||||
return WEBUI_VERSION !== null;
|
return WEBUI_VERSION !== null;
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle("get-models", (event, arg) => {});
|
ipcMain.handle("get-models", async (event, arg) => {
|
||||||
|
models = await getModels();
|
||||||
|
return models;
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("select-model", async (event, modelId) => {
|
||||||
|
return await selectModel(modelId);
|
||||||
|
});
|
||||||
|
|
||||||
ipcMain.handle("load-config", (event, arg) => {
|
ipcMain.handle("load-config", (event, arg) => {
|
||||||
return config;
|
return config;
|
||||||
|
@ -9,6 +9,8 @@ contextBridge.exposeInMainWorld("electron", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
checkConnection: () => ipcRenderer.invoke("check-connection"),
|
checkConnection: () => ipcRenderer.invoke("check-connection"),
|
||||||
|
getModels: () => ipcRenderer.invoke("get-models"),
|
||||||
|
selectModel: (modelId) => ipcRenderer.invoke("select-model", modelId),
|
||||||
loadConfig: () => ipcRenderer.invoke("load-config"),
|
loadConfig: () => ipcRenderer.invoke("load-config"),
|
||||||
saveConfig: (data) => ipcRenderer.send("save-config", data),
|
saveConfig: (data) => ipcRenderer.send("save-config", data),
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user