Commit 9f950aea authored by Timothy J. Baek's avatar Timothy J. Baek
Browse files

feat: changelog.md

parent 9b27d952
# Changelog
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.102] - 2024-02-22
### Added
- **🖼️ Image Generation**: Generate Images using the stable-difusion-webui API. You can set this up in settings -> images.
- **📝 Change title generation prompt**: Change the promt used to generate titles for your chats. You can set this up in the settings -> interface.
- **🤖 Change embedding model**: Change the embedding model used to generate embeddings for your chats in the Dockerfile. Use any sentence transformer model from huggingface.co.
- **📢 CHANGELOG.md/Popup**: This popup will show you the latest changes. You can edit it in the constants.ts file.
### Fixed
- X, Y, and Z
### Changed
- X, Y, and Z
### Removed
- X, Y, and Z
## [0.1.101] - 2024-02-21
### Added
- X, Y, and Z
...@@ -23,6 +23,14 @@ except ImportError: ...@@ -23,6 +23,14 @@ except ImportError:
ENV = os.environ.get("ENV", "dev") ENV = os.environ.get("ENV", "dev")
try:
with open(f"../package.json", "r") as f:
PACKAGE_DATA = json.load(f)
except:
PACKAGE_DATA = {"version": "0.0.0"}
VERSION = PACKAGE_DATA["version"]
#################################### ####################################
# DATA/FRONTEND BUILD DIR # DATA/FRONTEND BUILD DIR
#################################### ####################################
......
from bs4 import BeautifulSoup
import json
import markdown
import time import time
from fastapi import FastAPI, Request from fastapi import FastAPI, Request
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from fastapi import HTTPException from fastapi import HTTPException
...@@ -16,7 +20,7 @@ from apps.rag.main import app as rag_app ...@@ -16,7 +20,7 @@ from apps.rag.main import app as rag_app
from apps.web.main import app as webui_app from apps.web.main import app as webui_app
from config import ENV, FRONTEND_BUILD_DIR from config import ENV, VERSION, FRONTEND_BUILD_DIR
class SPAStaticFiles(StaticFiles): class SPAStaticFiles(StaticFiles):
...@@ -65,14 +69,85 @@ app.mount("/rag/api/v1", rag_app) ...@@ -65,14 +69,85 @@ app.mount("/rag/api/v1", rag_app)
@app.get("/api/config") @app.get("/api/config")
async def get_app_config(): async def get_app_config():
return { return {
"status": True, "status": True,
"version": VERSION,
"images": images_app.state.ENABLED, "images": images_app.state.ENABLED,
"default_models": webui_app.state.DEFAULT_MODELS, "default_models": webui_app.state.DEFAULT_MODELS,
"default_prompt_suggestions": webui_app.state.DEFAULT_PROMPT_SUGGESTIONS, "default_prompt_suggestions": webui_app.state.DEFAULT_PROMPT_SUGGESTIONS,
} }
# Function to parse each section
def parse_section(section):
items = []
for li in section.find_all("li"):
# Extract raw HTML string
raw_html = str(li)
# Extract text without HTML tags
text = li.get_text(separator=" ", strip=True)
# Split into title and content
parts = text.split(": ", 1)
title = parts[0].strip() if len(parts) > 1 else ""
content = parts[1].strip() if len(parts) > 1 else text
items.append({"title": title, "content": content, "raw": raw_html})
return items
@app.get("/api/changelog")
async def get_app_changelog():
try:
with open("../CHANGELOG.md", "r") as file:
changelog_content = file.read()
# Convert markdown content to HTML
html_content = markdown.markdown(changelog_content)
# Parse the HTML content
soup = BeautifulSoup(html_content, "html.parser")
print(soup)
# Initialize JSON structure
changelog_json = {}
# Iterate over each version
for version in soup.find_all("h2"):
version_number = (
version.get_text().strip().split(" - ")[0][1:-1]
) # Remove brackets
date = version.get_text().strip().split(" - ")[1]
version_data = {"date": date}
# Find the next sibling that is a h3 tag (section title)
current = version.find_next_sibling()
print(current)
while current and current.name != "h2":
if current.name == "h3":
section_title = current.get_text().lower() # e.g., "added", "fixed"
section_items = parse_section(current.find_next_sibling("ul"))
version_data[section_title] = section_items
# Move to the next element
current = current.find_next_sibling()
changelog_json[version_number] = version_data
# print(changelog_json)
# Return content as JSON string
return changelog_json
except FileNotFoundError:
return {"error": "readme.md not found"}
except Exception as e:
return {"error": f"An error occurred: {e}"}
app.mount( app.mount(
"/", "/",
SPAStaticFiles(directory=FRONTEND_BUILD_DIR, html=True), SPAStaticFiles(directory=FRONTEND_BUILD_DIR, html=True),
......
{ {
"name": "open-webui", "name": "open-webui",
"version": "v1.0.0-alpha.101", "version": "0.1.102",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite dev --host", "dev": "vite dev --host",
...@@ -53,4 +53,4 @@ ...@@ -53,4 +53,4 @@
"tippy.js": "^6.3.7", "tippy.js": "^6.3.7",
"uuid": "^9.0.1" "uuid": "^9.0.1"
} }
} }
\ No newline at end of file
...@@ -21,3 +21,25 @@ export const getBackendConfig = async () => { ...@@ -21,3 +21,25 @@ export const getBackendConfig = async () => {
return res; return res;
}; };
export const getChangelog = async () => {
let error = null;
const res = await fetch(`${WEBUI_BASE_URL}/api/changelog`, {
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;
});
return res;
};
<script lang="ts"> <script lang="ts">
import Modal from '../common/Modal.svelte'; import Modal from './common/Modal.svelte';
import { Confetti } from 'svelte-confetti'; import { Confetti } from 'svelte-confetti';
import { WEBUI_NAME, WEB_UI_VERSION, RELEASE_NOTES } from '$lib/constants'; import { WEBUI_NAME, WEB_UI_VERSION } from '$lib/constants';
import { config, showWhatsChanged } from '$lib/stores'; import { onMount } from 'svelte';
import { getChangelog } from '$lib/apis';
export let show = false; export let show = false;
function toggleVisibility() { let changelog = null;
showWhatsChanged.update((value) => !value);
}
function handleClick() { onMount(async () => {
toggleVisibility(); const res = await getChangelog();
} changelog = res;
});
let hasValidNotes = Array.isArray(RELEASE_NOTES) && RELEASE_NOTES.length > 0;
</script> </script>
<Modal bind:show> <Modal bind:show>
...@@ -22,9 +20,14 @@ ...@@ -22,9 +20,14 @@
<div class="flex justify-between items-start"> <div class="flex justify-between items-start">
<div class="text-xl font-bold"> <div class="text-xl font-bold">
{WEBUI_NAME} {WEBUI_NAME}
<Confetti x={[-1, -0.25]} y={[0, 0.5]} /> <!-- <Confetti x={[-1, -0.25]} y={[0, 0.5]} /> -->
</div> </div>
<button class="self-center" on:click={handleClick}> <button
class="self-center"
on:click={() => {
show = false;
}}
>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20" viewBox="0 0 20 20"
...@@ -41,37 +44,45 @@ ...@@ -41,37 +44,45 @@
<div class="text-sm dark:text-gray-200">Release Notes</div> <div class="text-sm dark:text-gray-200">Release Notes</div>
<div class="flex self-center w-[1px] h-6 mx-2.5 bg-gray-200 dark:bg-gray-700" /> <div class="flex self-center w-[1px] h-6 mx-2.5 bg-gray-200 dark:bg-gray-700" />
<div class="text-sm dark:text-gray-200"> <div class="text-sm dark:text-gray-200">
{$config && $config.version ? $config.version : WEB_UI_VERSION} v{WEB_UI_VERSION}
</div> </div>
</div> </div>
<hr class=" dark:border-gray-800" /> <hr class=" dark:border-gray-800" />
<div class="p-4 overflow-y-scroll max-h-80"> <div class=" overflow-y-scroll max-h-80">
{#if !hasValidNotes} <div class="my-3">
<div class="pt-10 text-center font-bold">There are no notes given.</div> {#if changelog}
{#each Object.keys(changelog) as version}
<div class="font-bold text-xl mb-1">
v{version} - {changelog[version].date}
</div>
<div class="pb-10 text-center"> {#each Object.keys(changelog[version]).filter((section) => section !== 'date') as section}
Check <div class="text-lg">
<a class="text-blue-500" href="https://github.com/open-webui/open-webui" target="_blank"> <div class="font-bold capitalize">{section}</div>
Open WebUI on GitHub</a
> for more information. <div class="my-2">
</div> {#each Object.keys(changelog[version][section]) as item}
{:else} <div class="text-sm mb-2">
{#each RELEASE_NOTES as { title, description }} <div class="font-semibold">
<div class="mt-4"> {changelog[version][section][item].title}
<div class="font-bold">{title}</div> </div>
<div>{description}</div> <div class="my-1.5">{changelog[version][section][item].content}</div>
</div> </div>
{/each} {/each}
{/if} </div>
</div>
{/each}
{/each}
{/if}
</div>
</div> </div>
<div class="m-4 flex justify-end pt-3 text-sm font-medium"> <div class="flex justify-end pt-3 text-sm font-medium">
<button <button
on:click={handleClick} on:click={() => {
class=" rounded px-4 py-2 overflow-hidden group bg-green-600 relative hover:bg-gradient-to-r hover:from-green-600 hover:to-green-500 text-white hover:ring-2 hover:ring-offset-2 hover:ring-green-500 transition-all ease-out duration-300" show = false;
}}
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
> >
<span
class="absolute right-0 w-8 h-32 -mt-12 transition-all duration-1000 transform translate-x-12 bg-white opacity-10 rotate-12 group-hover:-translate-x-40 ease"
/>
<span class="relative">Ok, let's go!</span> <span class="relative">Ok, let's go!</span>
</button> </button>
</div> </div>
......
<script lang="ts"> <script lang="ts">
import { getBackendConfig } from '$lib/apis'; import { getBackendConfig } from '$lib/apis';
import { setDefaultPromptSuggestions } from '$lib/apis/configs'; import { setDefaultPromptSuggestions } from '$lib/apis/configs';
import { config, models, user, showWhatsChanged } from '$lib/stores'; import { config, models, user } from '$lib/stores';
import { createEventDispatcher, onMount } from 'svelte'; import { createEventDispatcher, onMount } from 'svelte';
import toast from 'svelte-french-toast'; import toast from 'svelte-french-toast';
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
// Interface // Interface
let promptSuggestions = []; let promptSuggestions = [];
let showUsername = false; let showUsername = false;
let enableWhatsChanged = true;
const toggleFullScreenMode = async () => { const toggleFullScreenMode = async () => {
fullScreenMode = !fullScreenMode; fullScreenMode = !fullScreenMode;
...@@ -30,14 +29,6 @@ ...@@ -30,14 +29,6 @@
saveSettings({ showUsername: showUsername }); saveSettings({ showUsername: showUsername });
}; };
const toggleenableWhatsChanged = async () => {
enableWhatsChanged = !enableWhatsChanged;
if (enableWhatsChanged) {
showWhatsChanged.update((value) => true);
}
saveSettings({ enableWhatsChanged: enableWhatsChanged });
};
const toggleTitleAutoGenerate = async () => { const toggleTitleAutoGenerate = async () => {
titleAutoGenerate = !titleAutoGenerate; titleAutoGenerate = !titleAutoGenerate;
saveSettings({ titleAutoGenerate: titleAutoGenerate }); saveSettings({ titleAutoGenerate: titleAutoGenerate });
...@@ -86,7 +77,6 @@ ...@@ -86,7 +77,6 @@
titleAutoGenerate = settings.titleAutoGenerate ?? true; titleAutoGenerate = settings.titleAutoGenerate ?? true;
responseAutoCopy = settings.responseAutoCopy ?? false; responseAutoCopy = settings.responseAutoCopy ?? false;
showUsername = settings.showUsername ?? false; showUsername = settings.showUsername ?? false;
enableWhatsChanged = settings.enableWhatsChanged ?? true;
fullScreenMode = settings.fullScreenMode ?? false; fullScreenMode = settings.fullScreenMode ?? false;
titleAutoGenerateModel = settings.titleAutoGenerateModel ?? ''; titleAutoGenerateModel = settings.titleAutoGenerateModel ?? '';
titleGenerationPrompt = titleGenerationPrompt =
...@@ -187,25 +177,6 @@ ...@@ -187,25 +177,6 @@
</button> </button>
</div> </div>
</div> </div>
<div>
<div class=" py-0.5 flex w-full justify-between">
<div class=" self-center text-xs font-medium">Show "WhatsChanged" Modal on Startup</div>
<button
class="p-1 px-3 text-xs flex rounded transition"
on:click={() => {
toggleenableWhatsChanged();
}}
type="button"
>
{#if enableWhatsChanged === true}
<span class="ml-2 self-center">On</span>
{:else}
<span class="ml-2 self-center">Off</span>
{/if}
</button>
</div>
</div>
</div> </div>
<hr class=" dark:border-gray-700" /> <hr class=" dark:border-gray-700" />
......
...@@ -12,29 +12,6 @@ export const IMAGES_API_BASE_URL = `${WEBUI_BASE_URL}/images/api/v1`; ...@@ -12,29 +12,6 @@ export const IMAGES_API_BASE_URL = `${WEBUI_BASE_URL}/images/api/v1`;
export const RAG_API_BASE_URL = `${WEBUI_BASE_URL}/rag/api/v1`; export const RAG_API_BASE_URL = `${WEBUI_BASE_URL}/rag/api/v1`;
export const WEB_UI_VERSION = APP_VERSION; export const WEB_UI_VERSION = APP_VERSION;
export const RELEASE_NOTES = [
{
title: ' 🖼️ Image Generation',
description:
'Generate Images using the stable-difusion-webui API. You can set this up in settings -> images.'
},
{
title: ' 📝 Change title generation prompt',
description:
'Change the promt used to generate titles for your chats. You can set this up in the settings -> interface.'
},
{
title: ' 🤖 Change embedding model',
description:
'Change the embedding model used to generate embeddings for your chats in the Dockerfile. Use any sentence transformer model from huggingface.co.'
},
{
title: ' 📢 This Whats Changed Popup',
description:
'This popup will show you the latest changes. You can edit it in the constants.ts file.'
}
//...
];
export const REQUIRED_OLLAMA_VERSION = '0.1.16'; export const REQUIRED_OLLAMA_VERSION = '0.1.16';
export const SUPPORTED_FILE_TYPE = [ export const SUPPORTED_FILE_TYPE = [
......
...@@ -32,16 +32,3 @@ export const documents = writable([ ...@@ -32,16 +32,3 @@ export const documents = writable([
export const settings = writable({}); export const settings = writable({});
export const showSettings = writable(false); export const showSettings = writable(false);
function createLocalStorageStore(key, startValue) {
const storedValue = localStorage.getItem(key);
const initialValue = storedValue ? JSON.parse(storedValue) : startValue;
const store = writable(initialValue);
store.subscribe((value) => {
localStorage.setItem(key, JSON.stringify(value));
});
return store;
}
export const showWhatsChanged = createLocalStorageStore('showWhatsChanged', true);
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
import ShortcutsModal from '$lib/components/chat/ShortcutsModal.svelte'; import ShortcutsModal from '$lib/components/chat/ShortcutsModal.svelte';
import { getDocs } from '$lib/apis/documents'; import { getDocs } from '$lib/apis/documents';
import { getAllChatTags } from '$lib/apis/chats'; import { getAllChatTags } from '$lib/apis/chats';
import ChangelogModal from '$lib/components/ChangelogModal.svelte';
let ollamaVersion = ''; let ollamaVersion = '';
let loaded = false; let loaded = false;
...@@ -348,6 +349,8 @@ ...@@ -348,6 +349,8 @@
</div> </div>
</div> </div>
</div> </div>
{:else}
<ChangelogModal show={true} />
{/if} {/if}
<div <div
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
chats, chats,
chatId, chatId,
config, config,
showWhatsChanged,
tags as _tags tags as _tags
} from '$lib/stores'; } from '$lib/stores';
import { copyToClipboard, splitStream } from '$lib/utils'; import { copyToClipboard, splitStream } from '$lib/utils';
...@@ -36,7 +35,6 @@ ...@@ -36,7 +35,6 @@
import Messages from '$lib/components/chat/Messages.svelte'; import Messages from '$lib/components/chat/Messages.svelte';
import ModelSelector from '$lib/components/chat/ModelSelector.svelte'; import ModelSelector from '$lib/components/chat/ModelSelector.svelte';
import Navbar from '$lib/components/layout/Navbar.svelte'; import Navbar from '$lib/components/layout/Navbar.svelte';
import WhatsChangedModal from '$lib/components/chat//WhatsChangedModal.svelte';
import { RAGTemplate } from '$lib/utils/rag'; import { RAGTemplate } from '$lib/utils/rag';
let stopResponseFlag = false; let stopResponseFlag = false;
...@@ -799,9 +797,6 @@ ...@@ -799,9 +797,6 @@
</script> </script>
<div class="h-screen max-h-[100dvh] w-full flex flex-col"> <div class="h-screen max-h-[100dvh] w-full flex flex-col">
{#if $showWhatsChanged && !['pending'].includes($user.role) && $settings.enableWhatsChanged}
<WhatsChangedModal show={true} />
{/if}
<Navbar {title} shareEnabled={messages.length > 0} {initNewChat} {tags} {addTag} {deleteTag} /> <Navbar {title} shareEnabled={messages.length > 0} {initNewChat} {tags} {addTag} {deleteTag} />
<div class="flex flex-col flex-auto"> <div class="flex flex-col flex-auto">
<div <div
......
...@@ -5,5 +5,6 @@ export default defineConfig({ ...@@ -5,5 +5,6 @@ export default defineConfig({
plugins: [sveltekit()], plugins: [sveltekit()],
define: { define: {
APP_VERSION: JSON.stringify(process.env.npm_package_version) APP_VERSION: JSON.stringify(process.env.npm_package_version)
} },
assetsInclude: ['**/*.md']
}); });
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment