feat: changelog.md

This commit is contained in:
Timothy J. Baek 2024-02-23 00:30:26 -08:00
parent 9b27d952f8
commit 9f950aea9c
13 changed files with 237 additions and 154 deletions

33
CHANGELOG.md Normal file
View file

@ -0,0 +1,33 @@
# 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

View file

@ -23,6 +23,14 @@ except ImportError:
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
####################################

View file

@ -1,5 +1,9 @@
from bs4 import BeautifulSoup
import json
import markdown
import time
from fastapi import FastAPI, Request
from fastapi.staticfiles import StaticFiles
from fastapi import HTTPException
@ -16,7 +20,7 @@ from apps.rag.main import app as rag_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):
@ -65,14 +69,85 @@ app.mount("/rag/api/v1", rag_app)
@app.get("/api/config")
async def get_app_config():
return {
"status": True,
"version": VERSION,
"images": images_app.state.ENABLED,
"default_models": webui_app.state.DEFAULT_MODELS,
"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(
"/",
SPAStaticFiles(directory=FRONTEND_BUILD_DIR, html=True),

View file

@ -1,6 +1,6 @@
{
"name": "open-webui",
"version": "v1.0.0-alpha.101",
"version": "0.1.102",
"private": true,
"scripts": {
"dev": "vite dev --host",

View file

@ -21,3 +21,25 @@ export const getBackendConfig = async () => {
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;
};

View file

@ -0,0 +1,90 @@
<script lang="ts">
import Modal from './common/Modal.svelte';
import { Confetti } from 'svelte-confetti';
import { WEBUI_NAME, WEB_UI_VERSION } from '$lib/constants';
import { onMount } from 'svelte';
import { getChangelog } from '$lib/apis';
export let show = false;
let changelog = null;
onMount(async () => {
const res = await getChangelog();
changelog = res;
});
</script>
<Modal bind:show>
<div class="px-5 py-4 dark:text-gray-300">
<div class="flex justify-between items-start">
<div class="text-xl font-bold">
{WEBUI_NAME}
<!-- <Confetti x={[-1, -0.25]} y={[0, 0.5]} /> -->
</div>
<button
class="self-center"
on:click={() => {
show = false;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-5 h-5"
>
<path
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
/>
</svg>
</button>
</div>
<div class=" pb-3 flex items-center mt-2">
<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="text-sm dark:text-gray-200">
v{WEB_UI_VERSION}
</div>
</div>
<hr class=" dark:border-gray-800" />
<div class=" overflow-y-scroll max-h-80">
<div class="my-3">
{#if changelog}
{#each Object.keys(changelog) as version}
<div class="font-bold text-xl mb-1">
v{version} - {changelog[version].date}
</div>
{#each Object.keys(changelog[version]).filter((section) => section !== 'date') as section}
<div class="text-lg">
<div class="font-bold capitalize">{section}</div>
<div class="my-2">
{#each Object.keys(changelog[version][section]) as item}
<div class="text-sm mb-2">
<div class="font-semibold">
{changelog[version][section][item].title}
</div>
<div class="my-1.5">{changelog[version][section][item].content}</div>
</div>
{/each}
</div>
</div>
{/each}
{/each}
{/if}
</div>
</div>
<div class="flex justify-end pt-3 text-sm font-medium">
<button
on:click={() => {
show = false;
}}
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
>
<span class="relative">Ok, let's go!</span>
</button>
</div>
</div>
</Modal>

View file

@ -1,7 +1,7 @@
<script lang="ts">
import { getBackendConfig } from '$lib/apis';
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 toast from 'svelte-french-toast';
const dispatch = createEventDispatcher();
@ -18,7 +18,6 @@
// Interface
let promptSuggestions = [];
let showUsername = false;
let enableWhatsChanged = true;
const toggleFullScreenMode = async () => {
fullScreenMode = !fullScreenMode;
@ -30,14 +29,6 @@
saveSettings({ showUsername: showUsername });
};
const toggleenableWhatsChanged = async () => {
enableWhatsChanged = !enableWhatsChanged;
if (enableWhatsChanged) {
showWhatsChanged.update((value) => true);
}
saveSettings({ enableWhatsChanged: enableWhatsChanged });
};
const toggleTitleAutoGenerate = async () => {
titleAutoGenerate = !titleAutoGenerate;
saveSettings({ titleAutoGenerate: titleAutoGenerate });
@ -86,7 +77,6 @@
titleAutoGenerate = settings.titleAutoGenerate ?? true;
responseAutoCopy = settings.responseAutoCopy ?? false;
showUsername = settings.showUsername ?? false;
enableWhatsChanged = settings.enableWhatsChanged ?? true;
fullScreenMode = settings.fullScreenMode ?? false;
titleAutoGenerateModel = settings.titleAutoGenerateModel ?? '';
titleGenerationPrompt =
@ -187,25 +177,6 @@
</button>
</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>
<hr class=" dark:border-gray-700" />

View file

@ -1,79 +0,0 @@
<script lang="ts">
import Modal from '../common/Modal.svelte';
import { Confetti } from 'svelte-confetti';
import { WEBUI_NAME, WEB_UI_VERSION, RELEASE_NOTES } from '$lib/constants';
import { config, showWhatsChanged } from '$lib/stores';
export let show = false;
function toggleVisibility() {
showWhatsChanged.update((value) => !value);
}
function handleClick() {
toggleVisibility();
}
let hasValidNotes = Array.isArray(RELEASE_NOTES) && RELEASE_NOTES.length > 0;
</script>
<Modal bind:show>
<div class="px-5 py-4 dark:text-gray-300">
<div class="flex justify-between items-start">
<div class="text-xl font-bold">
{WEBUI_NAME}
<Confetti x={[-1, -0.25]} y={[0, 0.5]} />
</div>
<button class="self-center" on:click={handleClick}>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-5 h-5"
>
<path
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
/>
</svg>
</button>
</div>
<div class=" pb-3 flex items-center mt-2">
<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="text-sm dark:text-gray-200">
{$config && $config.version ? $config.version : WEB_UI_VERSION}
</div>
</div>
<hr class=" dark:border-gray-800" />
<div class="p-4 overflow-y-scroll max-h-80">
{#if !hasValidNotes}
<div class="pt-10 text-center font-bold">There are no notes given.</div>
<div class="pb-10 text-center">
Check
<a class="text-blue-500" href="https://github.com/open-webui/open-webui" target="_blank">
Open WebUI on GitHub</a
> for more information.
</div>
{:else}
{#each RELEASE_NOTES as { title, description }}
<div class="mt-4">
<div class="font-bold">{title}</div>
<div>{description}</div>
</div>
{/each}
{/if}
</div>
<div class="m-4 flex justify-end pt-3 text-sm font-medium">
<button
on:click={handleClick}
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"
>
<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>
</button>
</div>
</div>
</Modal>

View file

@ -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 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 SUPPORTED_FILE_TYPE = [

View file

@ -32,16 +32,3 @@ export const documents = writable([
export const settings = writable({});
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);

View file

@ -31,6 +31,7 @@
import ShortcutsModal from '$lib/components/chat/ShortcutsModal.svelte';
import { getDocs } from '$lib/apis/documents';
import { getAllChatTags } from '$lib/apis/chats';
import ChangelogModal from '$lib/components/ChangelogModal.svelte';
let ollamaVersion = '';
let loaded = false;
@ -348,6 +349,8 @@
</div>
</div>
</div>
{:else}
<ChangelogModal show={true} />
{/if}
<div

View file

@ -14,7 +14,6 @@
chats,
chatId,
config,
showWhatsChanged,
tags as _tags
} from '$lib/stores';
import { copyToClipboard, splitStream } from '$lib/utils';
@ -36,7 +35,6 @@
import Messages from '$lib/components/chat/Messages.svelte';
import ModelSelector from '$lib/components/chat/ModelSelector.svelte';
import Navbar from '$lib/components/layout/Navbar.svelte';
import WhatsChangedModal from '$lib/components/chat//WhatsChangedModal.svelte';
import { RAGTemplate } from '$lib/utils/rag';
let stopResponseFlag = false;
@ -799,9 +797,6 @@
</script>
<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} />
<div class="flex flex-col flex-auto">
<div

View file

@ -5,5 +5,6 @@ export default defineConfig({
plugins: [sveltekit()],
define: {
APP_VERSION: JSON.stringify(process.env.npm_package_version)
}
},
assetsInclude: ['**/*.md']
});