feat: save user settings to db
This commit is contained in:
		
							parent
							
								
									9d4c07b76a
								
							
						
					
					
						commit
						ccbafca74c
					
				|  | @ -198,7 +198,7 @@ async def fetch_url(url, key): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def merge_models_lists(model_lists): | def merge_models_lists(model_lists): | ||||||
|     log.info(f"merge_models_lists {model_lists}") |     log.debug(f"merge_models_lists {model_lists}") | ||||||
|     merged_list = [] |     merged_list = [] | ||||||
| 
 | 
 | ||||||
|     for idx, models in enumerate(model_lists): |     for idx, models in enumerate(model_lists): | ||||||
|  | @ -237,7 +237,7 @@ async def get_all_models(): | ||||||
|         ] |         ] | ||||||
| 
 | 
 | ||||||
|         responses = await asyncio.gather(*tasks) |         responses = await asyncio.gather(*tasks) | ||||||
|         log.info(f"get_all_models:responses() {responses}") |         log.debug(f"get_all_models:responses() {responses}") | ||||||
| 
 | 
 | ||||||
|         models = { |         models = { | ||||||
|             "data": merge_models_lists( |             "data": merge_models_lists( | ||||||
|  | @ -254,7 +254,7 @@ async def get_all_models(): | ||||||
|             ) |             ) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         log.info(f"models: {models}") |         log.debug(f"models: {models}") | ||||||
|         app.state.MODELS = {model["id"]: model for model in models["data"]} |         app.state.MODELS = {model["id"]: model for model in models["data"]} | ||||||
| 
 | 
 | ||||||
|     return models |     return models | ||||||
|  |  | ||||||
|  | @ -0,0 +1,48 @@ | ||||||
|  | """Peewee migrations -- 002_add_local_sharing.py. | ||||||
|  | 
 | ||||||
|  | Some examples (model - class or model name):: | ||||||
|  | 
 | ||||||
|  |     > Model = migrator.orm['table_name']            # Return model in current state by name | ||||||
|  |     > Model = migrator.ModelClass                   # Return model in current state by name | ||||||
|  | 
 | ||||||
|  |     > migrator.sql(sql)                             # Run custom SQL | ||||||
|  |     > migrator.run(func, *args, **kwargs)           # Run python function with the given args | ||||||
|  |     > migrator.create_model(Model)                  # Create a model (could be used as decorator) | ||||||
|  |     > migrator.remove_model(model, cascade=True)    # Remove a model | ||||||
|  |     > migrator.add_fields(model, **fields)          # Add fields to a model | ||||||
|  |     > migrator.change_fields(model, **fields)       # Change fields | ||||||
|  |     > migrator.remove_fields(model, *field_names, cascade=True) | ||||||
|  |     > migrator.rename_field(model, old_field_name, new_field_name) | ||||||
|  |     > migrator.rename_table(model, new_table_name) | ||||||
|  |     > migrator.add_index(model, *col_names, unique=False) | ||||||
|  |     > migrator.add_not_null(model, *field_names) | ||||||
|  |     > migrator.add_default(model, field_name, default) | ||||||
|  |     > migrator.add_constraint(model, name, sql) | ||||||
|  |     > migrator.drop_index(model, *col_names) | ||||||
|  |     > migrator.drop_not_null(model, *field_names) | ||||||
|  |     > migrator.drop_constraints(model, *constraints) | ||||||
|  | 
 | ||||||
|  | """ | ||||||
|  | 
 | ||||||
|  | from contextlib import suppress | ||||||
|  | 
 | ||||||
|  | import peewee as pw | ||||||
|  | from peewee_migrate import Migrator | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | with suppress(ImportError): | ||||||
|  |     import playhouse.postgres_ext as pw_pext | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def migrate(migrator: Migrator, database: pw.Database, *, fake=False): | ||||||
|  |     """Write your migrations here.""" | ||||||
|  | 
 | ||||||
|  |     # Adding fields settings to the 'user' table | ||||||
|  |     migrator.add_fields("user", settings=pw.TextField(null=True)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def rollback(migrator: Migrator, database: pw.Database, *, fake=False): | ||||||
|  |     """Write your rollback migrations here.""" | ||||||
|  | 
 | ||||||
|  |     # Remove the settings field | ||||||
|  |     migrator.remove_fields("user", "settings") | ||||||
|  | @ -1,11 +1,11 @@ | ||||||
| from pydantic import BaseModel | from pydantic import BaseModel, ConfigDict | ||||||
| from peewee import * | from peewee import * | ||||||
| from playhouse.shortcuts import model_to_dict | from playhouse.shortcuts import model_to_dict | ||||||
| from typing import List, Union, Optional | from typing import List, Union, Optional | ||||||
| import time | import time | ||||||
| from utils.misc import get_gravatar_url | from utils.misc import get_gravatar_url | ||||||
| 
 | 
 | ||||||
| from apps.webui.internal.db import DB | from apps.webui.internal.db import DB, JSONField | ||||||
| from apps.webui.models.chats import Chats | from apps.webui.models.chats import Chats | ||||||
| 
 | 
 | ||||||
| #################### | #################### | ||||||
|  | @ -25,11 +25,18 @@ class User(Model): | ||||||
|     created_at = BigIntegerField() |     created_at = BigIntegerField() | ||||||
| 
 | 
 | ||||||
|     api_key = CharField(null=True, unique=True) |     api_key = CharField(null=True, unique=True) | ||||||
|  |     settings = JSONField(null=True) | ||||||
| 
 | 
 | ||||||
|     class Meta: |     class Meta: | ||||||
|         database = DB |         database = DB | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | class UserSettings(BaseModel): | ||||||
|  |     ui: Optional[dict] = {} | ||||||
|  |     model_config = ConfigDict(extra="allow") | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class UserModel(BaseModel): | class UserModel(BaseModel): | ||||||
|     id: str |     id: str | ||||||
|     name: str |     name: str | ||||||
|  | @ -42,6 +49,7 @@ class UserModel(BaseModel): | ||||||
|     created_at: int  # timestamp in epoch |     created_at: int  # timestamp in epoch | ||||||
| 
 | 
 | ||||||
|     api_key: Optional[str] = None |     api_key: Optional[str] = None | ||||||
|  |     settings: Optional[UserSettings] = None | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #################### | #################### | ||||||
|  |  | ||||||
|  | @ -9,7 +9,13 @@ import time | ||||||
| import uuid | import uuid | ||||||
| import logging | import logging | ||||||
| 
 | 
 | ||||||
| from apps.webui.models.users import UserModel, UserUpdateForm, UserRoleUpdateForm, Users | from apps.webui.models.users import ( | ||||||
|  |     UserModel, | ||||||
|  |     UserUpdateForm, | ||||||
|  |     UserRoleUpdateForm, | ||||||
|  |     UserSettings, | ||||||
|  |     Users, | ||||||
|  | ) | ||||||
| from apps.webui.models.auths import Auths | from apps.webui.models.auths import Auths | ||||||
| from apps.webui.models.chats import Chats | from apps.webui.models.chats import Chats | ||||||
| 
 | 
 | ||||||
|  | @ -68,6 +74,42 @@ async def update_user_role(form_data: UserRoleUpdateForm, user=Depends(get_admin | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | ############################ | ||||||
|  | # GetUserSettingsBySessionUser | ||||||
|  | ############################ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @router.get("/user/settings", response_model=Optional[UserSettings]) | ||||||
|  | async def get_user_settings_by_session_user(user=Depends(get_verified_user)): | ||||||
|  |     user = Users.get_user_by_id(user.id) | ||||||
|  |     if user: | ||||||
|  |         return user.settings | ||||||
|  |     else: | ||||||
|  |         raise HTTPException( | ||||||
|  |             status_code=status.HTTP_400_BAD_REQUEST, | ||||||
|  |             detail=ERROR_MESSAGES.USER_NOT_FOUND, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ############################ | ||||||
|  | # UpdateUserSettingsBySessionUser | ||||||
|  | ############################ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @router.post("/user/settings/update", response_model=UserSettings) | ||||||
|  | async def update_user_settings_by_session_user( | ||||||
|  |     form_data: UserSettings, user=Depends(get_verified_user) | ||||||
|  | ): | ||||||
|  |     user = Users.update_user_by_id(user.id, {"settings": form_data.model_dump()}) | ||||||
|  |     if user: | ||||||
|  |         return user.settings | ||||||
|  |     else: | ||||||
|  |         raise HTTPException( | ||||||
|  |             status_code=status.HTTP_400_BAD_REQUEST, | ||||||
|  |             detail=ERROR_MESSAGES.USER_NOT_FOUND, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| ############################ | ############################ | ||||||
| # GetUserById | # GetUserById | ||||||
| ############################ | ############################ | ||||||
|  | @ -81,6 +123,8 @@ class UserResponse(BaseModel): | ||||||
| @router.get("/{user_id}", response_model=UserResponse) | @router.get("/{user_id}", response_model=UserResponse) | ||||||
| async def get_user_by_id(user_id: str, user=Depends(get_verified_user)): | async def get_user_by_id(user_id: str, user=Depends(get_verified_user)): | ||||||
| 
 | 
 | ||||||
|  |     # Check if user_id is a shared chat | ||||||
|  |     # If it is, get the user_id from the chat | ||||||
|     if user_id.startswith("shared-"): |     if user_id.startswith("shared-"): | ||||||
|         chat_id = user_id.replace("shared-", "") |         chat_id = user_id.replace("shared-", "") | ||||||
|         chat = Chats.get_chat_by_id(chat_id) |         chat = Chats.get_chat_by_id(chat_id) | ||||||
|  |  | ||||||
|  | @ -115,6 +115,62 @@ export const getUsers = async (token: string) => { | ||||||
| 	return res ? res : []; | 	return res ? res : []; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | export const getUserSettings = async (token: string) => { | ||||||
|  | 	let error = null; | ||||||
|  | 	const res = await fetch(`${WEBUI_API_BASE_URL}/users/user/settings`, { | ||||||
|  | 		method: 'GET', | ||||||
|  | 		headers: { | ||||||
|  | 			'Content-Type': 'application/json', | ||||||
|  | 			Authorization: `Bearer ${token}` | ||||||
|  | 		} | ||||||
|  | 	}) | ||||||
|  | 		.then(async (res) => { | ||||||
|  | 			if (!res.ok) throw await res.json(); | ||||||
|  | 			return res.json(); | ||||||
|  | 		}) | ||||||
|  | 		.catch((err) => { | ||||||
|  | 			console.log(err); | ||||||
|  | 			error = err.detail; | ||||||
|  | 			return null; | ||||||
|  | 		}); | ||||||
|  | 
 | ||||||
|  | 	if (error) { | ||||||
|  | 		throw error; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return res; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export const updateUserSettings = async (token: string, settings: object) => { | ||||||
|  | 	let error = null; | ||||||
|  | 
 | ||||||
|  | 	const res = await fetch(`${WEBUI_API_BASE_URL}/users/user/settings/update`, { | ||||||
|  | 		method: 'POST', | ||||||
|  | 		headers: { | ||||||
|  | 			'Content-Type': 'application/json', | ||||||
|  | 			Authorization: `Bearer ${token}` | ||||||
|  | 		}, | ||||||
|  | 		body: JSON.stringify({ | ||||||
|  | 			...settings | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | 		.then(async (res) => { | ||||||
|  | 			if (!res.ok) throw await res.json(); | ||||||
|  | 			return res.json(); | ||||||
|  | 		}) | ||||||
|  | 		.catch((err) => { | ||||||
|  | 			console.log(err); | ||||||
|  | 			error = err.detail; | ||||||
|  | 			return null; | ||||||
|  | 		}); | ||||||
|  | 
 | ||||||
|  | 	if (error) { | ||||||
|  | 		throw error; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return res; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| export const getUserById = async (token: string, userId: string) => { | export const getUserById = async (token: string, userId: string) => { | ||||||
| 	let error = null; | 	let error = null; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -42,6 +42,7 @@ | ||||||
| 	import type { Writable } from 'svelte/store'; | 	import type { Writable } from 'svelte/store'; | ||||||
| 	import type { i18n as i18nType } from 'i18next'; | 	import type { i18n as i18nType } from 'i18next'; | ||||||
| 	import Banner from '../common/Banner.svelte'; | 	import Banner from '../common/Banner.svelte'; | ||||||
|  | 	import { getUserSettings } from '$lib/apis/users'; | ||||||
| 
 | 
 | ||||||
| 	const i18n: Writable<i18nType> = getContext('i18n'); | 	const i18n: Writable<i18nType> = getContext('i18n'); | ||||||
| 
 | 
 | ||||||
|  | @ -154,10 +155,13 @@ | ||||||
| 			$models.map((m) => m.id).includes(modelId) ? modelId : '' | 			$models.map((m) => m.id).includes(modelId) ? modelId : '' | ||||||
| 		); | 		); | ||||||
| 
 | 
 | ||||||
| 		let _settings = JSON.parse(localStorage.getItem('settings') ?? '{}'); | 		const userSettings = await getUserSettings(localStorage.token); | ||||||
| 		settings.set({ | 
 | ||||||
| 			..._settings | 		if (userSettings) { | ||||||
| 		}); | 			settings.set(userSettings.ui); | ||||||
|  | 		} else { | ||||||
|  | 			settings.set(JSON.parse(localStorage.getItem('settings') ?? '{}')); | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| 		const chatInput = document.getElementById('chat-textarea'); | 		const chatInput = document.getElementById('chat-textarea'); | ||||||
| 		setTimeout(() => chatInput?.focus(), 0); | 		setTimeout(() => chatInput?.focus(), 0); | ||||||
|  | @ -187,11 +191,18 @@ | ||||||
| 						: convertMessagesToHistory(chatContent.messages); | 						: convertMessagesToHistory(chatContent.messages); | ||||||
| 				title = chatContent.title; | 				title = chatContent.title; | ||||||
| 
 | 
 | ||||||
| 				let _settings = JSON.parse(localStorage.getItem('settings') ?? '{}'); | 				const userSettings = await getUserSettings(localStorage.token); | ||||||
|  | 
 | ||||||
|  | 				if (userSettings) { | ||||||
|  | 					await settings.set(userSettings.ui); | ||||||
|  | 				} else { | ||||||
|  | 					await settings.set(JSON.parse(localStorage.getItem('settings') ?? '{}')); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
| 				await settings.set({ | 				await settings.set({ | ||||||
| 					..._settings, | 					...$settings, | ||||||
| 					system: chatContent.system ?? _settings.system, | 					system: chatContent.system ?? $settings.system, | ||||||
| 					params: chatContent.options ?? _settings.params | 					params: chatContent.options ?? $settings.params | ||||||
| 				}); | 				}); | ||||||
| 				autoScroll = true; | 				autoScroll = true; | ||||||
| 				await tick(); | 				await tick(); | ||||||
|  |  | ||||||
|  | @ -1,13 +1,13 @@ | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
| 	import { Collapsible } from 'bits-ui'; |  | ||||||
| 
 |  | ||||||
| 	import { setDefaultModels } from '$lib/apis/configs'; |  | ||||||
| 	import { models, showSettings, settings, user, mobile } from '$lib/stores'; | 	import { models, showSettings, settings, user, mobile } from '$lib/stores'; | ||||||
| 	import { onMount, tick, getContext } from 'svelte'; | 	import { onMount, tick, getContext } from 'svelte'; | ||||||
| 	import { toast } from 'svelte-sonner'; | 	import { toast } from 'svelte-sonner'; | ||||||
| 	import Selector from './ModelSelector/Selector.svelte'; | 	import Selector from './ModelSelector/Selector.svelte'; | ||||||
| 	import Tooltip from '../common/Tooltip.svelte'; | 	import Tooltip from '../common/Tooltip.svelte'; | ||||||
| 
 | 
 | ||||||
|  | 	import { setDefaultModels } from '$lib/apis/configs'; | ||||||
|  | 	import { updateUserSettings } from '$lib/apis/users'; | ||||||
|  | 
 | ||||||
| 	const i18n = getContext('i18n'); | 	const i18n = getContext('i18n'); | ||||||
| 
 | 
 | ||||||
| 	export let selectedModels = ['']; | 	export let selectedModels = ['']; | ||||||
|  | @ -22,7 +22,9 @@ | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
| 		settings.set({ ...$settings, models: selectedModels }); | 		settings.set({ ...$settings, models: selectedModels }); | ||||||
|  | 
 | ||||||
| 		localStorage.setItem('settings', JSON.stringify($settings)); | 		localStorage.setItem('settings', JSON.stringify($settings)); | ||||||
|  | 		await updateUserSettings(localStorage.token, { ui: $settings }); | ||||||
| 
 | 
 | ||||||
| 		if ($user.role === 'admin') { | 		if ($user.role === 'admin') { | ||||||
| 			console.log('setting default models globally'); | 			console.log('setting default models globally'); | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
| 	import { getAudioConfig, updateAudioConfig } from '$lib/apis/audio'; | 	import { getAudioConfig, updateAudioConfig } from '$lib/apis/audio'; | ||||||
| 	import { user } from '$lib/stores'; | 	import { user, settings } from '$lib/stores'; | ||||||
| 	import { createEventDispatcher, onMount, getContext } from 'svelte'; | 	import { createEventDispatcher, onMount, getContext } from 'svelte'; | ||||||
| 	import { toast } from 'svelte-sonner'; | 	import { toast } from 'svelte-sonner'; | ||||||
| 	const dispatch = createEventDispatcher(); | 	const dispatch = createEventDispatcher(); | ||||||
|  | @ -99,16 +99,14 @@ | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	onMount(async () => { | 	onMount(async () => { | ||||||
| 		let settings = JSON.parse(localStorage.getItem('settings') ?? '{}'); | 		conversationMode = $settings.conversationMode ?? false; | ||||||
|  | 		speechAutoSend = $settings.speechAutoSend ?? false; | ||||||
|  | 		responseAutoPlayback = $settings.responseAutoPlayback ?? false; | ||||||
| 
 | 
 | ||||||
| 		conversationMode = settings.conversationMode ?? false; | 		STTEngine = $settings?.audio?.STTEngine ?? ''; | ||||||
| 		speechAutoSend = settings.speechAutoSend ?? false; | 		TTSEngine = $settings?.audio?.TTSEngine ?? ''; | ||||||
| 		responseAutoPlayback = settings.responseAutoPlayback ?? false; | 		speaker = $settings?.audio?.speaker ?? ''; | ||||||
| 
 | 		model = $settings?.audio?.model ?? ''; | ||||||
| 		STTEngine = settings?.audio?.STTEngine ?? ''; |  | ||||||
| 		TTSEngine = settings?.audio?.TTSEngine ?? ''; |  | ||||||
| 		speaker = settings?.audio?.speaker ?? ''; |  | ||||||
| 		model = settings?.audio?.model ?? ''; |  | ||||||
| 
 | 
 | ||||||
| 		if (TTSEngine === 'openai') { | 		if (TTSEngine === 'openai') { | ||||||
| 			getOpenAIVoices(); | 			getOpenAIVoices(); | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| 	import fileSaver from 'file-saver'; | 	import fileSaver from 'file-saver'; | ||||||
| 	const { saveAs } = fileSaver; | 	const { saveAs } = fileSaver; | ||||||
| 
 | 
 | ||||||
| 	import { chats, user, config } from '$lib/stores'; | 	import { chats, user, settings } from '$lib/stores'; | ||||||
| 
 | 
 | ||||||
| 	import { | 	import { | ||||||
| 		archiveAllChats, | 		archiveAllChats, | ||||||
|  | @ -99,9 +99,7 @@ | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	onMount(async () => { | 	onMount(async () => { | ||||||
| 		let settings = JSON.parse(localStorage.getItem('settings') ?? '{}'); | 		saveChatHistory = $settings.saveChatHistory ?? true; | ||||||
| 
 |  | ||||||
| 		saveChatHistory = settings.saveChatHistory ?? true; |  | ||||||
| 	}); | 	}); | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -4,7 +4,7 @@ | ||||||
| 	import { getLanguages } from '$lib/i18n'; | 	import { getLanguages } from '$lib/i18n'; | ||||||
| 	const dispatch = createEventDispatcher(); | 	const dispatch = createEventDispatcher(); | ||||||
| 
 | 
 | ||||||
| 	import { models, user, theme } from '$lib/stores'; | 	import { models, settings, theme } from '$lib/stores'; | ||||||
| 
 | 
 | ||||||
| 	const i18n = getContext('i18n'); | 	const i18n = getContext('i18n'); | ||||||
| 
 | 
 | ||||||
|  | @ -71,23 +71,22 @@ | ||||||
| 	onMount(async () => { | 	onMount(async () => { | ||||||
| 		selectedTheme = localStorage.theme ?? 'system'; | 		selectedTheme = localStorage.theme ?? 'system'; | ||||||
| 
 | 
 | ||||||
| 		let settings = JSON.parse(localStorage.getItem('settings') ?? '{}'); |  | ||||||
| 		languages = await getLanguages(); | 		languages = await getLanguages(); | ||||||
| 
 | 
 | ||||||
| 		notificationEnabled = settings.notificationEnabled ?? false; | 		notificationEnabled = $settings.notificationEnabled ?? false; | ||||||
| 		system = settings.system ?? ''; | 		system = $settings.system ?? ''; | ||||||
| 
 | 
 | ||||||
| 		requestFormat = settings.requestFormat ?? ''; | 		requestFormat = $settings.requestFormat ?? ''; | ||||||
| 		keepAlive = settings.keepAlive ?? null; | 		keepAlive = $settings.keepAlive ?? null; | ||||||
| 
 | 
 | ||||||
| 		params.seed = settings.seed ?? 0; | 		params.seed = $settings.seed ?? 0; | ||||||
| 		params.temperature = settings.temperature ?? ''; | 		params.temperature = $settings.temperature ?? ''; | ||||||
| 		params.frequency_penalty = settings.frequency_penalty ?? ''; | 		params.frequency_penalty = $settings.frequency_penalty ?? ''; | ||||||
| 		params.top_k = settings.top_k ?? ''; | 		params.top_k = $settings.top_k ?? ''; | ||||||
| 		params.top_p = settings.top_p ?? ''; | 		params.top_p = $settings.top_p ?? ''; | ||||||
| 		params.num_ctx = settings.num_ctx ?? ''; | 		params.num_ctx = $settings.num_ctx ?? ''; | ||||||
| 		params = { ...params, ...settings.params }; | 		params = { ...params, ...$settings.params }; | ||||||
| 		params.stop = settings?.params?.stop ? (settings?.params?.stop ?? []).join(',') : null; | 		params.stop = $settings?.params?.stop ? ($settings?.params?.stop ?? []).join(',') : null; | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	const applyTheme = (_theme: string) => { | 	const applyTheme = (_theme: string) => { | ||||||
|  |  | ||||||
|  | @ -104,23 +104,18 @@ | ||||||
| 			promptSuggestions = $config?.default_prompt_suggestions; | 			promptSuggestions = $config?.default_prompt_suggestions; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		let settings = JSON.parse(localStorage.getItem('settings') ?? '{}'); | 		titleAutoGenerate = $settings?.title?.auto ?? true; | ||||||
| 
 | 		titleAutoGenerateModel = $settings?.title?.model ?? ''; | ||||||
| 		titleAutoGenerate = settings?.title?.auto ?? true; | 		titleAutoGenerateModelExternal = $settings?.title?.modelExternal ?? ''; | ||||||
| 		titleAutoGenerateModel = settings?.title?.model ?? ''; |  | ||||||
| 		titleAutoGenerateModelExternal = settings?.title?.modelExternal ?? ''; |  | ||||||
| 		titleGenerationPrompt = | 		titleGenerationPrompt = | ||||||
| 			settings?.title?.prompt ?? | 			$settings?.title?.prompt ?? | ||||||
| 			$i18n.t( | 			`Create a concise, 3-5 word phrase as a header for the following query, strictly adhering to the 3-5 word limit and avoiding the use of the word 'title': {{prompt}}`; | ||||||
| 				"Create a concise, 3-5 word phrase as a header for the following query, strictly adhering to the 3-5 word limit and avoiding the use of the word 'title':" | 		responseAutoCopy = $settings.responseAutoCopy ?? false; | ||||||
| 			) + ' {{prompt}}'; | 		showUsername = $settings.showUsername ?? false; | ||||||
| 
 | 		chatBubble = $settings.chatBubble ?? true; | ||||||
| 		responseAutoCopy = settings.responseAutoCopy ?? false; | 		fullScreenMode = $settings.fullScreenMode ?? false; | ||||||
| 		showUsername = settings.showUsername ?? false; | 		splitLargeChunks = $settings.splitLargeChunks ?? false; | ||||||
| 		chatBubble = settings.chatBubble ?? true; | 		chatDirection = $settings.chatDirection ?? 'LTR'; | ||||||
| 		fullScreenMode = settings.fullScreenMode ?? false; |  | ||||||
| 		splitLargeChunks = settings.splitLargeChunks ?? false; |  | ||||||
| 		chatDirection = settings.chatDirection ?? 'LTR'; |  | ||||||
| 	}); | 	}); | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -19,8 +19,7 @@ | ||||||
| 	let enableMemory = false; | 	let enableMemory = false; | ||||||
| 
 | 
 | ||||||
| 	onMount(async () => { | 	onMount(async () => { | ||||||
| 		let settings = JSON.parse(localStorage.getItem('settings') ?? '{}'); | 		enableMemory = $settings?.memory ?? false; | ||||||
| 		enableMemory = settings?.memory ?? false; |  | ||||||
| 	}); | 	}); | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -17,6 +17,7 @@ | ||||||
| 	import Images from './Settings/Images.svelte'; | 	import Images from './Settings/Images.svelte'; | ||||||
| 	import User from '../icons/User.svelte'; | 	import User from '../icons/User.svelte'; | ||||||
| 	import Personalization from './Settings/Personalization.svelte'; | 	import Personalization from './Settings/Personalization.svelte'; | ||||||
|  | 	import { updateUserSettings } from '$lib/apis/users'; | ||||||
| 
 | 
 | ||||||
| 	const i18n = getContext('i18n'); | 	const i18n = getContext('i18n'); | ||||||
| 
 | 
 | ||||||
|  | @ -26,7 +27,9 @@ | ||||||
| 		console.log(updated); | 		console.log(updated); | ||||||
| 		await settings.set({ ...$settings, ...updated }); | 		await settings.set({ ...$settings, ...updated }); | ||||||
| 		await models.set(await getModels()); | 		await models.set(await getModels()); | ||||||
|  | 
 | ||||||
| 		localStorage.setItem('settings', JSON.stringify($settings)); | 		localStorage.setItem('settings', JSON.stringify($settings)); | ||||||
|  | 		await updateUserSettings(localStorage.token, { ui: $settings }); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	const getModels = async () => { | 	const getModels = async () => { | ||||||
|  |  | ||||||
|  | @ -33,6 +33,7 @@ | ||||||
| 	import ArchiveBox from '../icons/ArchiveBox.svelte'; | 	import ArchiveBox from '../icons/ArchiveBox.svelte'; | ||||||
| 	import ArchivedChatsModal from './Sidebar/ArchivedChatsModal.svelte'; | 	import ArchivedChatsModal from './Sidebar/ArchivedChatsModal.svelte'; | ||||||
| 	import UserMenu from './Sidebar/UserMenu.svelte'; | 	import UserMenu from './Sidebar/UserMenu.svelte'; | ||||||
|  | 	import { updateUserSettings } from '$lib/apis/users'; | ||||||
| 
 | 
 | ||||||
| 	const BREAKPOINT = 768; | 	const BREAKPOINT = 768; | ||||||
| 
 | 
 | ||||||
|  | @ -184,6 +185,8 @@ | ||||||
| 	const saveSettings = async (updated) => { | 	const saveSettings = async (updated) => { | ||||||
| 		await settings.set({ ...$settings, ...updated }); | 		await settings.set({ ...$settings, ...updated }); | ||||||
| 		localStorage.setItem('settings', JSON.stringify($settings)); | 		localStorage.setItem('settings', JSON.stringify($settings)); | ||||||
|  | 		await updateUserSettings(localStorage.token, { ui: $settings }); | ||||||
|  | 
 | ||||||
| 		location.href = '/'; | 		location.href = '/'; | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -35,6 +35,7 @@ | ||||||
| 	import ChangelogModal from '$lib/components/ChangelogModal.svelte'; | 	import ChangelogModal from '$lib/components/ChangelogModal.svelte'; | ||||||
| 	import Tooltip from '$lib/components/common/Tooltip.svelte'; | 	import Tooltip from '$lib/components/common/Tooltip.svelte'; | ||||||
| 	import { getBanners } from '$lib/apis/configs'; | 	import { getBanners } from '$lib/apis/configs'; | ||||||
|  | 	import { getUserSettings } from '$lib/apis/users'; | ||||||
| 
 | 
 | ||||||
| 	const i18n = getContext('i18n'); | 	const i18n = getContext('i18n'); | ||||||
| 
 | 
 | ||||||
|  | @ -72,7 +73,13 @@ | ||||||
| 				// IndexedDB Not Found | 				// IndexedDB Not Found | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			settings.set(JSON.parse(localStorage.getItem('settings') ?? '{}')); | 			const userSettings = await getUserSettings(localStorage.token); | ||||||
|  | 
 | ||||||
|  | 			if (userSettings) { | ||||||
|  | 				await settings.set(userSettings.ui); | ||||||
|  | 			} else { | ||||||
|  | 				await settings.set(JSON.parse(localStorage.getItem('settings') ?? '{}')); | ||||||
|  | 			} | ||||||
| 
 | 
 | ||||||
| 			await Promise.all([ | 			await Promise.all([ | ||||||
| 				(async () => { | 				(async () => { | ||||||
|  |  | ||||||
|  | @ -98,12 +98,6 @@ | ||||||
| 						: convertMessagesToHistory(chatContent.messages); | 						: convertMessagesToHistory(chatContent.messages); | ||||||
| 				title = chatContent.title; | 				title = chatContent.title; | ||||||
| 
 | 
 | ||||||
| 				let _settings = JSON.parse(localStorage.getItem('settings') ?? '{}'); |  | ||||||
| 				await settings.set({ |  | ||||||
| 					..._settings, |  | ||||||
| 					system: chatContent.system ?? _settings.system, |  | ||||||
| 					options: chatContent.options ?? _settings.options |  | ||||||
| 				}); |  | ||||||
| 				autoScroll = true; | 				autoScroll = true; | ||||||
| 				await tick(); | 				await tick(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue