Compare commits

...

14 Commits

Author SHA1 Message Date
Timothy Jaeryang Baek 3c10c3b928 Merge pull request #910 from open-webui/dev
Create and publish a Docker image / build-and-push-image (push) Failing after 1m9s
0.1.104
2024-02-25 16:05:21 -05:00
Timothy J. Baek 1d937ec2f2 fix: cleanup unused tags 2024-02-25 13:03:26 -08:00
Timothy J. Baek 7c1b8e396a Update CHANGELOG.md 2024-02-25 12:51:08 -08:00
Timothy J. Baek ce891dfd5a fix: styling 2024-02-25 12:45:23 -08:00
Timothy J. Baek c5009907ff Update CHANGELOG.md 2024-02-25 12:37:23 -08:00
Timothy J. Baek 6780b1b19e fix: styling 2024-02-25 12:36:33 -08:00
Timothy J. Baek 8dce6b4b49 feat: personalised greeting 2024-02-25 12:34:32 -08:00
Timothy J. Baek 0d9e38a0ce refac: styling 2024-02-25 12:31:31 -08:00
Timothy Jaeryang Baek 129bb21a0a Merge pull request #911 from open-webui/check-updates
fix: check version loading progress
2024-02-25 15:11:06 -05:00
Timothy J. Baek fb3804ac95 fix: check version loading progress 2024-02-25 12:10:14 -08:00
Timothy Jaeryang Baek 13db1d5063 Merge pull request #909 from open-webui/check-updates
Check updates
2024-02-25 14:55:56 -05:00
Timothy J. Baek 759883a4c8 feat: check for updates 2024-02-25 11:55:15 -08:00
Timothy J. Baek 6bfe2a6306 feat: latest release check api 2024-02-25 11:26:58 -08:00
Timothy Jaeryang Baek b82ac66c70 Merge pull request #908 from open-webui/main
dev
2024-02-25 14:12:48 -05:00
14 changed files with 210 additions and 66 deletions
+11
View File
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.1.104] - 2024-02-25
### Added
- **🔄 Check for Updates**: Keep your system current by checking for updates conveniently located in Settings > About.
- **🗑️ Automatic Tag Deletion**: Unused tags on the sidebar will now be deleted automatically with just a click.
### Changed
- **🎨 Modernized Styling**: Enjoy a refreshed look with updated styling for a more contemporary experience.
## [0.1.103] - 2024-02-25
### Added
+21
View File
@@ -167,6 +167,27 @@ class TagTable:
.count()
)
def delete_tag_by_tag_name_and_user_id(self, tag_name: str, user_id: str) -> bool:
try:
query = ChatIdTag.delete().where(
(ChatIdTag.tag_name == tag_name) & (ChatIdTag.user_id == user_id)
)
res = query.execute() # Remove the rows, return number of rows removed.
print(res)
tag_count = self.count_chat_ids_by_tag_name_and_user_id(tag_name, user_id)
if tag_count == 0:
# Remove tag item from Tag col as well
query = Tag.delete().where(
(Tag.name == tag_name) & (Tag.user_id == user_id)
)
query.execute() # Remove the rows, return number of rows removed.
return True
except Exception as e:
print("delete_tag", e)
return False
def delete_tag_by_tag_name_and_chat_id_and_user_id(
self, tag_name: str, chat_id: str, user_id: str
) -> bool:
+5 -2
View File
@@ -115,9 +115,12 @@ async def get_user_chats_by_tag_name(
for chat_id_tag in Tags.get_chat_ids_by_tag_name_and_user_id(tag_name, user.id)
]
print(chat_ids)
chats = Chats.get_chat_lists_by_chat_ids(chat_ids, skip, limit)
return Chats.get_chat_lists_by_chat_ids(chat_ids, skip, limit)
if len(chats) == 0:
Tags.delete_tag_by_tag_name_and_user_id(tag_name, user.id)
return chats
############################
+1
View File
@@ -47,3 +47,4 @@ class ERROR_MESSAGES(str, Enum):
INCORRECT_FORMAT = (
lambda err="": f"Invalid format. Please use the correct format{err if err else ''}"
)
RATE_LIMIT_EXCEEDED = "API rate limit exceeded"
+21 -1
View File
@@ -4,8 +4,9 @@ import markdown
import time
import os
import sys
import requests
from fastapi import FastAPI, Request, Depends
from fastapi import FastAPI, Request, Depends, status
from fastapi.staticfiles import StaticFiles
from fastapi import HTTPException
from fastapi.responses import JSONResponse
@@ -26,6 +27,8 @@ from apps.web.main import app as webui_app
from config import WEBUI_NAME, ENV, VERSION, CHANGELOG, FRONTEND_BUILD_DIR
from constants import ERROR_MESSAGES
from utils.utils import get_http_authorization_cred, get_current_user
@@ -127,6 +130,23 @@ async def get_app_changelog():
return CHANGELOG
@app.get("/api/version/updates")
async def get_app_latest_release_version():
try:
response = requests.get(
f"https://api.github.com/repos/open-webui/open-webui/releases/latest"
)
response.raise_for_status()
latest_version = response.json()["tag_name"]
return {"current": VERSION, "latest": latest_version[1:]}
except Exception as e:
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail=ERROR_MESSAGES.RATE_LIMIT_EXCEEDED,
)
app.mount("/static", StaticFiles(directory="static"), name="static")
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "open-webui",
"version": "0.1.103",
"version": "0.1.104",
"private": true,
"scripts": {
"dev": "vite dev --host",
+34
View File
@@ -19,6 +19,10 @@ export const getBackendConfig = async () => {
return null;
});
if (error) {
throw error;
}
return res;
};
@@ -41,5 +45,35 @@ export const getChangelog = async () => {
return null;
});
if (error) {
throw error;
}
return res;
};
export const getVersionUpdates = async () => {
let error = null;
const res = await fetch(`${WEBUI_BASE_URL}/api/version/updates`, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
error = err;
return null;
});
if (error) {
throw error;
}
return res;
};
+12 -14
View File
@@ -490,7 +490,7 @@
}}
/>
<form
class=" flex flex-col relative w-full rounded-xl border dark:border-gray-700 bg-white dark:bg-gray-900 dark:text-gray-100"
class=" flex flex-col relative w-full rounded-3xl px-1.5 border border-gray-100 dark:border-gray-850 bg-white dark:bg-gray-900 dark:text-gray-100"
on:submit|preventDefault={() => {
submitPrompt(prompt, user);
}}
@@ -633,9 +633,9 @@
<div class=" flex">
{#if fileUploadEnabled}
<div class=" self-end mb-2 ml-1.5">
<div class=" self-end mb-2 ml-1">
<button
class=" text-gray-600 dark:text-gray-200 transition rounded-lg p-1 ml-1"
class="bg-gray-50 hover:bg-gray-100 text-gray-800 dark:bg-gray-850 dark:text-white dark:hover:bg-gray-800 transition rounded-full p-1.5"
type="button"
on:click={() => {
filesInputElement.click();
@@ -643,14 +643,12 @@
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
viewBox="0 0 16 16"
fill="currentColor"
class="w-5 h-5"
class="w-[1.2rem] h-[1.2rem]"
>
<path
fill-rule="evenodd"
d="M15.621 4.379a3 3 0 00-4.242 0l-7 7a3 3 0 004.241 4.243h.001l.497-.5a.75.75 0 011.064 1.057l-.498.501-.002.002a4.5 4.5 0 01-6.364-6.364l7-7a4.5 4.5 0 016.368 6.36l-3.455 3.553A2.625 2.625 0 119.52 9.52l3.45-3.451a.75.75 0 111.061 1.06l-3.45 3.451a1.125 1.125 0 001.587 1.595l3.454-3.553a3 3 0 000-4.242z"
clip-rule="evenodd"
d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z"
/>
</svg>
</button>
@@ -659,7 +657,7 @@
<textarea
id="chat-textarea"
class=" dark:bg-gray-900 dark:text-gray-100 outline-none w-full py-3 px-2 {fileUploadEnabled
class=" dark:bg-gray-900 dark:text-gray-100 outline-none w-full py-3 px-3 {fileUploadEnabled
? ''
: ' pl-4'} rounded-xl resize-none h-[48px]"
placeholder={chatInputPlaceholder !== ''
@@ -803,12 +801,12 @@
}}
/>
<div class="self-end mb-2 flex space-x-0.5 mr-2">
<div class="self-end mb-2 flex space-x-1 mr-1">
{#if messages.length == 0 || messages.at(-1).done == true}
{#if speechRecognitionEnabled}
<button
id="voice-input-button"
class=" text-gray-600 dark:text-gray-300 transition rounded-lg p-1.5 mr-0.5 self-center"
class=" text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-850 transition rounded-full p-1.5 mr-0.5 self-center"
type="button"
on:click={() => {
speechRecognitionHandler();
@@ -869,7 +867,7 @@
<button
class="{prompt !== ''
? 'bg-black text-white hover:bg-gray-900 dark:bg-white dark:text-black dark:hover:bg-gray-100 '
: 'text-white bg-gray-100 dark:text-gray-900 dark:bg-gray-800 disabled'} transition rounded-lg p-1 mr-0.5 w-7 h-7 self-center"
: 'text-white bg-gray-100 dark:text-gray-900 dark:bg-gray-800 disabled'} transition rounded-full p-1.5 self-center"
type="submit"
disabled={prompt === ''}
>
@@ -877,7 +875,7 @@
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4.5 h-4.5 mx-auto"
class="w-5 h-5"
>
<path
fill-rule="evenodd"
@@ -888,7 +886,7 @@
</button>
{:else}
<button
class="bg-white hover:bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-white dark:hover:bg-gray-800 transition rounded-lg p-1.5"
class="bg-white hover:bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-white dark:hover:bg-gray-800 transition rounded-full p-1.5"
on:click={stopResponse}
>
<svg
@@ -10,45 +10,47 @@
: suggestionPrompts.sort(() => Math.random() - 0.5).slice(0, 4);
</script>
<div class=" flex flex-wrap-reverse mb-3 md:p-1 text-left w-full">
{#each prompts as prompt, promptIdx}
<div
class="{promptIdx > 1 ? 'hidden sm:inline-flex' : ''} basis-full sm:basis-1/2 p-[5px] px-2"
>
<button
class=" flex-1 flex justify-between w-full h-full px-4 py-2.5 bg-white hover:bg-gray-50 dark:bg-gray-900 dark:hover:bg-gray-700 outline outline-1 outline-gray-200 dark:outline-gray-800 rounded-lg transition group"
on:click={() => {
submitPrompt(prompt.content);
}}
<div class=" mb-3 md:p-1 text-left w-full">
<div class=" flex flex-wrap-reverse px-2 text-left">
{#each prompts as prompt, promptIdx}
<div
class="{promptIdx > 1 ? 'hidden sm:inline-flex' : ''} basis-full sm:basis-1/2 p-[5px] px-1"
>
<div class="flex flex-col text-left self-center">
{#if prompt.title && prompt.title[0] !== ''}
<div class="text-sm font-medium dark:text-gray-300">{prompt.title[0]}</div>
<div class="text-sm text-gray-500 line-clamp-1">{prompt.title[1]}</div>
{:else}
<div class=" self-center text-sm font-medium dark:text-gray-300 line-clamp-2">
{prompt.content}
</div>
{/if}
</div>
<div
class="self-center p-1 rounded-lg text-white group-hover:bg-gray-100 group-hover:text-gray-800 dark:group-hover:bg-gray-800 dark:group-hover:text-gray-100 dark:text-gray-900 transition"
<button
class=" flex-1 flex justify-between w-full h-full px-4 py-2.5 bg-gray-50 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 rounded-2xl transition group"
on:click={() => {
submitPrompt(prompt.content);
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
<div class="flex flex-col text-left self-center">
{#if prompt.title && prompt.title[0] !== ''}
<div class="text-sm font-medium dark:text-gray-300">{prompt.title[0]}</div>
<div class="text-sm text-gray-500 line-clamp-1">{prompt.title[1]}</div>
{:else}
<div class=" self-center text-sm font-medium dark:text-gray-300 line-clamp-2">
{prompt.content}
</div>
{/if}
</div>
<div
class="self-center p-1 rounded-lg text-gray-50 group-hover:text-gray-800 dark:text-gray-850 dark:group-hover:text-gray-100 transition"
>
<path
fill-rule="evenodd"
d="M8 14a.75.75 0 0 1-.75-.75V4.56L4.03 7.78a.75.75 0 0 1-1.06-1.06l4.5-4.5a.75.75 0 0 1 1.06 0l4.5 4.5a.75.75 0 0 1-1.06 1.06L8.75 4.56v8.69A.75.75 0 0 1 8 14Z"
clip-rule="evenodd"
/>
</svg>
</div>
</button>
</div>
{/each}
<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="M8 14a.75.75 0 0 1-.75-.75V4.56L4.03 7.78a.75.75 0 0 1-1.06-1.06l4.5-4.5a.75.75 0 0 1 1.06 0l4.5 4.5a.75.75 0 0 1-1.06 1.06L8.75 4.56v8.69A.75.75 0 0 1 8 14Z"
clip-rule="evenodd"
/>
</svg>
</div>
</button>
</div>
{/each}
</div>
</div>
@@ -1,5 +1,6 @@
<script lang="ts">
import { WEBUI_BASE_URL } from '$lib/constants';
import { user } from '$lib/stores';
import { onMount } from 'svelte';
export let models = [];
@@ -63,7 +64,9 @@
</div>
{/if}
{:else}
How can I help you today?
<div class=" line-clamp-1">Hello, {$user.name}</div>
<div>How can I help you today?</div>
{/if}
</div>
</div>
+48 -2
View File
@@ -1,14 +1,40 @@
<script lang="ts">
import { getVersionUpdates } from '$lib/apis';
import { getOllamaVersion } from '$lib/apis/ollama';
import { WEBUI_VERSION } from '$lib/constants';
import { WEBUI_NAME, config, showChangelog } from '$lib/stores';
import { compareVersion } from '$lib/utils';
import { onMount } from 'svelte';
let ollamaVersion = '';
let updateAvailable = null;
let version = {
current: '',
latest: ''
};
const checkForVersionUpdates = async () => {
updateAvailable = null;
version = await getVersionUpdates(localStorage.token).catch((error) => {
return {
current: WEBUI_VERSION,
latest: WEBUI_VERSION
};
});
console.log(version);
updateAvailable = compareVersion(version.latest, version.current);
console.log(updateAvailable);
};
onMount(async () => {
ollamaVersion = await getOllamaVersion(localStorage.token).catch((error) => {
return '';
});
checkForVersionUpdates();
});
</script>
@@ -20,10 +46,21 @@
{$WEBUI_NAME} Version
</div>
</div>
<div class="flex w-full">
<div class="flex-1 text-xs text-gray-700 dark:text-gray-200 flex space-x-1.5 items-center">
<div class="flex w-full justify-between items-center">
<div class="flex flex-col text-xs text-gray-700 dark:text-gray-200">
<div>
v{WEBUI_VERSION}
<a
href="https://github.com/open-webui/open-webui/releases/tag/v{version.latest}"
target="_blank"
>
{updateAvailable === null
? 'Checking for updates...'
: updateAvailable
? `(v${version.latest} available!)`
: '(latest)'}
</a>
</div>
<button
@@ -35,6 +72,15 @@
<div>See what's new</div>
</button>
</div>
<button
class=" text-xs px-3 py-1.5 bg-gray-100 hover:bg-gray-200 dark:bg-gray-850 dark:hover:bg-gray-800 transition rounded-lg font-medium"
on:click={() => {
checkForVersionUpdates();
}}
>
Check for updates
</button>
</div>
</div>
+8 -2
View File
@@ -13,7 +13,8 @@
getChatList,
getChatById,
getChatListByTagName,
updateChatById
updateChatById,
getAllChatTags
} from '$lib/apis/chats';
import toast from 'svelte-french-toast';
import { slide } from 'svelte/transition';
@@ -330,7 +331,12 @@
<button
class="px-2.5 text-xs font-medium bg-gray-900 hover:bg-gray-800 transition rounded-full"
on:click={async () => {
await chats.set(await getChatListByTagName(localStorage.token, tag.name));
let chatIds = await getChatListByTagName(localStorage.token, tag.name);
if (chatIds.length === 0) {
await tags.set(await getAllChatTags(localStorage.token));
chatIds = await getChatList(localStorage.token);
}
await chats.set(chatIds);
}}
>
{tag.name}
+2 -3
View File
@@ -101,11 +101,10 @@ export const copyToClipboard = (text) => {
);
};
export const checkVersion = (required, current) => {
// Returns true when current version is below required
export const compareVersion = (latest, current) => {
return current === '0.0.0'
? false
: current.localeCompare(required, undefined, {
: current.localeCompare(latest, undefined, {
numeric: true,
sensitivity: 'case',
caseFirst: 'upper'
+2 -2
View File
@@ -28,7 +28,7 @@
config
} from '$lib/stores';
import { REQUIRED_OLLAMA_VERSION, WEBUI_API_BASE_URL } from '$lib/constants';
import { checkVersion } from '$lib/utils';
import { compareVersion } from '$lib/utils';
import SettingsModal from '$lib/components/chat/SettingsModal.svelte';
import Sidebar from '$lib/components/layout/Sidebar.svelte';
@@ -79,7 +79,7 @@
ollamaVersion = version;
console.log(ollamaVersion);
if (checkVersion(REQUIRED_OLLAMA_VERSION, ollamaVersion)) {
if (compareVersion(REQUIRED_OLLAMA_VERSION, ollamaVersion)) {
toast.error(`Ollama Version: ${ollamaVersion !== '' ? ollamaVersion : 'Not Detected'}`);
}
};