diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 41c19d32d..8d9dd7aeb 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -15,6 +15,13 @@ jobs: name: Run Cypress Integration Tests runs-on: ubuntu-latest steps: + - name: Maximize build space + uses: AdityaGarg8/remove-unwanted-software@v4.1 + with: + remove-android: 'true' + remove-haskell: 'true' + remove-codeql: 'true' + - name: Checkout Repository uses: actions/checkout@v4 diff --git a/CHANGELOG.md b/CHANGELOG.md index d0b175fbf..1b6bdd98f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,33 @@ 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.3.13] - 2024-08-14 + +### Added + +- **🎨 Enhanced Markdown Rendering**: Significant improvements in rendering markdown, ensuring smooth and reliable display of LaTeX and Mermaid charts, enhancing user experience with more robust visual content. +- **🔄 Auto-Install Tools & Functions Python Dependencies**: For 'Tools' and 'Functions', Open WebUI now automatically install extra python requirements specified in the frontmatter, streamlining setup processes and customization. +- **🌀 OAuth Email Claim Customization**: Introduced an 'OAUTH_EMAIL_CLAIM' variable to allow customization of the default "email" claim within OAuth configurations, providing greater flexibility in authentication processes. +- **📶 Websocket Reconnection**: Enhanced reliability with the capability to automatically reconnect when a websocket is closed, ensuring consistent and stable communication. +- **🤳 Haptic Feedback on Support Devices**: Android devices now support haptic feedback for an immersive tactile experience during certain interactions. + +### Fixed + +- **🛠️ ComfyUI Performance Improvement**: Addressed an issue causing FastAPI to stall when ComfyUI image generation was active; now runs in a separate thread to prevent UI unresponsiveness. +- **🔀 Session Handling**: Fixed an issue mandating session_id on client-side to ensure smoother session management and transitions. +- **🖋️ Minor Bug Fixes and Format Corrections**: Various minor fixes including typo corrections, backend formatting improvements, and test amendments enhancing overall system stability and performance. + +### Changed + +- **🚀 Migration to SvelteKit 2**: Upgraded the underlying framework to SvelteKit version 2, offering enhanced speed, better code structure, and improved deployment capabilities. +- **🧹 General Cleanup and Refactoring**: Performed broad cleanup and refactoring across the platform, improving code efficiency and maintaining high standards of code health. +- **🚧 Integration Testing Improvements**: Modified how Cypress integration tests detect chat messages and updated sharing tests for better reliability and accuracy. +- **📁 Standardized '.safetensors' File Extension**: Renamed the '.sft' file extension to '.safetensors' for ComfyUI workflows, standardizing file formats across the platform. + +### Removed + +- **🗑️ Deprecated Frontend Functions**: Removed frontend functions that were migrated to backend to declutter the codebase and reduce redundancy. + ## [0.3.12] - 2024-08-07 ### Added diff --git a/backend/apps/audio/main.py b/backend/apps/audio/main.py index 167db77ba..20519b59b 100644 --- a/backend/apps/audio/main.py +++ b/backend/apps/audio/main.py @@ -15,7 +15,7 @@ from fastapi.responses import StreamingResponse, JSONResponse, FileResponse from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel -from typing import List + import uuid import requests import hashlib @@ -244,7 +244,7 @@ async def speech(request: Request, user=Depends(get_verified_user)): res = r.json() if "error" in res: error_detail = f"External: {res['error']['message']}" - except: + except Exception: error_detail = f"External: {e}" raise HTTPException( @@ -299,7 +299,7 @@ async def speech(request: Request, user=Depends(get_verified_user)): res = r.json() if "error" in res: error_detail = f"External: {res['error']['message']}" - except: + except Exception: error_detail = f"External: {e}" raise HTTPException( @@ -353,7 +353,7 @@ def transcribe( try: model = WhisperModel(**whisper_kwargs) - except: + except Exception: log.warning( "WhisperModel initialization failed, attempting download with local_files_only=False" ) @@ -421,7 +421,7 @@ def transcribe( res = r.json() if "error" in res: error_detail = f"External: {res['error']['message']}" - except: + except Exception: error_detail = f"External: {e}" raise HTTPException( @@ -438,7 +438,7 @@ def transcribe( ) -def get_available_models() -> List[dict]: +def get_available_models() -> list[dict]: if app.state.config.TTS_ENGINE == "openai": return [{"id": "tts-1"}, {"id": "tts-1-hd"}] elif app.state.config.TTS_ENGINE == "elevenlabs": @@ -466,7 +466,7 @@ async def get_models(user=Depends(get_verified_user)): return {"models": get_available_models()} -def get_available_voices() -> List[dict]: +def get_available_voices() -> list[dict]: if app.state.config.TTS_ENGINE == "openai": return [ {"name": "alloy", "id": "alloy"}, diff --git a/backend/apps/images/main.py b/backend/apps/images/main.py index 0b123088d..d2f5ddd5d 100644 --- a/backend/apps/images/main.py +++ b/backend/apps/images/main.py @@ -94,7 +94,7 @@ app.state.config.COMFYUI_FLUX_FP8_CLIP = COMFYUI_FLUX_FP8_CLIP def get_automatic1111_api_auth(): - if app.state.config.AUTOMATIC1111_API_AUTH == None: + if app.state.config.AUTOMATIC1111_API_AUTH is None: return "" else: auth1111_byte_string = app.state.config.AUTOMATIC1111_API_AUTH.encode("utf-8") @@ -145,28 +145,30 @@ async def get_engine_url(user=Depends(get_admin_user)): async def update_engine_url( form_data: EngineUrlUpdateForm, user=Depends(get_admin_user) ): - if form_data.AUTOMATIC1111_BASE_URL == None: + if form_data.AUTOMATIC1111_BASE_URL is None: app.state.config.AUTOMATIC1111_BASE_URL = AUTOMATIC1111_BASE_URL else: url = form_data.AUTOMATIC1111_BASE_URL.strip("/") try: - r = requests.head(url) + r = requests.head(url) + r.raise_for_status() app.state.config.AUTOMATIC1111_BASE_URL = url except Exception as e: - raise HTTPException(status_code=400, detail="Invalid URL provided.") + raise HTTPException(status_code=400, detail=ERROR_MESSAGES.INVALID_URL) - if form_data.COMFYUI_BASE_URL == None: + if form_data.COMFYUI_BASE_URL is None: app.state.config.COMFYUI_BASE_URL = COMFYUI_BASE_URL else: url = form_data.COMFYUI_BASE_URL.strip("/") try: r = requests.head(url) + r.raise_for_status() app.state.config.COMFYUI_BASE_URL = url except Exception as e: - raise HTTPException(status_code=400, detail=ERROR_MESSAGES.DEFAULT(e)) + raise HTTPException(status_code=400, detail=ERROR_MESSAGES.INVALID_URL) - if form_data.AUTOMATIC1111_API_AUTH == None: + if form_data.AUTOMATIC1111_API_AUTH is None: app.state.config.AUTOMATIC1111_API_AUTH = AUTOMATIC1111_API_AUTH else: app.state.config.AUTOMATIC1111_API_AUTH = form_data.AUTOMATIC1111_API_AUTH @@ -514,7 +516,7 @@ async def image_generations( data = ImageGenerationPayload(**data) - res = comfyui_generate_image( + res = await comfyui_generate_image( app.state.config.MODEL, data, user.id, diff --git a/backend/apps/images/utils/comfyui.py b/backend/apps/images/utils/comfyui.py index 6c37f0c49..f11dca57c 100644 --- a/backend/apps/images/utils/comfyui.py +++ b/backend/apps/images/utils/comfyui.py @@ -1,5 +1,5 @@ +import asyncio import websocket # NOTE: websocket-client (https://github.com/websocket-client/websocket-client) -import uuid import json import urllib.request import urllib.parse @@ -170,7 +170,7 @@ FLUX_DEFAULT_PROMPT = """ }, "10": { "inputs": { - "vae_name": "ae.sft" + "vae_name": "ae.safetensors" }, "class_type": "VAELoader" }, @@ -184,7 +184,7 @@ FLUX_DEFAULT_PROMPT = """ }, "12": { "inputs": { - "unet_name": "flux1-dev.sft", + "unet_name": "flux1-dev.safetensors", "weight_dtype": "default" }, "class_type": "UNETLoader" @@ -328,7 +328,7 @@ class ImageGenerationPayload(BaseModel): flux_fp8_clip: Optional[bool] = None -def comfyui_generate_image( +async def comfyui_generate_image( model: str, payload: ImageGenerationPayload, client_id, base_url ): ws_url = base_url.replace("http://", "ws://").replace("https://", "wss://") @@ -397,7 +397,9 @@ def comfyui_generate_image( return None try: - images = get_images(ws, comfyui_prompt, client_id, base_url) + images = await asyncio.to_thread( + get_images, ws, comfyui_prompt, client_id, base_url + ) except Exception as e: log.exception(f"Error while receiving images: {e}") images = None diff --git a/backend/apps/ollama/main.py b/backend/apps/ollama/main.py index 442d99ff2..03a8e198e 100644 --- a/backend/apps/ollama/main.py +++ b/backend/apps/ollama/main.py @@ -1,47 +1,36 @@ from fastapi import ( FastAPI, Request, - Response, HTTPException, Depends, - status, UploadFile, File, - BackgroundTasks, ) from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import StreamingResponse -from fastapi.concurrency import run_in_threadpool from pydantic import BaseModel, ConfigDict import os import re -import copy import random import requests import json -import uuid import aiohttp import asyncio import logging import time from urllib.parse import urlparse -from typing import Optional, List, Union +from typing import Optional, Union from starlette.background import BackgroundTask from apps.webui.models.models import Models -from apps.webui.models.users import Users from constants import ERROR_MESSAGES from utils.utils import ( - decode_token, - get_current_user, get_verified_user, get_admin_user, ) -from utils.task import prompt_template - from config import ( SRC_LOG_LEVELS, @@ -53,7 +42,12 @@ from config import ( UPLOAD_DIR, AppConfig, ) -from utils.misc import calculate_sha256, add_or_update_system_message +from utils.misc import ( + calculate_sha256, + apply_model_params_to_body_ollama, + apply_model_params_to_body_openai, + apply_model_system_prompt_to_body, +) log = logging.getLogger(__name__) log.setLevel(SRC_LOG_LEVELS["OLLAMA"]) @@ -120,7 +114,7 @@ async def get_ollama_api_urls(user=Depends(get_admin_user)): class UrlUpdateForm(BaseModel): - urls: List[str] + urls: list[str] @app.post("/urls/update") @@ -183,7 +177,7 @@ async def post_streaming_url(url: str, payload: str, stream: bool = True): res = await r.json() if "error" in res: error_detail = f"Ollama: {res['error']}" - except: + except Exception: error_detail = f"Ollama: {e}" raise HTTPException( @@ -238,7 +232,7 @@ async def get_all_models(): async def get_ollama_tags( url_idx: Optional[int] = None, user=Depends(get_verified_user) ): - if url_idx == None: + if url_idx is None: models = await get_all_models() if app.state.config.ENABLE_MODEL_FILTER: @@ -269,7 +263,7 @@ async def get_ollama_tags( res = r.json() if "error" in res: error_detail = f"Ollama: {res['error']}" - except: + except Exception: error_detail = f"Ollama: {e}" raise HTTPException( @@ -282,8 +276,7 @@ async def get_ollama_tags( @app.get("/api/version/{url_idx}") async def get_ollama_versions(url_idx: Optional[int] = None): if app.state.config.ENABLE_OLLAMA_API: - if url_idx == None: - + if url_idx is None: # returns lowest version tasks = [ fetch_url(f"{url}/api/version") @@ -323,7 +316,7 @@ async def get_ollama_versions(url_idx: Optional[int] = None): res = r.json() if "error" in res: error_detail = f"Ollama: {res['error']}" - except: + except Exception: error_detail = f"Ollama: {e}" raise HTTPException( @@ -346,8 +339,6 @@ async def pull_model( url = app.state.config.OLLAMA_BASE_URLS[url_idx] log.info(f"url: {url}") - r = None - # Admin should be able to pull models from any source payload = {**form_data.model_dump(exclude_none=True), "insecure": True} @@ -367,7 +358,7 @@ async def push_model( url_idx: Optional[int] = None, user=Depends(get_admin_user), ): - if url_idx == None: + if url_idx is None: if form_data.name in app.state.MODELS: url_idx = app.state.MODELS[form_data.name]["urls"][0] else: @@ -417,7 +408,7 @@ async def copy_model( url_idx: Optional[int] = None, user=Depends(get_admin_user), ): - if url_idx == None: + if url_idx is None: if form_data.source in app.state.MODELS: url_idx = app.state.MODELS[form_data.source]["urls"][0] else: @@ -428,13 +419,13 @@ async def copy_model( url = app.state.config.OLLAMA_BASE_URLS[url_idx] log.info(f"url: {url}") + r = requests.request( + method="POST", + url=f"{url}/api/copy", + data=form_data.model_dump_json(exclude_none=True).encode(), + ) try: - r = requests.request( - method="POST", - url=f"{url}/api/copy", - data=form_data.model_dump_json(exclude_none=True).encode(), - ) r.raise_for_status() log.debug(f"r.text: {r.text}") @@ -448,7 +439,7 @@ async def copy_model( res = r.json() if "error" in res: error_detail = f"Ollama: {res['error']}" - except: + except Exception: error_detail = f"Ollama: {e}" raise HTTPException( @@ -464,7 +455,7 @@ async def delete_model( url_idx: Optional[int] = None, user=Depends(get_admin_user), ): - if url_idx == None: + if url_idx is None: if form_data.name in app.state.MODELS: url_idx = app.state.MODELS[form_data.name]["urls"][0] else: @@ -476,12 +467,12 @@ async def delete_model( url = app.state.config.OLLAMA_BASE_URLS[url_idx] log.info(f"url: {url}") + r = requests.request( + method="DELETE", + url=f"{url}/api/delete", + data=form_data.model_dump_json(exclude_none=True).encode(), + ) try: - r = requests.request( - method="DELETE", - url=f"{url}/api/delete", - data=form_data.model_dump_json(exclude_none=True).encode(), - ) r.raise_for_status() log.debug(f"r.text: {r.text}") @@ -495,7 +486,7 @@ async def delete_model( res = r.json() if "error" in res: error_detail = f"Ollama: {res['error']}" - except: + except Exception: error_detail = f"Ollama: {e}" raise HTTPException( @@ -516,12 +507,12 @@ async def show_model_info(form_data: ModelNameForm, user=Depends(get_verified_us url = app.state.config.OLLAMA_BASE_URLS[url_idx] log.info(f"url: {url}") + r = requests.request( + method="POST", + url=f"{url}/api/show", + data=form_data.model_dump_json(exclude_none=True).encode(), + ) try: - r = requests.request( - method="POST", - url=f"{url}/api/show", - data=form_data.model_dump_json(exclude_none=True).encode(), - ) r.raise_for_status() return r.json() @@ -533,7 +524,7 @@ async def show_model_info(form_data: ModelNameForm, user=Depends(get_verified_us res = r.json() if "error" in res: error_detail = f"Ollama: {res['error']}" - except: + except Exception: error_detail = f"Ollama: {e}" raise HTTPException( @@ -556,7 +547,7 @@ async def generate_embeddings( url_idx: Optional[int] = None, user=Depends(get_verified_user), ): - if url_idx == None: + if url_idx is None: model = form_data.model if ":" not in model: @@ -573,12 +564,12 @@ async def generate_embeddings( url = app.state.config.OLLAMA_BASE_URLS[url_idx] log.info(f"url: {url}") + r = requests.request( + method="POST", + url=f"{url}/api/embeddings", + data=form_data.model_dump_json(exclude_none=True).encode(), + ) try: - r = requests.request( - method="POST", - url=f"{url}/api/embeddings", - data=form_data.model_dump_json(exclude_none=True).encode(), - ) r.raise_for_status() return r.json() @@ -590,7 +581,7 @@ async def generate_embeddings( res = r.json() if "error" in res: error_detail = f"Ollama: {res['error']}" - except: + except Exception: error_detail = f"Ollama: {e}" raise HTTPException( @@ -603,10 +594,9 @@ def generate_ollama_embeddings( form_data: GenerateEmbeddingsForm, url_idx: Optional[int] = None, ): - log.info(f"generate_ollama_embeddings {form_data}") - if url_idx == None: + if url_idx is None: model = form_data.model if ":" not in model: @@ -623,12 +613,12 @@ def generate_ollama_embeddings( url = app.state.config.OLLAMA_BASE_URLS[url_idx] log.info(f"url: {url}") + r = requests.request( + method="POST", + url=f"{url}/api/embeddings", + data=form_data.model_dump_json(exclude_none=True).encode(), + ) try: - r = requests.request( - method="POST", - url=f"{url}/api/embeddings", - data=form_data.model_dump_json(exclude_none=True).encode(), - ) r.raise_for_status() data = r.json() @@ -638,7 +628,7 @@ def generate_ollama_embeddings( if "embedding" in data: return data["embedding"] else: - raise "Something went wrong :/" + raise Exception("Something went wrong :/") except Exception as e: log.exception(e) error_detail = "Open WebUI: Server Connection Error" @@ -647,16 +637,16 @@ def generate_ollama_embeddings( res = r.json() if "error" in res: error_detail = f"Ollama: {res['error']}" - except: + except Exception: error_detail = f"Ollama: {e}" - raise error_detail + raise Exception(error_detail) class GenerateCompletionForm(BaseModel): model: str prompt: str - images: Optional[List[str]] = None + images: Optional[list[str]] = None format: Optional[str] = None options: Optional[dict] = None system: Optional[str] = None @@ -674,8 +664,7 @@ async def generate_completion( url_idx: Optional[int] = None, user=Depends(get_verified_user), ): - - if url_idx == None: + if url_idx is None: model = form_data.model if ":" not in model: @@ -700,12 +689,12 @@ async def generate_completion( class ChatMessage(BaseModel): role: str content: str - images: Optional[List[str]] = None + images: Optional[list[str]] = None class GenerateChatCompletionForm(BaseModel): model: str - messages: List[ChatMessage] + messages: list[ChatMessage] format: Optional[str] = None options: Optional[dict] = None template: Optional[str] = None @@ -713,6 +702,18 @@ class GenerateChatCompletionForm(BaseModel): keep_alive: Optional[Union[int, str]] = None +def get_ollama_url(url_idx: Optional[int], model: str): + if url_idx is None: + if model not in app.state.MODELS: + raise HTTPException( + status_code=400, + detail=ERROR_MESSAGES.MODEL_NOT_FOUND(model), + ) + url_idx = random.choice(app.state.MODELS[model]["urls"]) + url = app.state.config.OLLAMA_BASE_URLS[url_idx] + return url + + @app.post("/api/chat") @app.post("/api/chat/{url_idx}") async def generate_chat_completion( @@ -720,12 +721,7 @@ async def generate_chat_completion( url_idx: Optional[int] = None, user=Depends(get_verified_user), ): - - log.debug( - "form_data.model_dump_json(exclude_none=True).encode(): {0} ".format( - form_data.model_dump_json(exclude_none=True).encode() - ) - ) + log.debug(f"{form_data.model_dump_json(exclude_none=True).encode()}=") payload = { **form_data.model_dump(exclude_none=True, exclude=["metadata"]), @@ -740,185 +736,21 @@ async def generate_chat_completion( if model_info.base_model_id: payload["model"] = model_info.base_model_id - model_info.params = model_info.params.model_dump() + params = model_info.params.model_dump() - if model_info.params: + if params: if payload.get("options") is None: payload["options"] = {} - if ( - model_info.params.get("mirostat", None) - and payload["options"].get("mirostat") is None - ): - payload["options"]["mirostat"] = model_info.params.get("mirostat", None) - - if ( - model_info.params.get("mirostat_eta", None) - and payload["options"].get("mirostat_eta") is None - ): - payload["options"]["mirostat_eta"] = model_info.params.get( - "mirostat_eta", None - ) - - if ( - model_info.params.get("mirostat_tau", None) - and payload["options"].get("mirostat_tau") is None - ): - payload["options"]["mirostat_tau"] = model_info.params.get( - "mirostat_tau", None - ) - - if ( - model_info.params.get("num_ctx", None) - and payload["options"].get("num_ctx") is None - ): - payload["options"]["num_ctx"] = model_info.params.get("num_ctx", None) - - if ( - model_info.params.get("num_batch", None) - and payload["options"].get("num_batch") is None - ): - payload["options"]["num_batch"] = model_info.params.get( - "num_batch", None - ) - - if ( - model_info.params.get("num_keep", None) - and payload["options"].get("num_keep") is None - ): - payload["options"]["num_keep"] = model_info.params.get("num_keep", None) - - if ( - model_info.params.get("repeat_last_n", None) - and payload["options"].get("repeat_last_n") is None - ): - payload["options"]["repeat_last_n"] = model_info.params.get( - "repeat_last_n", None - ) - - if ( - model_info.params.get("frequency_penalty", None) - and payload["options"].get("frequency_penalty") is None - ): - payload["options"]["repeat_penalty"] = model_info.params.get( - "frequency_penalty", None - ) - - if ( - model_info.params.get("temperature", None) is not None - and payload["options"].get("temperature") is None - ): - payload["options"]["temperature"] = model_info.params.get( - "temperature", None - ) - - if ( - model_info.params.get("seed", None) is not None - and payload["options"].get("seed") is None - ): - payload["options"]["seed"] = model_info.params.get("seed", None) - - if ( - model_info.params.get("stop", None) - and payload["options"].get("stop") is None - ): - payload["options"]["stop"] = ( - [ - bytes(stop, "utf-8").decode("unicode_escape") - for stop in model_info.params["stop"] - ] - if model_info.params.get("stop", None) - else None - ) - - if ( - model_info.params.get("tfs_z", None) - and payload["options"].get("tfs_z") is None - ): - payload["options"]["tfs_z"] = model_info.params.get("tfs_z", None) - - if ( - model_info.params.get("max_tokens", None) - and payload["options"].get("max_tokens") is None - ): - payload["options"]["num_predict"] = model_info.params.get( - "max_tokens", None - ) - - if ( - model_info.params.get("top_k", None) - and payload["options"].get("top_k") is None - ): - payload["options"]["top_k"] = model_info.params.get("top_k", None) - - if ( - model_info.params.get("top_p", None) - and payload["options"].get("top_p") is None - ): - payload["options"]["top_p"] = model_info.params.get("top_p", None) - - if ( - model_info.params.get("min_p", None) - and payload["options"].get("min_p") is None - ): - payload["options"]["min_p"] = model_info.params.get("min_p", None) - - if ( - model_info.params.get("use_mmap", None) - and payload["options"].get("use_mmap") is None - ): - payload["options"]["use_mmap"] = model_info.params.get("use_mmap", None) - - if ( - model_info.params.get("use_mlock", None) - and payload["options"].get("use_mlock") is None - ): - payload["options"]["use_mlock"] = model_info.params.get( - "use_mlock", None - ) - - if ( - model_info.params.get("num_thread", None) - and payload["options"].get("num_thread") is None - ): - payload["options"]["num_thread"] = model_info.params.get( - "num_thread", None - ) - - system = model_info.params.get("system", None) - if system: - system = prompt_template( - system, - **( - { - "user_name": user.name, - "user_location": ( - user.info.get("location") if user.info else None - ), - } - if user - else {} - ), + payload["options"] = apply_model_params_to_body_ollama( + params, payload["options"] ) + payload = apply_model_system_prompt_to_body(params, payload, user) - if payload.get("messages"): - payload["messages"] = add_or_update_system_message( - system, payload["messages"] - ) + if ":" not in payload["model"]: + payload["model"] = f"{payload['model']}:latest" - if url_idx == None: - if ":" not in payload["model"]: - payload["model"] = f"{payload['model']}:latest" - - if payload["model"] in app.state.MODELS: - url_idx = random.choice(app.state.MODELS[payload["model"]]["urls"]) - else: - raise HTTPException( - status_code=400, - detail=ERROR_MESSAGES.MODEL_NOT_FOUND(form_data.model), - ) - - url = app.state.config.OLLAMA_BASE_URLS[url_idx] + url = get_ollama_url(url_idx, payload["model"]) log.info(f"url: {url}") log.debug(payload) @@ -940,7 +772,7 @@ class OpenAIChatMessage(BaseModel): class OpenAIChatCompletionForm(BaseModel): model: str - messages: List[OpenAIChatMessage] + messages: list[OpenAIChatMessage] model_config = ConfigDict(extra="allow") @@ -952,83 +784,28 @@ async def generate_openai_chat_completion( url_idx: Optional[int] = None, user=Depends(get_verified_user), ): - form_data = OpenAIChatCompletionForm(**form_data) - payload = {**form_data.model_dump(exclude_none=True, exclude=["metadata"])} - + completion_form = OpenAIChatCompletionForm(**form_data) + payload = {**completion_form.model_dump(exclude_none=True, exclude=["metadata"])} if "metadata" in payload: del payload["metadata"] - model_id = form_data.model + model_id = completion_form.model model_info = Models.get_model_by_id(model_id) if model_info: if model_info.base_model_id: payload["model"] = model_info.base_model_id - model_info.params = model_info.params.model_dump() + params = model_info.params.model_dump() - if model_info.params: - payload["temperature"] = model_info.params.get("temperature", None) - payload["top_p"] = model_info.params.get("top_p", None) - payload["max_tokens"] = model_info.params.get("max_tokens", None) - payload["frequency_penalty"] = model_info.params.get( - "frequency_penalty", None - ) - payload["seed"] = model_info.params.get("seed", None) - payload["stop"] = ( - [ - bytes(stop, "utf-8").decode("unicode_escape") - for stop in model_info.params["stop"] - ] - if model_info.params.get("stop", None) - else None - ) + if params: + payload = apply_model_params_to_body_openai(params, payload) + payload = apply_model_system_prompt_to_body(params, payload, user) - system = model_info.params.get("system", None) + if ":" not in payload["model"]: + payload["model"] = f"{payload['model']}:latest" - if system: - system = prompt_template( - system, - **( - { - "user_name": user.name, - "user_location": ( - user.info.get("location") if user.info else None - ), - } - if user - else {} - ), - ) - # Check if the payload already has a system message - # If not, add a system message to the payload - if payload.get("messages"): - for message in payload["messages"]: - if message.get("role") == "system": - message["content"] = system + message["content"] - break - else: - payload["messages"].insert( - 0, - { - "role": "system", - "content": system, - }, - ) - - if url_idx == None: - if ":" not in payload["model"]: - payload["model"] = f"{payload['model']}:latest" - - if payload["model"] in app.state.MODELS: - url_idx = random.choice(app.state.MODELS[payload["model"]]["urls"]) - else: - raise HTTPException( - status_code=400, - detail=ERROR_MESSAGES.MODEL_NOT_FOUND(form_data.model), - ) - - url = app.state.config.OLLAMA_BASE_URLS[url_idx] + url = get_ollama_url(url_idx, payload["model"]) log.info(f"url: {url}") return await post_streaming_url( @@ -1044,7 +821,7 @@ async def get_openai_models( url_idx: Optional[int] = None, user=Depends(get_verified_user), ): - if url_idx == None: + if url_idx is None: models = await get_all_models() if app.state.config.ENABLE_MODEL_FILTER: @@ -1099,7 +876,7 @@ async def get_openai_models( res = r.json() if "error" in res: error_detail = f"Ollama: {res['error']}" - except: + except Exception: error_detail = f"Ollama: {e}" raise HTTPException( @@ -1125,7 +902,6 @@ def parse_huggingface_url(hf_url): path_components = parsed_url.path.split("/") # Extract the desired output - user_repo = "/".join(path_components[1:3]) model_file = path_components[-1] return model_file @@ -1190,7 +966,6 @@ async def download_model( url_idx: Optional[int] = None, user=Depends(get_admin_user), ): - allowed_hosts = ["https://huggingface.co/", "https://github.com/"] if not any(form_data.url.startswith(host) for host in allowed_hosts): @@ -1199,7 +974,7 @@ async def download_model( detail="Invalid file_url. Only URLs from allowed hosts are permitted.", ) - if url_idx == None: + if url_idx is None: url_idx = 0 url = app.state.config.OLLAMA_BASE_URLS[url_idx] @@ -1222,7 +997,7 @@ def upload_model( url_idx: Optional[int] = None, user=Depends(get_admin_user), ): - if url_idx == None: + if url_idx is None: url_idx = 0 ollama_url = app.state.config.OLLAMA_BASE_URLS[url_idx] diff --git a/backend/apps/openai/main.py b/backend/apps/openai/main.py index 948ee7c46..d344c6622 100644 --- a/backend/apps/openai/main.py +++ b/backend/apps/openai/main.py @@ -17,7 +17,10 @@ from utils.utils import ( get_verified_user, get_admin_user, ) -from utils.misc import apply_model_params_to_body, apply_model_system_prompt_to_body +from utils.misc import ( + apply_model_params_to_body_openai, + apply_model_system_prompt_to_body, +) from config import ( SRC_LOG_LEVELS, @@ -30,7 +33,7 @@ from config import ( MODEL_FILTER_LIST, AppConfig, ) -from typing import List, Optional, Literal, overload +from typing import Optional, Literal, overload import hashlib @@ -86,11 +89,11 @@ async def update_config(form_data: OpenAIConfigForm, user=Depends(get_admin_user class UrlsUpdateForm(BaseModel): - urls: List[str] + urls: list[str] class KeysUpdateForm(BaseModel): - keys: List[str] + keys: list[str] @app.get("/urls") @@ -368,7 +371,7 @@ async def generate_chat_completion( payload["model"] = model_info.base_model_id params = model_info.params.model_dump() - payload = apply_model_params_to_body(params, payload) + payload = apply_model_params_to_body_openai(params, payload) payload = apply_model_system_prompt_to_body(params, payload, user) model = app.state.MODELS[payload.get("model")] diff --git a/backend/apps/rag/main.py b/backend/apps/rag/main.py index dc6b8830e..f9788556b 100644 --- a/backend/apps/rag/main.py +++ b/backend/apps/rag/main.py @@ -13,7 +13,7 @@ import os, shutil, logging, re from datetime import datetime from pathlib import Path -from typing import List, Union, Sequence, Iterator, Any +from typing import Union, Sequence, Iterator, Any from chromadb.utils.batch_utils import create_batches from langchain_core.documents import Document @@ -376,7 +376,7 @@ async def update_reranking_config( try: app.state.config.RAG_RERANKING_MODEL = form_data.reranking_model - update_reranking_model(app.state.config.RAG_RERANKING_MODEL), True + update_reranking_model(app.state.config.RAG_RERANKING_MODEL, True) return { "status": True, @@ -439,7 +439,7 @@ class ChunkParamUpdateForm(BaseModel): class YoutubeLoaderConfig(BaseModel): - language: List[str] + language: list[str] translation: Optional[str] = None @@ -642,7 +642,7 @@ def query_doc_handler( class QueryCollectionsForm(BaseModel): - collection_names: List[str] + collection_names: list[str] query: str k: Optional[int] = None r: Optional[float] = None @@ -1021,7 +1021,7 @@ class TikaLoader: self.file_path = file_path self.mime_type = mime_type - def load(self) -> List[Document]: + def load(self) -> list[Document]: with open(self.file_path, "rb") as f: data = f.read() @@ -1185,7 +1185,7 @@ def store_doc( f.close() f = open(file_path, "rb") - if collection_name == None: + if collection_name is None: collection_name = calculate_sha256(f)[:63] f.close() @@ -1238,7 +1238,7 @@ def process_doc( f = open(file_path, "rb") collection_name = form_data.collection_name - if collection_name == None: + if collection_name is None: collection_name = calculate_sha256(f)[:63] f.close() @@ -1296,7 +1296,7 @@ def store_text( ): collection_name = form_data.collection_name - if collection_name == None: + if collection_name is None: collection_name = calculate_sha256_string(form_data.content) result = store_text_in_vector_db( @@ -1339,7 +1339,7 @@ def scan_docs_dir(user=Depends(get_admin_user)): sanitized_filename = sanitize_filename(filename) doc = Documents.get_doc_by_name(sanitized_filename) - if doc == None: + if doc is None: doc = Documents.insert_new_doc( user.id, DocumentForm( diff --git a/backend/apps/rag/search/brave.py b/backend/apps/rag/search/brave.py index 76ad1fb47..681caa976 100644 --- a/backend/apps/rag/search/brave.py +++ b/backend/apps/rag/search/brave.py @@ -1,5 +1,5 @@ import logging -from typing import List, Optional +from typing import Optional import requests from apps.rag.search.main import SearchResult, get_filtered_results @@ -10,7 +10,7 @@ log.setLevel(SRC_LOG_LEVELS["RAG"]) def search_brave( - api_key: str, query: str, count: int, filter_list: Optional[List[str]] = None + api_key: str, query: str, count: int, filter_list: Optional[list[str]] = None ) -> list[SearchResult]: """Search using Brave's Search API and return the results as a list of SearchResult objects. diff --git a/backend/apps/rag/search/duckduckgo.py b/backend/apps/rag/search/duckduckgo.py index f0cc2a710..e994ef47a 100644 --- a/backend/apps/rag/search/duckduckgo.py +++ b/backend/apps/rag/search/duckduckgo.py @@ -1,5 +1,5 @@ import logging -from typing import List, Optional +from typing import Optional from apps.rag.search.main import SearchResult, get_filtered_results from duckduckgo_search import DDGS from config import SRC_LOG_LEVELS @@ -9,7 +9,7 @@ log.setLevel(SRC_LOG_LEVELS["RAG"]) def search_duckduckgo( - query: str, count: int, filter_list: Optional[List[str]] = None + query: str, count: int, filter_list: Optional[list[str]] = None ) -> list[SearchResult]: """ Search using DuckDuckGo's Search API and return the results as a list of SearchResult objects. @@ -18,7 +18,7 @@ def search_duckduckgo( count (int): The number of results to return Returns: - List[SearchResult]: A list of search results + list[SearchResult]: A list of search results """ # Use the DDGS context manager to create a DDGS object with DDGS() as ddgs: diff --git a/backend/apps/rag/search/google_pse.py b/backend/apps/rag/search/google_pse.py index 0c78512e7..7fedb3dad 100644 --- a/backend/apps/rag/search/google_pse.py +++ b/backend/apps/rag/search/google_pse.py @@ -1,6 +1,6 @@ import json import logging -from typing import List, Optional +from typing import Optional import requests from apps.rag.search.main import SearchResult, get_filtered_results @@ -15,7 +15,7 @@ def search_google_pse( search_engine_id: str, query: str, count: int, - filter_list: Optional[List[str]] = None, + filter_list: Optional[list[str]] = None, ) -> list[SearchResult]: """Search using Google's Programmable Search Engine API and return the results as a list of SearchResult objects. diff --git a/backend/apps/rag/search/jina_search.py b/backend/apps/rag/search/jina_search.py index 65f9ad68f..8d1c582a1 100644 --- a/backend/apps/rag/search/jina_search.py +++ b/backend/apps/rag/search/jina_search.py @@ -17,7 +17,7 @@ def search_jina(query: str, count: int) -> list[SearchResult]: count (int): The number of results to return Returns: - List[SearchResult]: A list of search results + list[SearchResult]: A list of search results """ jina_search_endpoint = "https://s.jina.ai/" headers = { diff --git a/backend/apps/rag/search/searxng.py b/backend/apps/rag/search/searxng.py index 6e545e994..94bed2857 100644 --- a/backend/apps/rag/search/searxng.py +++ b/backend/apps/rag/search/searxng.py @@ -1,7 +1,7 @@ import logging import requests -from typing import List, Optional +from typing import Optional from apps.rag.search.main import SearchResult, get_filtered_results from config import SRC_LOG_LEVELS @@ -14,9 +14,9 @@ def search_searxng( query_url: str, query: str, count: int, - filter_list: Optional[List[str]] = None, + filter_list: Optional[list[str]] = None, **kwargs, -) -> List[SearchResult]: +) -> list[SearchResult]: """ Search a SearXNG instance for a given query and return the results as a list of SearchResult objects. @@ -31,10 +31,10 @@ def search_searxng( language (str): Language filter for the search results; e.g., "en-US". Defaults to an empty string. safesearch (int): Safe search filter for safer web results; 0 = off, 1 = moderate, 2 = strict. Defaults to 1 (moderate). time_range (str): Time range for filtering results by date; e.g., "2023-04-05..today" or "all-time". Defaults to ''. - categories: (Optional[List[str]]): Specific categories within which the search should be performed, defaulting to an empty string if not provided. + categories: (Optional[list[str]]): Specific categories within which the search should be performed, defaulting to an empty string if not provided. Returns: - List[SearchResult]: A list of SearchResults sorted by relevance score in descending order. + list[SearchResult]: A list of SearchResults sorted by relevance score in descending order. Raise: requests.exceptions.RequestException: If a request error occurs during the search process. diff --git a/backend/apps/rag/search/serper.py b/backend/apps/rag/search/serper.py index b278a4df1..e71fbb628 100644 --- a/backend/apps/rag/search/serper.py +++ b/backend/apps/rag/search/serper.py @@ -1,6 +1,6 @@ import json import logging -from typing import List, Optional +from typing import Optional import requests from apps.rag.search.main import SearchResult, get_filtered_results @@ -11,7 +11,7 @@ log.setLevel(SRC_LOG_LEVELS["RAG"]) def search_serper( - api_key: str, query: str, count: int, filter_list: Optional[List[str]] = None + api_key: str, query: str, count: int, filter_list: Optional[list[str]] = None ) -> list[SearchResult]: """Search using serper.dev's API and return the results as a list of SearchResult objects. diff --git a/backend/apps/rag/search/serply.py b/backend/apps/rag/search/serply.py index 24b249b73..28c15fd78 100644 --- a/backend/apps/rag/search/serply.py +++ b/backend/apps/rag/search/serply.py @@ -1,6 +1,6 @@ import json import logging -from typing import List, Optional +from typing import Optional import requests from urllib.parse import urlencode @@ -19,7 +19,7 @@ def search_serply( limit: int = 10, device_type: str = "desktop", proxy_location: str = "US", - filter_list: Optional[List[str]] = None, + filter_list: Optional[list[str]] = None, ) -> list[SearchResult]: """Search using serper.dev's API and return the results as a list of SearchResult objects. diff --git a/backend/apps/rag/search/serpstack.py b/backend/apps/rag/search/serpstack.py index 64b0f117d..5c19bd134 100644 --- a/backend/apps/rag/search/serpstack.py +++ b/backend/apps/rag/search/serpstack.py @@ -1,6 +1,6 @@ import json import logging -from typing import List, Optional +from typing import Optional import requests from apps.rag.search.main import SearchResult, get_filtered_results @@ -14,7 +14,7 @@ def search_serpstack( api_key: str, query: str, count: int, - filter_list: Optional[List[str]] = None, + filter_list: Optional[list[str]] = None, https_enabled: bool = True, ) -> list[SearchResult]: """Search using serpstack.com's and return the results as a list of SearchResult objects. diff --git a/backend/apps/rag/search/tavily.py b/backend/apps/rag/search/tavily.py index b15d6ef9d..ed4ab6e08 100644 --- a/backend/apps/rag/search/tavily.py +++ b/backend/apps/rag/search/tavily.py @@ -17,7 +17,7 @@ def search_tavily(api_key: str, query: str, count: int) -> list[SearchResult]: query (str): The query to search for Returns: - List[SearchResult]: A list of search results + list[SearchResult]: A list of search results """ url = "https://api.tavily.com/search" data = {"query": query, "api_key": api_key} diff --git a/backend/apps/rag/utils.py b/backend/apps/rag/utils.py index fde89b069..034f71292 100644 --- a/backend/apps/rag/utils.py +++ b/backend/apps/rag/utils.py @@ -2,7 +2,7 @@ import os import logging import requests -from typing import List, Union +from typing import Union from apps.ollama.main import ( generate_ollama_embeddings, @@ -142,7 +142,7 @@ def merge_and_sort_query_results(query_results, k, reverse=False): def query_collection( - collection_names: List[str], + collection_names: list[str], query: str, embedding_function, k: int, @@ -157,13 +157,13 @@ def query_collection( embedding_function=embedding_function, ) results.append(result) - except: + except Exception: pass return merge_and_sort_query_results(results, k=k) def query_collection_with_hybrid_search( - collection_names: List[str], + collection_names: list[str], query: str, embedding_function, k: int, @@ -182,7 +182,7 @@ def query_collection_with_hybrid_search( r=r, ) results.append(result) - except: + except Exception: pass return merge_and_sort_query_results(results, k=k, reverse=True) @@ -411,7 +411,7 @@ class ChromaRetriever(BaseRetriever): query: str, *, run_manager: CallbackManagerForRetrieverRun, - ) -> List[Document]: + ) -> list[Document]: query_embeddings = self.embedding_function(query) results = self.collection.query( diff --git a/backend/apps/webui/main.py b/backend/apps/webui/main.py index a0b9f5008..dddf3fbb2 100644 --- a/backend/apps/webui/main.py +++ b/backend/apps/webui/main.py @@ -22,7 +22,7 @@ from apps.webui.utils import load_function_module_by_id from utils.misc import ( openai_chat_chunk_message_template, openai_chat_completion_message_template, - apply_model_params_to_body, + apply_model_params_to_body_openai, apply_model_system_prompt_to_body, ) @@ -46,6 +46,7 @@ from config import ( AppConfig, OAUTH_USERNAME_CLAIM, OAUTH_PICTURE_CLAIM, + OAUTH_EMAIL_CLAIM, ) from apps.socket.main import get_event_call, get_event_emitter @@ -84,6 +85,7 @@ app.state.config.ENABLE_COMMUNITY_SHARING = ENABLE_COMMUNITY_SHARING app.state.config.OAUTH_USERNAME_CLAIM = OAUTH_USERNAME_CLAIM app.state.config.OAUTH_PICTURE_CLAIM = OAUTH_PICTURE_CLAIM +app.state.config.OAUTH_EMAIL_CLAIM = OAUTH_EMAIL_CLAIM app.state.MODELS = {} app.state.TOOLS = {} @@ -289,7 +291,7 @@ async def generate_function_chat_completion(form_data, user): form_data["model"] = model_info.base_model_id params = model_info.params.model_dump() - form_data = apply_model_params_to_body(params, form_data) + form_data = apply_model_params_to_body_openai(params, form_data) form_data = apply_model_system_prompt_to_body(params, form_data, user) pipe_id = get_pipe_id(form_data) diff --git a/backend/apps/webui/models/auths.py b/backend/apps/webui/models/auths.py index bcea4a367..3cbe8c887 100644 --- a/backend/apps/webui/models/auths.py +++ b/backend/apps/webui/models/auths.py @@ -140,7 +140,7 @@ class AuthsTable: return None else: return None - except: + except Exception: return None def authenticate_user_by_api_key(self, api_key: str) -> Optional[UserModel]: @@ -152,7 +152,7 @@ class AuthsTable: try: user = Users.get_user_by_api_key(api_key) return user if user else None - except: + except Exception: return False def authenticate_user_by_trusted_header(self, email: str) -> Optional[UserModel]: @@ -163,7 +163,7 @@ class AuthsTable: if auth: user = Users.get_user_by_id(auth.id) return user - except: + except Exception: return None def update_user_password_by_id(self, id: str, new_password: str) -> bool: @@ -174,7 +174,7 @@ class AuthsTable: ) db.commit() return True if result == 1 else False - except: + except Exception: return False def update_email_by_id(self, id: str, email: str) -> bool: @@ -183,7 +183,7 @@ class AuthsTable: result = db.query(Auth).filter_by(id=id).update({"email": email}) db.commit() return True if result == 1 else False - except: + except Exception: return False def delete_auth_by_id(self, id: str) -> bool: @@ -200,7 +200,7 @@ class AuthsTable: return True else: return False - except: + except Exception: return False diff --git a/backend/apps/webui/models/chats.py b/backend/apps/webui/models/chats.py index d504b18c3..be77595ec 100644 --- a/backend/apps/webui/models/chats.py +++ b/backend/apps/webui/models/chats.py @@ -1,5 +1,5 @@ from pydantic import BaseModel, ConfigDict -from typing import List, Union, Optional +from typing import Union, Optional import json import uuid @@ -164,7 +164,7 @@ class ChatTable: db.refresh(chat) return self.get_chat_by_id(chat.share_id) - except: + except Exception: return None def delete_shared_chat_by_chat_id(self, chat_id: str) -> bool: @@ -175,7 +175,7 @@ class ChatTable: db.commit() return True - except: + except Exception: return False def update_chat_share_id_by_id( @@ -189,7 +189,7 @@ class ChatTable: db.commit() db.refresh(chat) return ChatModel.model_validate(chat) - except: + except Exception: return None def toggle_chat_archive_by_id(self, id: str) -> Optional[ChatModel]: @@ -201,7 +201,7 @@ class ChatTable: db.commit() db.refresh(chat) return ChatModel.model_validate(chat) - except: + except Exception: return None def archive_all_chats_by_user_id(self, user_id: str) -> bool: @@ -210,12 +210,12 @@ class ChatTable: db.query(Chat).filter_by(user_id=user_id).update({"archived": True}) db.commit() return True - except: + except Exception: return False def get_archived_chat_list_by_user_id( self, user_id: str, skip: int = 0, limit: int = 50 - ) -> List[ChatModel]: + ) -> list[ChatModel]: with get_db() as db: all_chats = ( @@ -233,7 +233,7 @@ class ChatTable: include_archived: bool = False, skip: int = 0, limit: int = 50, - ) -> List[ChatModel]: + ) -> list[ChatModel]: with get_db() as db: query = db.query(Chat).filter_by(user_id=user_id) if not include_archived: @@ -251,7 +251,7 @@ class ChatTable: include_archived: bool = False, skip: int = 0, limit: int = -1, - ) -> List[ChatTitleIdResponse]: + ) -> list[ChatTitleIdResponse]: with get_db() as db: query = db.query(Chat).filter_by(user_id=user_id) if not include_archived: @@ -279,8 +279,8 @@ class ChatTable: ] def get_chat_list_by_chat_ids( - self, chat_ids: List[str], skip: int = 0, limit: int = 50 - ) -> List[ChatModel]: + self, chat_ids: list[str], skip: int = 0, limit: int = 50 + ) -> list[ChatModel]: with get_db() as db: all_chats = ( db.query(Chat) @@ -297,7 +297,7 @@ class ChatTable: chat = db.get(Chat, id) return ChatModel.model_validate(chat) - except: + except Exception: return None def get_chat_by_share_id(self, id: str) -> Optional[ChatModel]: @@ -319,10 +319,10 @@ class ChatTable: chat = db.query(Chat).filter_by(id=id, user_id=user_id).first() return ChatModel.model_validate(chat) - except: + except Exception: return None - def get_chats(self, skip: int = 0, limit: int = 50) -> List[ChatModel]: + def get_chats(self, skip: int = 0, limit: int = 50) -> list[ChatModel]: with get_db() as db: all_chats = ( @@ -332,7 +332,7 @@ class ChatTable: ) return [ChatModel.model_validate(chat) for chat in all_chats] - def get_chats_by_user_id(self, user_id: str) -> List[ChatModel]: + def get_chats_by_user_id(self, user_id: str) -> list[ChatModel]: with get_db() as db: all_chats = ( @@ -342,7 +342,7 @@ class ChatTable: ) return [ChatModel.model_validate(chat) for chat in all_chats] - def get_archived_chats_by_user_id(self, user_id: str) -> List[ChatModel]: + def get_archived_chats_by_user_id(self, user_id: str) -> list[ChatModel]: with get_db() as db: all_chats = ( @@ -360,7 +360,7 @@ class ChatTable: db.commit() return True and self.delete_shared_chat_by_chat_id(id) - except: + except Exception: return False def delete_chat_by_id_and_user_id(self, id: str, user_id: str) -> bool: @@ -371,7 +371,7 @@ class ChatTable: db.commit() return True and self.delete_shared_chat_by_chat_id(id) - except: + except Exception: return False def delete_chats_by_user_id(self, user_id: str) -> bool: @@ -385,7 +385,7 @@ class ChatTable: db.commit() return True - except: + except Exception: return False def delete_shared_chats_by_user_id(self, user_id: str) -> bool: @@ -400,7 +400,7 @@ class ChatTable: db.commit() return True - except: + except Exception: return False diff --git a/backend/apps/webui/models/documents.py b/backend/apps/webui/models/documents.py index ac8655da9..4157c2c95 100644 --- a/backend/apps/webui/models/documents.py +++ b/backend/apps/webui/models/documents.py @@ -1,5 +1,5 @@ from pydantic import BaseModel, ConfigDict -from typing import List, Optional +from typing import Optional import time import logging @@ -93,7 +93,7 @@ class DocumentsTable: return DocumentModel.model_validate(result) else: return None - except: + except Exception: return None def get_doc_by_name(self, name: str) -> Optional[DocumentModel]: @@ -102,10 +102,10 @@ class DocumentsTable: document = db.query(Document).filter_by(name=name).first() return DocumentModel.model_validate(document) if document else None - except: + except Exception: return None - def get_docs(self) -> List[DocumentModel]: + def get_docs(self) -> list[DocumentModel]: with get_db() as db: return [ @@ -160,7 +160,7 @@ class DocumentsTable: db.query(Document).filter_by(name=name).delete() db.commit() return True - except: + except Exception: return False diff --git a/backend/apps/webui/models/files.py b/backend/apps/webui/models/files.py index 16272f24a..2de5c33b5 100644 --- a/backend/apps/webui/models/files.py +++ b/backend/apps/webui/models/files.py @@ -1,5 +1,5 @@ from pydantic import BaseModel, ConfigDict -from typing import List, Union, Optional +from typing import Union, Optional import time import logging @@ -90,10 +90,10 @@ class FilesTable: try: file = db.get(File, id) return FileModel.model_validate(file) - except: + except Exception: return None - def get_files(self) -> List[FileModel]: + def get_files(self) -> list[FileModel]: with get_db() as db: return [FileModel.model_validate(file) for file in db.query(File).all()] @@ -107,7 +107,7 @@ class FilesTable: db.commit() return True - except: + except Exception: return False def delete_all_files(self) -> bool: @@ -119,7 +119,7 @@ class FilesTable: db.commit() return True - except: + except Exception: return False diff --git a/backend/apps/webui/models/functions.py b/backend/apps/webui/models/functions.py index cb73da694..3afdc1ea9 100644 --- a/backend/apps/webui/models/functions.py +++ b/backend/apps/webui/models/functions.py @@ -1,5 +1,5 @@ from pydantic import BaseModel, ConfigDict -from typing import List, Union, Optional +from typing import Union, Optional import time import logging @@ -122,10 +122,10 @@ class FunctionsTable: function = db.get(Function, id) return FunctionModel.model_validate(function) - except: + except Exception: return None - def get_functions(self, active_only=False) -> List[FunctionModel]: + def get_functions(self, active_only=False) -> list[FunctionModel]: with get_db() as db: if active_only: @@ -141,7 +141,7 @@ class FunctionsTable: def get_functions_by_type( self, type: str, active_only=False - ) -> List[FunctionModel]: + ) -> list[FunctionModel]: with get_db() as db: if active_only: @@ -157,7 +157,7 @@ class FunctionsTable: for function in db.query(Function).filter_by(type=type).all() ] - def get_global_filter_functions(self) -> List[FunctionModel]: + def get_global_filter_functions(self) -> list[FunctionModel]: with get_db() as db: return [ @@ -167,7 +167,7 @@ class FunctionsTable: .all() ] - def get_global_action_functions(self) -> List[FunctionModel]: + def get_global_action_functions(self) -> list[FunctionModel]: with get_db() as db: return [ FunctionModel.model_validate(function) @@ -198,7 +198,7 @@ class FunctionsTable: db.commit() db.refresh(function) return self.get_function_by_id(id) - except: + except Exception: return None def get_user_valves_by_id_and_user_id( @@ -256,7 +256,7 @@ class FunctionsTable: ) db.commit() return self.get_function_by_id(id) - except: + except Exception: return None def deactivate_all_functions(self) -> Optional[bool]: @@ -271,7 +271,7 @@ class FunctionsTable: ) db.commit() return True - except: + except Exception: return None def delete_function_by_id(self, id: str) -> bool: @@ -281,7 +281,7 @@ class FunctionsTable: db.commit() return True - except: + except Exception: return False diff --git a/backend/apps/webui/models/memories.py b/backend/apps/webui/models/memories.py index 02d4b6924..41bb11ccf 100644 --- a/backend/apps/webui/models/memories.py +++ b/backend/apps/webui/models/memories.py @@ -1,5 +1,5 @@ from pydantic import BaseModel, ConfigDict -from typing import List, Union, Optional +from typing import Union, Optional from sqlalchemy import Column, String, BigInteger, Text @@ -80,25 +80,25 @@ class MemoriesTable: ) db.commit() return self.get_memory_by_id(id) - except: + except Exception: return None - def get_memories(self) -> List[MemoryModel]: + def get_memories(self) -> list[MemoryModel]: with get_db() as db: try: memories = db.query(Memory).all() return [MemoryModel.model_validate(memory) for memory in memories] - except: + except Exception: return None - def get_memories_by_user_id(self, user_id: str) -> List[MemoryModel]: + def get_memories_by_user_id(self, user_id: str) -> list[MemoryModel]: with get_db() as db: try: memories = db.query(Memory).filter_by(user_id=user_id).all() return [MemoryModel.model_validate(memory) for memory in memories] - except: + except Exception: return None def get_memory_by_id(self, id: str) -> Optional[MemoryModel]: @@ -107,7 +107,7 @@ class MemoriesTable: try: memory = db.get(Memory, id) return MemoryModel.model_validate(memory) - except: + except Exception: return None def delete_memory_by_id(self, id: str) -> bool: @@ -119,7 +119,7 @@ class MemoriesTable: return True - except: + except Exception: return False def delete_memories_by_user_id(self, user_id: str) -> bool: @@ -130,7 +130,7 @@ class MemoriesTable: db.commit() return True - except: + except Exception: return False def delete_memory_by_id_and_user_id(self, id: str, user_id: str) -> bool: @@ -141,7 +141,7 @@ class MemoriesTable: db.commit() return True - except: + except Exception: return False diff --git a/backend/apps/webui/models/models.py b/backend/apps/webui/models/models.py index 8277d1d0b..616beb2a9 100644 --- a/backend/apps/webui/models/models.py +++ b/backend/apps/webui/models/models.py @@ -137,7 +137,7 @@ class ModelsTable: print(e) return None - def get_all_models(self) -> List[ModelModel]: + def get_all_models(self) -> list[ModelModel]: with get_db() as db: return [ModelModel.model_validate(model) for model in db.query(Model).all()] @@ -146,7 +146,7 @@ class ModelsTable: with get_db() as db: model = db.get(Model, id) return ModelModel.model_validate(model) - except: + except Exception: return None def update_model_by_id(self, id: str, model: ModelForm) -> Optional[ModelModel]: @@ -175,7 +175,7 @@ class ModelsTable: db.commit() return True - except: + except Exception: return False diff --git a/backend/apps/webui/models/prompts.py b/backend/apps/webui/models/prompts.py index b8467b631..942f64a43 100644 --- a/backend/apps/webui/models/prompts.py +++ b/backend/apps/webui/models/prompts.py @@ -1,5 +1,5 @@ from pydantic import BaseModel, ConfigDict -from typing import List, Optional +from typing import Optional import time from sqlalchemy import String, Column, BigInteger, Text @@ -79,10 +79,10 @@ class PromptsTable: prompt = db.query(Prompt).filter_by(command=command).first() return PromptModel.model_validate(prompt) - except: + except Exception: return None - def get_prompts(self) -> List[PromptModel]: + def get_prompts(self) -> list[PromptModel]: with get_db() as db: return [ @@ -101,7 +101,7 @@ class PromptsTable: prompt.timestamp = int(time.time()) db.commit() return PromptModel.model_validate(prompt) - except: + except Exception: return None def delete_prompt_by_command(self, command: str) -> bool: @@ -112,7 +112,7 @@ class PromptsTable: db.commit() return True - except: + except Exception: return False diff --git a/backend/apps/webui/models/tags.py b/backend/apps/webui/models/tags.py index 7285b6fe2..7ce06cb60 100644 --- a/backend/apps/webui/models/tags.py +++ b/backend/apps/webui/models/tags.py @@ -1,5 +1,5 @@ from pydantic import BaseModel, ConfigDict -from typing import List, Optional +from typing import Optional import json import uuid @@ -69,11 +69,11 @@ class ChatIdTagForm(BaseModel): class TagChatIdsResponse(BaseModel): - chat_ids: List[str] + chat_ids: list[str] class ChatTagsResponse(BaseModel): - tags: List[str] + tags: list[str] class TagTable: @@ -109,7 +109,7 @@ class TagTable: self, user_id: str, form_data: ChatIdTagForm ) -> Optional[ChatIdTagModel]: tag = self.get_tag_by_name_and_user_id(form_data.tag_name, user_id) - if tag == None: + if tag is None: tag = self.insert_new_tag(form_data.tag_name, user_id) id = str(uuid.uuid4()) @@ -132,10 +132,10 @@ class TagTable: return ChatIdTagModel.model_validate(result) else: return None - except: + except Exception: return None - def get_tags_by_user_id(self, user_id: str) -> List[TagModel]: + def get_tags_by_user_id(self, user_id: str) -> list[TagModel]: with get_db() as db: tag_names = [ chat_id_tag.tag_name @@ -159,7 +159,7 @@ class TagTable: def get_tags_by_chat_id_and_user_id( self, chat_id: str, user_id: str - ) -> List[TagModel]: + ) -> list[TagModel]: with get_db() as db: tag_names = [ @@ -184,7 +184,7 @@ class TagTable: def get_chat_ids_by_tag_name_and_user_id( self, tag_name: str, user_id: str - ) -> List[ChatIdTagModel]: + ) -> list[ChatIdTagModel]: with get_db() as db: return [ diff --git a/backend/apps/webui/models/tools.py b/backend/apps/webui/models/tools.py index 685ce6fcf..c8c56fb97 100644 --- a/backend/apps/webui/models/tools.py +++ b/backend/apps/webui/models/tools.py @@ -1,5 +1,5 @@ from pydantic import BaseModel, ConfigDict -from typing import List, Optional +from typing import Optional import time import logging from sqlalchemy import String, Column, BigInteger, Text @@ -45,7 +45,7 @@ class ToolModel(BaseModel): user_id: str name: str content: str - specs: List[dict] + specs: list[dict] meta: ToolMeta updated_at: int # timestamp in epoch created_at: int # timestamp in epoch @@ -81,7 +81,7 @@ class ToolValves(BaseModel): class ToolsTable: def insert_new_tool( - self, user_id: str, form_data: ToolForm, specs: List[dict] + self, user_id: str, form_data: ToolForm, specs: list[dict] ) -> Optional[ToolModel]: with get_db() as db: @@ -115,10 +115,10 @@ class ToolsTable: tool = db.get(Tool, id) return ToolModel.model_validate(tool) - except: + except Exception: return None - def get_tools(self) -> List[ToolModel]: + def get_tools(self) -> list[ToolModel]: with get_db() as db: return [ToolModel.model_validate(tool) for tool in db.query(Tool).all()] @@ -141,7 +141,7 @@ class ToolsTable: ) db.commit() return self.get_tool_by_id(id) - except: + except Exception: return None def get_user_valves_by_id_and_user_id( @@ -196,7 +196,7 @@ class ToolsTable: tool = db.query(Tool).get(id) db.refresh(tool) return ToolModel.model_validate(tool) - except: + except Exception: return None def delete_tool_by_id(self, id: str) -> bool: @@ -206,7 +206,7 @@ class ToolsTable: db.commit() return True - except: + except Exception: return False diff --git a/backend/apps/webui/models/users.py b/backend/apps/webui/models/users.py index 2f30cda02..36dfa4f85 100644 --- a/backend/apps/webui/models/users.py +++ b/backend/apps/webui/models/users.py @@ -1,5 +1,5 @@ from pydantic import BaseModel, ConfigDict, parse_obj_as -from typing import List, Union, Optional +from typing import Union, Optional import time from sqlalchemy import String, Column, BigInteger, Text @@ -125,7 +125,7 @@ class UsersTable: user = db.query(User).filter_by(api_key=api_key).first() return UserModel.model_validate(user) - except: + except Exception: return None def get_user_by_email(self, email: str) -> Optional[UserModel]: @@ -134,7 +134,7 @@ class UsersTable: user = db.query(User).filter_by(email=email).first() return UserModel.model_validate(user) - except: + except Exception: return None def get_user_by_oauth_sub(self, sub: str) -> Optional[UserModel]: @@ -143,10 +143,10 @@ class UsersTable: user = db.query(User).filter_by(oauth_sub=sub).first() return UserModel.model_validate(user) - except: + except Exception: return None - def get_users(self, skip: int = 0, limit: int = 50) -> List[UserModel]: + def get_users(self, skip: int = 0, limit: int = 50) -> list[UserModel]: with get_db() as db: users = ( db.query(User) @@ -164,7 +164,7 @@ class UsersTable: with get_db() as db: user = db.query(User).order_by(User.created_at).first() return UserModel.model_validate(user) - except: + except Exception: return None def update_user_role_by_id(self, id: str, role: str) -> Optional[UserModel]: @@ -174,7 +174,7 @@ class UsersTable: db.commit() user = db.query(User).filter_by(id=id).first() return UserModel.model_validate(user) - except: + except Exception: return None def update_user_profile_image_url_by_id( @@ -189,7 +189,7 @@ class UsersTable: user = db.query(User).filter_by(id=id).first() return UserModel.model_validate(user) - except: + except Exception: return None def update_user_last_active_by_id(self, id: str) -> Optional[UserModel]: @@ -203,7 +203,7 @@ class UsersTable: user = db.query(User).filter_by(id=id).first() return UserModel.model_validate(user) - except: + except Exception: return None def update_user_oauth_sub_by_id( @@ -216,7 +216,7 @@ class UsersTable: user = db.query(User).filter_by(id=id).first() return UserModel.model_validate(user) - except: + except Exception: return None def update_user_by_id(self, id: str, updated: dict) -> Optional[UserModel]: @@ -245,7 +245,7 @@ class UsersTable: return True else: return False - except: + except Exception: return False def update_user_api_key_by_id(self, id: str, api_key: str) -> str: @@ -254,7 +254,7 @@ class UsersTable: result = db.query(User).filter_by(id=id).update({"api_key": api_key}) db.commit() return True if result == 1 else False - except: + except Exception: return False def get_user_api_key_by_id(self, id: str) -> Optional[str]: diff --git a/backend/apps/webui/routers/chats.py b/backend/apps/webui/routers/chats.py index 6e89722d3..6621e7337 100644 --- a/backend/apps/webui/routers/chats.py +++ b/backend/apps/webui/routers/chats.py @@ -1,6 +1,6 @@ from fastapi import Depends, Request, HTTPException, status from datetime import datetime, timedelta -from typing import List, Union, Optional +from typing import Union, Optional from utils.utils import get_verified_user, get_admin_user from fastapi import APIRouter from pydantic import BaseModel @@ -40,8 +40,8 @@ router = APIRouter() ############################ -@router.get("/", response_model=List[ChatTitleIdResponse]) -@router.get("/list", response_model=List[ChatTitleIdResponse]) +@router.get("/", response_model=list[ChatTitleIdResponse]) +@router.get("/list", response_model=list[ChatTitleIdResponse]) async def get_session_user_chat_list( user=Depends(get_verified_user), page: Optional[int] = None ): @@ -80,7 +80,7 @@ async def delete_all_user_chats(request: Request, user=Depends(get_verified_user ############################ -@router.get("/list/user/{user_id}", response_model=List[ChatTitleIdResponse]) +@router.get("/list/user/{user_id}", response_model=list[ChatTitleIdResponse]) async def get_user_chat_list_by_user_id( user_id: str, user=Depends(get_admin_user), @@ -119,7 +119,7 @@ async def create_new_chat(form_data: ChatForm, user=Depends(get_verified_user)): ############################ -@router.get("/all", response_model=List[ChatResponse]) +@router.get("/all", response_model=list[ChatResponse]) async def get_user_chats(user=Depends(get_verified_user)): return [ ChatResponse(**{**chat.model_dump(), "chat": json.loads(chat.chat)}) @@ -132,7 +132,7 @@ async def get_user_chats(user=Depends(get_verified_user)): ############################ -@router.get("/all/archived", response_model=List[ChatResponse]) +@router.get("/all/archived", response_model=list[ChatResponse]) async def get_user_archived_chats(user=Depends(get_verified_user)): return [ ChatResponse(**{**chat.model_dump(), "chat": json.loads(chat.chat)}) @@ -145,7 +145,7 @@ async def get_user_archived_chats(user=Depends(get_verified_user)): ############################ -@router.get("/all/db", response_model=List[ChatResponse]) +@router.get("/all/db", response_model=list[ChatResponse]) async def get_all_user_chats_in_db(user=Depends(get_admin_user)): if not ENABLE_ADMIN_EXPORT: raise HTTPException( @@ -163,7 +163,7 @@ async def get_all_user_chats_in_db(user=Depends(get_admin_user)): ############################ -@router.get("/archived", response_model=List[ChatTitleIdResponse]) +@router.get("/archived", response_model=list[ChatTitleIdResponse]) async def get_archived_session_user_chat_list( user=Depends(get_verified_user), skip: int = 0, limit: int = 50 ): @@ -216,7 +216,7 @@ class TagNameForm(BaseModel): limit: Optional[int] = 50 -@router.post("/tags", response_model=List[ChatTitleIdResponse]) +@router.post("/tags", response_model=list[ChatTitleIdResponse]) async def get_user_chat_list_by_tag_name( form_data: TagNameForm, user=Depends(get_verified_user) ): @@ -241,7 +241,7 @@ async def get_user_chat_list_by_tag_name( ############################ -@router.get("/tags/all", response_model=List[TagModel]) +@router.get("/tags/all", response_model=list[TagModel]) async def get_all_tags(user=Depends(get_verified_user)): try: tags = Tags.get_tags_by_user_id(user.id) @@ -417,7 +417,7 @@ async def delete_shared_chat_by_id(id: str, user=Depends(get_verified_user)): ############################ -@router.get("/{id}/tags", response_model=List[TagModel]) +@router.get("/{id}/tags", response_model=list[TagModel]) async def get_chat_tags_by_id(id: str, user=Depends(get_verified_user)): tags = Tags.get_tags_by_chat_id_and_user_id(id, user.id) diff --git a/backend/apps/webui/routers/configs.py b/backend/apps/webui/routers/configs.py index 39e435013..68c687374 100644 --- a/backend/apps/webui/routers/configs.py +++ b/backend/apps/webui/routers/configs.py @@ -1,7 +1,7 @@ from fastapi import Response, Request from fastapi import Depends, FastAPI, HTTPException, status from datetime import datetime, timedelta -from typing import List, Union +from typing import Union from fastapi import APIRouter from pydantic import BaseModel @@ -29,12 +29,12 @@ class SetDefaultModelsForm(BaseModel): class PromptSuggestion(BaseModel): - title: List[str] + title: list[str] content: str class SetDefaultSuggestionsForm(BaseModel): - suggestions: List[PromptSuggestion] + suggestions: list[PromptSuggestion] ############################ @@ -50,7 +50,7 @@ async def set_global_default_models( return request.app.state.config.DEFAULT_MODELS -@router.post("/default/suggestions", response_model=List[PromptSuggestion]) +@router.post("/default/suggestions", response_model=list[PromptSuggestion]) async def set_global_default_suggestions( request: Request, form_data: SetDefaultSuggestionsForm, @@ -67,10 +67,10 @@ async def set_global_default_suggestions( class SetBannersForm(BaseModel): - banners: List[BannerModel] + banners: list[BannerModel] -@router.post("/banners", response_model=List[BannerModel]) +@router.post("/banners", response_model=list[BannerModel]) async def set_banners( request: Request, form_data: SetBannersForm, @@ -81,7 +81,7 @@ async def set_banners( return request.app.state.config.BANNERS -@router.get("/banners", response_model=List[BannerModel]) +@router.get("/banners", response_model=list[BannerModel]) async def get_banners( request: Request, user=Depends(get_verified_user), diff --git a/backend/apps/webui/routers/documents.py b/backend/apps/webui/routers/documents.py index 2299b2fee..3bb2aa15b 100644 --- a/backend/apps/webui/routers/documents.py +++ b/backend/apps/webui/routers/documents.py @@ -1,6 +1,6 @@ from fastapi import Depends, FastAPI, HTTPException, status from datetime import datetime, timedelta -from typing import List, Union, Optional +from typing import Union, Optional from fastapi import APIRouter from pydantic import BaseModel @@ -24,7 +24,7 @@ router = APIRouter() ############################ -@router.get("/", response_model=List[DocumentResponse]) +@router.get("/", response_model=list[DocumentResponse]) async def get_documents(user=Depends(get_verified_user)): docs = [ DocumentResponse( @@ -46,7 +46,7 @@ async def get_documents(user=Depends(get_verified_user)): @router.post("/create", response_model=Optional[DocumentResponse]) async def create_new_doc(form_data: DocumentForm, user=Depends(get_admin_user)): doc = Documents.get_doc_by_name(form_data.name) - if doc == None: + if doc is None: doc = Documents.insert_new_doc(user.id, form_data) if doc: @@ -102,7 +102,7 @@ class TagItem(BaseModel): class TagDocumentForm(BaseModel): name: str - tags: List[dict] + tags: list[dict] @router.post("/doc/tags", response_model=Optional[DocumentResponse]) diff --git a/backend/apps/webui/routers/files.py b/backend/apps/webui/routers/files.py index 99fb923a1..ba571fc71 100644 --- a/backend/apps/webui/routers/files.py +++ b/backend/apps/webui/routers/files.py @@ -11,7 +11,7 @@ from fastapi import ( from datetime import datetime, timedelta -from typing import List, Union, Optional +from typing import Union, Optional from pathlib import Path from fastapi import APIRouter @@ -104,7 +104,7 @@ def upload_file(file: UploadFile = File(...), user=Depends(get_verified_user)): ############################ -@router.get("/", response_model=List[FileModel]) +@router.get("/", response_model=list[FileModel]) async def list_files(user=Depends(get_verified_user)): files = Files.get_files() return files diff --git a/backend/apps/webui/routers/functions.py b/backend/apps/webui/routers/functions.py index eb5216b20..f40d28264 100644 --- a/backend/apps/webui/routers/functions.py +++ b/backend/apps/webui/routers/functions.py @@ -1,6 +1,6 @@ from fastapi import Depends, FastAPI, HTTPException, status, Request from datetime import datetime, timedelta -from typing import List, Union, Optional +from typing import Union, Optional from fastapi import APIRouter from pydantic import BaseModel @@ -30,7 +30,7 @@ router = APIRouter() ############################ -@router.get("/", response_model=List[FunctionResponse]) +@router.get("/", response_model=list[FunctionResponse]) async def get_functions(user=Depends(get_verified_user)): return Functions.get_functions() @@ -40,7 +40,7 @@ async def get_functions(user=Depends(get_verified_user)): ############################ -@router.get("/export", response_model=List[FunctionModel]) +@router.get("/export", response_model=list[FunctionModel]) async def get_functions(user=Depends(get_admin_user)): return Functions.get_functions() @@ -63,7 +63,7 @@ async def create_new_function( form_data.id = form_data.id.lower() function = Functions.get_function_by_id(form_data.id) - if function == None: + if function is None: function_path = os.path.join(FUNCTIONS_DIR, f"{form_data.id}.py") try: with open(function_path, "w") as function_file: @@ -235,7 +235,7 @@ async def delete_function_by_id( function_path = os.path.join(FUNCTIONS_DIR, f"{id}.py") try: os.remove(function_path) - except: + except Exception: pass return result diff --git a/backend/apps/webui/routers/memories.py b/backend/apps/webui/routers/memories.py index 2c473ebe8..a7b5474f0 100644 --- a/backend/apps/webui/routers/memories.py +++ b/backend/apps/webui/routers/memories.py @@ -1,7 +1,7 @@ from fastapi import Response, Request from fastapi import Depends, FastAPI, HTTPException, status from datetime import datetime, timedelta -from typing import List, Union, Optional +from typing import Union, Optional from fastapi import APIRouter from pydantic import BaseModel @@ -30,7 +30,7 @@ async def get_embeddings(request: Request): ############################ -@router.get("/", response_model=List[MemoryModel]) +@router.get("/", response_model=list[MemoryModel]) async def get_memories(user=Depends(get_verified_user)): return Memories.get_memories_by_user_id(user.id) diff --git a/backend/apps/webui/routers/models.py b/backend/apps/webui/routers/models.py index eeae9e1c4..8faeed7a6 100644 --- a/backend/apps/webui/routers/models.py +++ b/backend/apps/webui/routers/models.py @@ -1,6 +1,6 @@ from fastapi import Depends, FastAPI, HTTPException, status, Request from datetime import datetime, timedelta -from typing import List, Union, Optional +from typing import Union, Optional from fastapi import APIRouter from pydantic import BaseModel @@ -18,7 +18,7 @@ router = APIRouter() ########################### -@router.get("/", response_model=List[ModelResponse]) +@router.get("/", response_model=list[ModelResponse]) async def get_models(user=Depends(get_verified_user)): return Models.get_all_models() diff --git a/backend/apps/webui/routers/prompts.py b/backend/apps/webui/routers/prompts.py index c674590e9..39d79362a 100644 --- a/backend/apps/webui/routers/prompts.py +++ b/backend/apps/webui/routers/prompts.py @@ -1,6 +1,6 @@ from fastapi import Depends, FastAPI, HTTPException, status from datetime import datetime, timedelta -from typing import List, Union, Optional +from typing import Union, Optional from fastapi import APIRouter from pydantic import BaseModel @@ -18,7 +18,7 @@ router = APIRouter() ############################ -@router.get("/", response_model=List[PromptModel]) +@router.get("/", response_model=list[PromptModel]) async def get_prompts(user=Depends(get_verified_user)): return Prompts.get_prompts() @@ -31,7 +31,7 @@ async def get_prompts(user=Depends(get_verified_user)): @router.post("/create", response_model=Optional[PromptModel]) async def create_new_prompt(form_data: PromptForm, user=Depends(get_admin_user)): prompt = Prompts.get_prompt_by_command(form_data.command) - if prompt == None: + if prompt is None: prompt = Prompts.insert_new_prompt(user.id, form_data) if prompt: diff --git a/backend/apps/webui/routers/tools.py b/backend/apps/webui/routers/tools.py index 7e60fe4d1..d6da7ae92 100644 --- a/backend/apps/webui/routers/tools.py +++ b/backend/apps/webui/routers/tools.py @@ -1,5 +1,5 @@ from fastapi import Depends, HTTPException, status, Request -from typing import List, Optional +from typing import Optional from fastapi import APIRouter @@ -27,7 +27,7 @@ router = APIRouter() ############################ -@router.get("/", response_model=List[ToolResponse]) +@router.get("/", response_model=list[ToolResponse]) async def get_toolkits(user=Depends(get_verified_user)): toolkits = [toolkit for toolkit in Tools.get_tools()] return toolkits @@ -38,7 +38,7 @@ async def get_toolkits(user=Depends(get_verified_user)): ############################ -@router.get("/export", response_model=List[ToolModel]) +@router.get("/export", response_model=list[ToolModel]) async def get_toolkits(user=Depends(get_admin_user)): toolkits = [toolkit for toolkit in Tools.get_tools()] return toolkits diff --git a/backend/apps/webui/routers/users.py b/backend/apps/webui/routers/users.py index 9627f0b06..543757275 100644 --- a/backend/apps/webui/routers/users.py +++ b/backend/apps/webui/routers/users.py @@ -1,7 +1,7 @@ from fastapi import Response, Request from fastapi import Depends, FastAPI, HTTPException, status from datetime import datetime, timedelta -from typing import List, Union, Optional +from typing import Union, Optional from fastapi import APIRouter from pydantic import BaseModel @@ -39,7 +39,7 @@ router = APIRouter() ############################ -@router.get("/", response_model=List[UserModel]) +@router.get("/", response_model=list[UserModel]) async def get_users(skip: int = 0, limit: int = 50, user=Depends(get_admin_user)): return Users.get_users(skip, limit) diff --git a/backend/apps/webui/routers/utils.py b/backend/apps/webui/routers/utils.py index 4ffe748b0..7a3c33932 100644 --- a/backend/apps/webui/routers/utils.py +++ b/backend/apps/webui/routers/utils.py @@ -17,7 +17,7 @@ from utils.misc import calculate_sha256, get_gravatar_url from config import OLLAMA_BASE_URLS, DATA_DIR, UPLOAD_DIR, ENABLE_ADMIN_EXPORT from constants import ERROR_MESSAGES -from typing import List + router = APIRouter() @@ -57,7 +57,7 @@ async def get_html_from_markdown( class ChatForm(BaseModel): title: str - messages: List[dict] + messages: list[dict] @router.post("/pdf") diff --git a/backend/apps/webui/utils.py b/backend/apps/webui/utils.py index 96d2b29eb..bf5ebedeb 100644 --- a/backend/apps/webui/utils.py +++ b/backend/apps/webui/utils.py @@ -1,6 +1,8 @@ from importlib import util import os import re +import sys +import subprocess from config import TOOLS_DIR, FUNCTIONS_DIR @@ -52,6 +54,7 @@ def load_toolkit_module_by_id(toolkit_id): frontmatter = extract_frontmatter(toolkit_path) try: + install_frontmatter_requirements(frontmatter.get("requirements", "")) spec.loader.exec_module(module) print(f"Loaded module: {module.__name__}") if hasattr(module, "Tools"): @@ -73,6 +76,7 @@ def load_function_module_by_id(function_id): frontmatter = extract_frontmatter(function_path) try: + install_frontmatter_requirements(frontmatter.get("requirements", "")) spec.loader.exec_module(module) print(f"Loaded module: {module.__name__}") if hasattr(module, "Pipe"): @@ -88,3 +92,13 @@ def load_function_module_by_id(function_id): # Move the file to the error folder os.rename(function_path, f"{function_path}.error") raise e + + +def install_frontmatter_requirements(requirements): + if requirements: + req_list = [req.strip() for req in requirements.split(",")] + for req in req_list: + print(f"Installing requirement: {req}") + subprocess.check_call([sys.executable, "-m", "pip", "install", req]) + else: + print("No requirements found in frontmatter.") diff --git a/backend/config.py b/backend/config.py index 30a970012..07ee06a58 100644 --- a/backend/config.py +++ b/backend/config.py @@ -104,7 +104,7 @@ ENV = os.environ.get("ENV", "dev") try: PACKAGE_DATA = json.loads((BASE_DIR / "package.json").read_text()) -except: +except Exception: try: PACKAGE_DATA = {"version": importlib.metadata.version("open-webui")} except importlib.metadata.PackageNotFoundError: @@ -137,7 +137,7 @@ try: with open(str(changelog_path.absolute()), "r", encoding="utf8") as file: changelog_content = file.read() -except: +except Exception: changelog_content = (pkgutil.get_data("open_webui", "CHANGELOG.md") or b"").decode() @@ -202,12 +202,12 @@ if RESET_CONFIG_ON_START: os.remove(f"{DATA_DIR}/config.json") with open(f"{DATA_DIR}/config.json", "w") as f: f.write("{}") - except: + except Exception: pass try: CONFIG_DATA = json.loads((DATA_DIR / "config.json").read_text()) -except: +except Exception: CONFIG_DATA = {} @@ -433,6 +433,12 @@ OAUTH_PICTURE_CLAIM = PersistentConfig( os.environ.get("OAUTH_PICTURE_CLAIM", "picture"), ) +OAUTH_EMAIL_CLAIM = PersistentConfig( + "OAUTH_EMAIL_CLAIM", + "oauth.oidc.email_claim", + os.environ.get("OAUTH_EMAIL_CLAIM", "email"), +) + def load_oauth_providers(): OAUTH_PROVIDERS.clear() @@ -641,7 +647,7 @@ if AIOHTTP_CLIENT_TIMEOUT == "": else: try: AIOHTTP_CLIENT_TIMEOUT = int(AIOHTTP_CLIENT_TIMEOUT) - except: + except Exception: AIOHTTP_CLIENT_TIMEOUT = 300 @@ -721,7 +727,7 @@ try: OPENAI_API_KEY = OPENAI_API_KEYS.value[ OPENAI_API_BASE_URLS.value.index("https://api.openai.com/v1") ] -except: +except Exception: pass OPENAI_API_BASE_URL = "https://api.openai.com/v1" @@ -1037,7 +1043,7 @@ RAG_EMBEDDING_MODEL = PersistentConfig( "rag.embedding_model", os.environ.get("RAG_EMBEDDING_MODEL", "sentence-transformers/all-MiniLM-L6-v2"), ) -log.info(f"Embedding model set: {RAG_EMBEDDING_MODEL.value}"), +log.info(f"Embedding model set: {RAG_EMBEDDING_MODEL.value}") RAG_EMBEDDING_MODEL_AUTO_UPDATE = ( os.environ.get("RAG_EMBEDDING_MODEL_AUTO_UPDATE", "").lower() == "true" @@ -1059,7 +1065,7 @@ RAG_RERANKING_MODEL = PersistentConfig( os.environ.get("RAG_RERANKING_MODEL", ""), ) if RAG_RERANKING_MODEL.value != "": - log.info(f"Reranking model set: {RAG_RERANKING_MODEL.value}"), + log.info(f"Reranking model set: {RAG_RERANKING_MODEL.value}") RAG_RERANKING_MODEL_AUTO_UPDATE = ( os.environ.get("RAG_RERANKING_MODEL_AUTO_UPDATE", "").lower() == "true" diff --git a/backend/main.py b/backend/main.py index d7bff888e..d8ce5f5d7 100644 --- a/backend/main.py +++ b/backend/main.py @@ -51,7 +51,7 @@ from apps.webui.internal.db import Session from pydantic import BaseModel -from typing import List, Optional +from typing import Optional from apps.webui.models.auths import Auths from apps.webui.models.models import Models @@ -1883,7 +1883,7 @@ async def get_pipeline_valves( res = r.json() if "detail" in res: detail = res["detail"] - except: + except Exception: pass raise HTTPException( @@ -2027,7 +2027,7 @@ async def get_model_filter_config(user=Depends(get_admin_user)): class ModelFilterConfigForm(BaseModel): enabled: bool - models: List[str] + models: list[str] @app.post("/api/config/model/filter") @@ -2158,7 +2158,8 @@ async def oauth_callback(provider: str, request: Request, response: Response): log.warning(f"OAuth callback failed, sub is missing: {user_data}") raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED) provider_sub = f"{provider}@{sub}" - email = user_data.get("email", "").lower() + email_claim = webui_app.state.config.OAUTH_EMAIL_CLAIM + email = user_data.get(email_claim, "").lower() # We currently mandate that email addresses are provided if not email: log.warning(f"OAuth callback failed, email is missing: {user_data}") diff --git a/backend/requirements.txt b/backend/requirements.txt index e8466a649..6ef299b5f 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -11,7 +11,7 @@ python-jose==3.3.0 passlib[bcrypt]==1.7.4 requests==2.32.3 -aiohttp==3.9.5 +aiohttp==3.10.2 sqlalchemy==2.0.31 alembic==1.13.2 @@ -34,12 +34,12 @@ anthropic google-generativeai==0.7.2 tiktoken -langchain==0.2.11 +langchain==0.2.12 langchain-community==0.2.10 langchain-chroma==0.1.2 fake-useragent==1.5.1 -chromadb==0.5.4 +chromadb==0.5.5 sentence-transformers==3.0.1 pypdf==4.3.1 docx2txt==0.8 @@ -62,11 +62,11 @@ rank-bm25==0.2.2 faster-whisper==1.0.2 -PyJWT[crypto]==2.8.0 +PyJWT[crypto]==2.9.0 authlib==1.3.1 black==24.8.0 -langfuse==2.39.2 +langfuse==2.43.3 youtube-transcript-api==0.6.2 pytube==15.0.0 @@ -76,5 +76,5 @@ duckduckgo-search~=6.2.1 ## Tests docker~=7.1.0 -pytest~=8.2.2 +pytest~=8.3.2 pytest-docker~=3.1.1 diff --git a/backend/start.sh b/backend/start.sh index 16a004e45..0a5c48e8c 100755 --- a/backend/start.sh +++ b/backend/start.sh @@ -30,7 +30,6 @@ if [[ "${USE_CUDA_DOCKER,,}" == "true" ]]; then export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib/python3.11/site-packages/torch/lib:/usr/local/lib/python3.11/site-packages/nvidia/cudnn/lib" fi - # Check if SPACE_ID is set, if so, configure for space if [ -n "$SPACE_ID" ]; then echo "Configuring for HuggingFace Space deployment" diff --git a/backend/utils/misc.py b/backend/utils/misc.py index 25dd4dd5b..2eed58f41 100644 --- a/backend/utils/misc.py +++ b/backend/utils/misc.py @@ -2,14 +2,14 @@ from pathlib import Path import hashlib import re from datetime import timedelta -from typing import Optional, List, Tuple +from typing import Optional, Callable import uuid import time from utils.task import prompt_template -def get_last_user_message_item(messages: List[dict]) -> Optional[dict]: +def get_last_user_message_item(messages: list[dict]) -> Optional[dict]: for message in reversed(messages): if message["role"] == "user": return message @@ -26,7 +26,7 @@ def get_content_from_message(message: dict) -> Optional[str]: return None -def get_last_user_message(messages: List[dict]) -> Optional[str]: +def get_last_user_message(messages: list[dict]) -> Optional[str]: message = get_last_user_message_item(messages) if message is None: return None @@ -34,31 +34,31 @@ def get_last_user_message(messages: List[dict]) -> Optional[str]: return get_content_from_message(message) -def get_last_assistant_message(messages: List[dict]) -> Optional[str]: +def get_last_assistant_message(messages: list[dict]) -> Optional[str]: for message in reversed(messages): if message["role"] == "assistant": return get_content_from_message(message) return None -def get_system_message(messages: List[dict]) -> Optional[dict]: +def get_system_message(messages: list[dict]) -> Optional[dict]: for message in messages: if message["role"] == "system": return message return None -def remove_system_message(messages: List[dict]) -> List[dict]: +def remove_system_message(messages: list[dict]) -> list[dict]: return [message for message in messages if message["role"] != "system"] -def pop_system_message(messages: List[dict]) -> Tuple[Optional[dict], List[dict]]: +def pop_system_message(messages: list[dict]) -> tuple[Optional[dict], list[dict]]: return get_system_message(messages), remove_system_message(messages) def prepend_to_first_user_message_content( - content: str, messages: List[dict] -) -> List[dict]: + content: str, messages: list[dict] +) -> list[dict]: for message in messages: if message["role"] == "user": if isinstance(message["content"], list): @@ -71,7 +71,7 @@ def prepend_to_first_user_message_content( return messages -def add_or_update_system_message(content: str, messages: List[dict]): +def add_or_update_system_message(content: str, messages: list[dict]): """ Adds a new system message at the beginning of the messages list or updates the existing system message at the beginning. @@ -135,10 +135,21 @@ def apply_model_system_prompt_to_body(params: dict, form_data: dict, user) -> di # inplace function: form_data is modified -def apply_model_params_to_body(params: dict, form_data: dict) -> dict: +def apply_model_params_to_body( + params: dict, form_data: dict, mappings: dict[str, Callable] +) -> dict: if not params: return form_data + for key, cast_func in mappings.items(): + if (value := params.get(key)) is not None: + form_data[key] = cast_func(value) + + return form_data + + +# inplace function: form_data is modified +def apply_model_params_to_body_openai(params: dict, form_data: dict) -> dict: mappings = { "temperature": float, "top_p": int, @@ -147,10 +158,40 @@ def apply_model_params_to_body(params: dict, form_data: dict) -> dict: "seed": lambda x: x, "stop": lambda x: [bytes(s, "utf-8").decode("unicode_escape") for s in x], } + return apply_model_params_to_body(params, form_data, mappings) - for key, cast_func in mappings.items(): - if (value := params.get(key)) is not None: - form_data[key] = cast_func(value) + +def apply_model_params_to_body_ollama(params: dict, form_data: dict) -> dict: + opts = [ + "temperature", + "top_p", + "seed", + "mirostat", + "mirostat_eta", + "mirostat_tau", + "num_ctx", + "num_batch", + "num_keep", + "repeat_last_n", + "tfs_z", + "top_k", + "min_p", + "use_mmap", + "use_mlock", + "num_thread", + "num_gpu", + ] + mappings = {i: lambda x: x for i in opts} + form_data = apply_model_params_to_body(params, form_data, mappings) + + name_differences = { + "max_tokens": "num_predict", + "frequency_penalty": "repeat_penalty", + } + + for key, value in name_differences.items(): + if (param := params.get(key, None)) is not None: + form_data[value] = param return form_data diff --git a/backend/utils/tools.py b/backend/utils/tools.py index 3e5d82fd6..eac36b5d9 100644 --- a/backend/utils/tools.py +++ b/backend/utils/tools.py @@ -1,5 +1,5 @@ import inspect -from typing import get_type_hints, List, Dict, Any +from typing import get_type_hints def doc_to_dict(docstring): @@ -16,7 +16,7 @@ def doc_to_dict(docstring): return ret_dict -def get_tools_specs(tools) -> List[dict]: +def get_tools_specs(tools) -> list[dict]: function_list = [ {"name": func, "function": getattr(tools, func)} for func in dir(tools) diff --git a/cypress/e2e/chat.cy.ts b/cypress/e2e/chat.cy.ts index ddb33d6c0..20be9755a 100644 --- a/cypress/e2e/chat.cy.ts +++ b/cypress/e2e/chat.cy.ts @@ -38,9 +38,10 @@ describe('Settings', () => { // User's message should be visible cy.get('.chat-user').should('exist'); // Wait for the response - cy.get('.chat-assistant', { timeout: 120_000 }) // .chat-assistant is created after the first token is received - .find('div[aria-label="Generation Info"]', { timeout: 120_000 }) // Generation Info is created after the stop token is received - .should('exist'); + // .chat-assistant is created after the first token is received + cy.get('.chat-assistant', { timeout: 10_000 }).should('exist'); + // Generation Info is created after the stop token is received + cy.get('div[aria-label="Generation Info"]', { timeout: 120_000 }).should('exist'); }); it('user can share chat', () => { @@ -57,21 +58,24 @@ describe('Settings', () => { // User's message should be visible cy.get('.chat-user').should('exist'); // Wait for the response - cy.get('.chat-assistant', { timeout: 120_000 }) // .chat-assistant is created after the first token is received - .find('div[aria-label="Generation Info"]', { timeout: 120_000 }) // Generation Info is created after the stop token is received - .should('exist'); + // .chat-assistant is created after the first token is received + cy.get('.chat-assistant', { timeout: 10_000 }).should('exist'); + // Generation Info is created after the stop token is received + cy.get('div[aria-label="Generation Info"]', { timeout: 120_000 }).should('exist'); // spy on requests const spy = cy.spy(); - cy.intercept('GET', '/api/v1/chats/*', spy); + cy.intercept('POST', '/api/v1/chats/**/share', spy); // Open context menu cy.get('#chat-context-menu-button').click(); // Click share button cy.get('#chat-share-button').click(); // Check if the share dialog is visible cy.get('#copy-and-share-chat-button').should('exist'); - cy.wrap({}, { timeout: 5000 }).should(() => { - // Check if the request was made twice (once for to replace chat object and once more due to change event) - expect(spy).to.be.callCount(2); + // Click the copy button + cy.get('#copy-and-share-chat-button').click(); + cy.wrap({}, { timeout: 5_000 }).should(() => { + // Check if the share request was made + expect(spy).to.be.callCount(1); }); }); @@ -89,9 +93,10 @@ describe('Settings', () => { // User's message should be visible cy.get('.chat-user').should('exist'); // Wait for the response - cy.get('.chat-assistant', { timeout: 120_000 }) // .chat-assistant is created after the first token is received - .find('div[aria-label="Generation Info"]', { timeout: 120_000 }) // Generation Info is created after the stop token is received - .should('exist'); + // .chat-assistant is created after the first token is received + cy.get('.chat-assistant', { timeout: 10_000 }).should('exist'); + // Generation Info is created after the stop token is received + cy.get('div[aria-label="Generation Info"]', { timeout: 120_000 }).should('exist'); // Click on the generate image button cy.get('[aria-label="Generate Image"]').click(); // Wait for image to be visible diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 325964b1a..ec8a79bbc 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -22,7 +22,6 @@ Noticed something off? Have an idea? Check our [Issues tab](https://github.com/o > [!IMPORTANT] > > - **Template Compliance:** Please be aware that failure to follow the provided issue template, or not providing the requested information at all, will likely result in your issue being closed without further consideration. This approach is critical for maintaining the manageability and integrity of issue tracking. -> > - **Detail is Key:** To ensure your issue is understood and can be effectively addressed, it's imperative to include comprehensive details. Descriptions should be clear, including steps to reproduce, expected outcomes, and actual results. Lack of sufficient detail may hinder our ability to resolve your issue. ### 🧭 Scope of Support diff --git a/package-lock.json b/package-lock.json index f903771f6..52c5f8933 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,18 @@ { "name": "open-webui", - "version": "0.3.12", + "version": "0.3.13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "open-webui", - "version": "0.3.12", + "version": "0.3.13", "dependencies": { "@codemirror/lang-javascript": "^6.2.2", "@codemirror/lang-python": "^6.1.6", "@codemirror/theme-one-dark": "^6.1.2", "@pyscript/core": "^0.4.32", - "@sveltejs/adapter-node": "^1.3.1", + "@sveltejs/adapter-node": "^2.0.0", "async": "^3.2.5", "bits-ui": "^0.19.7", "codemirror": "^6.0.1", @@ -29,6 +29,7 @@ "js-sha256": "^0.10.1", "katex": "^0.16.9", "marked": "^9.1.0", + "marked-katex-extension": "^5.1.1", "mermaid": "^10.9.1", "pyodide": "^0.26.1", "socket.io-client": "^4.2.0", @@ -39,31 +40,36 @@ "uuid": "^9.0.1" }, "devDependencies": { - "@sveltejs/adapter-auto": "^2.0.0", - "@sveltejs/adapter-static": "^2.0.3", - "@sveltejs/kit": "^1.30.0", - "@tailwindcss/typography": "^0.5.10", + "@sveltejs/adapter-auto": "3.2.2", + "@sveltejs/adapter-static": "^3.0.2", + "@sveltejs/kit": "^2.5.20", + "@sveltejs/vite-plugin-svelte": "^3.1.1", + "@tailwindcss/typography": "^0.5.13", "@types/bun": "latest", "@typescript-eslint/eslint-plugin": "^6.17.0", "@typescript-eslint/parser": "^6.17.0", "autoprefixer": "^10.4.16", "cypress": "^13.8.1", "eslint": "^8.56.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-cypress": "^3.0.2", - "eslint-plugin-svelte": "^2.30.0", - "i18next-parser": "^8.13.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-cypress": "^3.4.0", + "eslint-plugin-svelte": "^2.43.0", + "i18next-parser": "^9.0.1", "postcss": "^8.4.31", - "prettier": "^2.8.0", - "prettier-plugin-svelte": "^2.10.1", - "svelte": "^4.0.5", - "svelte-check": "^3.4.3", + "prettier": "^3.3.3", + "prettier-plugin-svelte": "^3.2.6", + "svelte": "^4.2.18", + "svelte-check": "^3.8.5", "svelte-confetti": "^1.3.2", "tailwindcss": "^3.3.3", "tslib": "^2.4.1", - "typescript": "^5.0.0", - "vite": "^4.4.2", + "typescript": "^5.5.4", + "vite": "^5.3.5", "vitest": "^1.6.0" + }, + "engines": { + "node": ">=18.13.0 <=21.x.x", + "npm": ">=6.0.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -741,14 +747,6 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@fastify/busboy": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", - "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", - "engines": { - "node": ">=14" - } - }, "node_modules/@floating-ui/core": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", @@ -932,9 +930,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", @@ -1168,208 +1166,192 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz", - "integrity": "sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.20.0.tgz", + "integrity": "sha512-TSpWzflCc4VGAUJZlPpgAJE1+V60MePDQnBd7PPkpuEmOy8i87aL6tinFGKBFKuEDikYpig72QzdT3QPYIi+oA==", "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz", - "integrity": "sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.20.0.tgz", + "integrity": "sha512-u00Ro/nok7oGzVuh/FMYfNoGqxU5CPWz1mxV85S2w9LxHR8OoMQBuSk+3BKVIDYgkpeOET5yXkx90OYFc+ytpQ==", "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz", - "integrity": "sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.20.0.tgz", + "integrity": "sha512-uFVfvzvsdGtlSLuL0ZlvPJvl6ZmrH4CBwLGEFPe7hUmf7htGAN+aXo43R/V6LATyxlKVC/m6UsLb7jbG+LG39Q==", "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz", - "integrity": "sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.20.0.tgz", + "integrity": "sha512-xbrMDdlev53vNXexEa6l0LffojxhqDTBeL+VUxuuIXys4x6xyvbKq5XqTXBCEUA8ty8iEJblHvFaWRJTk/icAQ==", "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz", - "integrity": "sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.20.0.tgz", + "integrity": "sha512-jMYvxZwGmoHFBTbr12Xc6wOdc2xA5tF5F2q6t7Rcfab68TT0n+r7dgawD4qhPEvasDsVpQi+MgDzj2faOLsZjA==", "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz", - "integrity": "sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.20.0.tgz", + "integrity": "sha512-1asSTl4HKuIHIB1GcdFHNNZhxAYEdqML/MW4QmPS4G0ivbEcBr1JKlFLKsIRqjSwOBkdItn3/ZDlyvZ/N6KPlw==", "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz", - "integrity": "sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.20.0.tgz", + "integrity": "sha512-COBb8Bkx56KldOYJfMf6wKeYJrtJ9vEgBRAOkfw6Ens0tnmzPqvlpjZiLgkhg6cA3DGzCmLmmd319pmHvKWWlQ==", "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz", - "integrity": "sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.20.0.tgz", + "integrity": "sha512-+it+mBSyMslVQa8wSPvBx53fYuZK/oLTu5RJoXogjk6x7Q7sz1GNRsXWjn6SwyJm8E/oMjNVwPhmNdIjwP135Q==", "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz", - "integrity": "sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.20.0.tgz", + "integrity": "sha512-yAMvqhPfGKsAxHN8I4+jE0CpLWD8cv4z7CK7BMmhjDuz606Q2tFKkWRY8bHR9JQXYcoLfopo5TTqzxgPUjUMfw==", "cpu": [ "ppc64" ], - "dev": true, "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz", - "integrity": "sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.20.0.tgz", + "integrity": "sha512-qmuxFpfmi/2SUkAw95TtNq/w/I7Gpjurx609OOOV7U4vhvUhBcftcmXwl3rqAek+ADBwSjIC4IVNLiszoj3dPA==", "cpu": [ "riscv64" ], - "dev": true, "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz", - "integrity": "sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.20.0.tgz", + "integrity": "sha512-I0BtGXddHSHjV1mqTNkgUZLnS3WtsqebAXv11D5BZE/gfw5KoyXSAXVqyJximQXNvNzUo4GKlCK/dIwXlz+jlg==", "cpu": [ "s390x" ], - "dev": true, "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz", - "integrity": "sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.20.0.tgz", + "integrity": "sha512-y+eoL2I3iphUg9tN9GB6ku1FA8kOfmF4oUEWhztDJ4KXJy1agk/9+pejOuZkNFhRwHAOxMsBPLbXPd6mJiCwew==", "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz", - "integrity": "sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.20.0.tgz", + "integrity": "sha512-hM3nhW40kBNYUkZb/r9k2FKK+/MnKglX7UYd4ZUy5DJs8/sMsIbqWK2piZtVGE3kcXVNj3B2IrUYROJMMCikNg==", "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz", - "integrity": "sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.20.0.tgz", + "integrity": "sha512-psegMvP+Ik/Bg7QRJbv8w8PAytPA7Uo8fpFjXyCRHWm6Nt42L+JtoqH8eDQ5hRP7/XW2UiIriy1Z46jf0Oa1kA==", "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz", - "integrity": "sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.20.0.tgz", + "integrity": "sha512-GabekH3w4lgAJpVxkk7hUzUf2hICSQO0a/BLFA11/RMxQT92MabKAqyubzDZmMOC/hcJNlc+rrypzNzYl4Dx7A==", "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz", - "integrity": "sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.20.0.tgz", + "integrity": "sha512-aJ1EJSuTdGnM6qbVC4B5DSmozPTqIag9fSzXRNNo+humQLG89XpPgdt16Ia56ORD7s+H8Pmyx44uczDQ0yDzpg==", "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -1387,106 +1369,106 @@ "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" }, "node_modules/@sveltejs/adapter-auto": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-2.1.1.tgz", - "integrity": "sha512-nzi6x/7/3Axh5VKQ8Eed3pYxastxoa06Y/bFhWb7h3Nu+nGRVxKAy3+hBJgmPCwWScy8n0TsstZjSVKfyrIHkg==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-3.2.2.tgz", + "integrity": "sha512-Mso5xPCA8zgcKrv+QioVlqMZkyUQ5MjDJiEPuG/Z7cV/5tmwV7LmcVWk5tZ+H0NCOV1x12AsoSpt/CwFwuVXMA==", "dev": true, "dependencies": { - "import-meta-resolve": "^4.0.0" + "import-meta-resolve": "^4.1.0" }, "peerDependencies": { - "@sveltejs/kit": "^1.0.0" + "@sveltejs/kit": "^2.0.0" } }, "node_modules/@sveltejs/adapter-node": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-1.3.1.tgz", - "integrity": "sha512-A0VgRQDCDPzdLNoiAbcOxGw4zT1Mc+n1LwT1OmO350R7WxrEqdMUChPPOd1iMfIDWlP4ie6E2d/WQf5es2d4Zw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-2.1.2.tgz", + "integrity": "sha512-ZfVY5buBclWHoBT+RbkMUViJGEIZ3IfT/0Hvhlgp+qC3LRZwp+wS1Zsw5dgkB2sFDZXctbLNXJtwlkjSp1mw0g==", "dependencies": { - "@rollup/plugin-commonjs": "^25.0.0", - "@rollup/plugin-json": "^6.0.0", - "@rollup/plugin-node-resolve": "^15.0.1", - "rollup": "^3.7.0" + "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "^15.2.3", + "rollup": "^4.8.0" }, "peerDependencies": { - "@sveltejs/kit": "^1.0.0" + "@sveltejs/kit": "^2.0.0" } }, "node_modules/@sveltejs/adapter-static": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-2.0.3.tgz", - "integrity": "sha512-VUqTfXsxYGugCpMqQv1U0LIdbR3S5nBkMMDmpjGVJyM6Q2jHVMFtdWJCkeHMySc6mZxJ+0eZK3T7IgmUCDrcUQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.2.tgz", + "integrity": "sha512-/EBFydZDwfwFfFEuF1vzUseBoRziwKP7AoHAwv+Ot3M084sE/HTVBHf9mCmXfdM9ijprY5YEugZjleflncX5fQ==", "dev": true, "peerDependencies": { - "@sveltejs/kit": "^1.5.0" + "@sveltejs/kit": "^2.0.0" } }, "node_modules/@sveltejs/kit": { - "version": "1.30.4", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.30.4.tgz", - "integrity": "sha512-JSQIQT6XvdchCRQEm7BABxPC56WP5RYVONAi+09S8tmzeP43fBsRlr95bFmsTQM2RHBldfgQk+jgdnsKI75daA==", + "version": "2.5.20", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.20.tgz", + "integrity": "sha512-47rJ5BoYwURE/Rp7FNMLp3NzdbWC9DQ/PmKd0mebxT2D/PrPxZxcLImcD3zsWdX2iS6oJk8ITJbO/N2lWnnUqA==", "hasInstallScript": true, "dependencies": { - "@sveltejs/vite-plugin-svelte": "^2.5.0", - "@types/cookie": "^0.5.1", - "cookie": "^0.5.0", - "devalue": "^4.3.1", + "@types/cookie": "^0.6.0", + "cookie": "^0.6.0", + "devalue": "^5.0.0", "esm-env": "^1.0.0", + "import-meta-resolve": "^4.1.0", "kleur": "^4.1.5", - "magic-string": "^0.30.0", - "mrmime": "^1.0.1", + "magic-string": "^0.30.5", + "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", - "sirv": "^2.0.2", - "tiny-glob": "^0.2.9", - "undici": "^5.28.3" + "sirv": "^2.0.4", + "tiny-glob": "^0.2.9" }, "bin": { "svelte-kit": "svelte-kit.js" }, "engines": { - "node": "^16.14 || >=18" + "node": ">=18.13" }, "peerDependencies": { - "svelte": "^3.54.0 || ^4.0.0-next.0 || ^5.0.0-next.0", - "vite": "^4.0.0" + "@sveltejs/vite-plugin-svelte": "^3.0.0", + "svelte": "^4.0.0 || ^5.0.0-next.0", + "vite": "^5.0.3" } }, "node_modules/@sveltejs/vite-plugin-svelte": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.5.3.tgz", - "integrity": "sha512-erhNtXxE5/6xGZz/M9eXsmI7Pxa6MS7jyTy06zN3Ck++ldrppOnOlJwHHTsMC7DHDQdgUp4NAc4cDNQ9eGdB/w==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.1.1.tgz", + "integrity": "sha512-rimpFEAboBBHIlzISibg94iP09k/KYdHgVhJlcsTfn7KMBhc70jFX/GRWkRdFCc2fdnk+4+Bdfej23cMDnJS6A==", "dependencies": { - "@sveltejs/vite-plugin-svelte-inspector": "^1.0.4", + "@sveltejs/vite-plugin-svelte-inspector": "^2.1.0", "debug": "^4.3.4", "deepmerge": "^4.3.1", "kleur": "^4.1.5", - "magic-string": "^0.30.3", - "svelte-hmr": "^0.15.3", - "vitefu": "^0.2.4" + "magic-string": "^0.30.10", + "svelte-hmr": "^0.16.0", + "vitefu": "^0.2.5" }, "engines": { - "node": "^14.18.0 || >= 16" + "node": "^18.0.0 || >=20" }, "peerDependencies": { - "svelte": "^3.54.0 || ^4.0.0 || ^5.0.0-next.0", - "vite": "^4.0.0" + "svelte": "^4.0.0 || ^5.0.0-next.0", + "vite": "^5.0.0" } }, "node_modules/@sveltejs/vite-plugin-svelte-inspector": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-1.0.4.tgz", - "integrity": "sha512-zjiuZ3yydBtwpF3bj0kQNV0YXe+iKE545QGZVTaylW3eAzFr+pJ/cwK8lZEaRp4JtaJXhD5DyWAV4AxLh6DgaQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.1.0.tgz", + "integrity": "sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==", "dependencies": { "debug": "^4.3.4" }, "engines": { - "node": "^14.18.0 || >= 16" + "node": "^18.0.0 || >=20" }, "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^2.2.0", - "svelte": "^3.54.0 || ^4.0.0", - "vite": "^4.0.0" + "@sveltejs/vite-plugin-svelte": "^3.0.0", + "svelte": "^4.0.0 || ^5.0.0-next.0", + "vite": "^5.0.0" } }, "node_modules/@swc/helpers": { @@ -1498,9 +1480,9 @@ } }, "node_modules/@tailwindcss/typography": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.10.tgz", - "integrity": "sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw==", + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.13.tgz", + "integrity": "sha512-ADGcJ8dX21dVVHIwTRgzrcunY6YY9uSlAHHGVKvkA+vLc5qLwEszvKts40lx7z0qc4clpjclwLeK5rVCV2P/uw==", "dev": true, "dependencies": { "lodash.castarray": "^4.4.0", @@ -1522,9 +1504,9 @@ } }, "node_modules/@types/cookie": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.5.4.tgz", - "integrity": "sha512-7z/eR6O859gyWIAjuvBWFzNURmf2oPBmJlfVWkwehU5nzIyjwBsTh7WMmEEV4JFnHuQ3ex4oyTvfKzcyJVDBNA==" + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==" }, "node_modules/@types/d3-scale": { "version": "4.0.8", @@ -1563,6 +1545,11 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, + "node_modules/@types/katex": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.7.tgz", + "integrity": "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==" + }, "node_modules/@types/mdast": { "version": "3.0.15", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", @@ -3004,12 +2991,12 @@ } }, "node_modules/commander": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", - "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "dev": true, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/common-tags": { @@ -3045,9 +3032,9 @@ "dev": true }, "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "engines": { "node": ">= 0.6" } @@ -3726,12 +3713,6 @@ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" }, - "node_modules/de-indent": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", - "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", - "dev": true - }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -3838,9 +3819,9 @@ } }, "node_modules/devalue": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-4.3.2.tgz", - "integrity": "sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==" + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.0.0.tgz", + "integrity": "sha512-gO+/OMXF7488D+u3ue+G7Y4AA3ZmUnB3eHJXmBTgNHvr4ZNzl36A0ZtG+XCRNYCkYx/bFmw4qtkoFLa+wSrwAA==" }, "node_modules/didyoumean": { "version": "1.2.2", @@ -4196,10 +4177,13 @@ } }, "node_modules/eslint-compat-utils": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.1.2.tgz", - "integrity": "sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz", + "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==", "dev": true, + "dependencies": { + "semver": "^7.5.4" + }, "engines": { "node": ">=12" }, @@ -4208,9 +4192,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz", - "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -4220,35 +4204,34 @@ } }, "node_modules/eslint-plugin-cypress": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-3.0.2.tgz", - "integrity": "sha512-5hIWc3SqXSuR+Sd7gmNMzx8yJ3LWQQS0e+qLvEVF4C1JfFtu1s9imtEm1KxlCBCcKb7+6CyR9KQYs0GiI02AlA==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-3.4.0.tgz", + "integrity": "sha512-Rrrr3Ri6wHqzrRr+TyUV7bDS4UnMMrFY1R1PP2F7XdGfe9txDC6lQEshyoNOWqGoPkbbeDm1x1XPc/adxemsnA==", "dev": true, "dependencies": { "globals": "^13.20.0" }, "peerDependencies": { - "eslint": ">=7 <9" + "eslint": ">=7" } }, "node_modules/eslint-plugin-svelte": { - "version": "2.35.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-2.35.1.tgz", - "integrity": "sha512-IF8TpLnROSGy98Z3NrsKXWDSCbNY2ReHDcrYTuXZMbfX7VmESISR78TWgO9zdg4Dht1X8coub5jKwHzP0ExRug==", + "version": "2.43.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-2.43.0.tgz", + "integrity": "sha512-REkxQWvg2pp7QVLxQNa+dJ97xUqRe7Y2JJbSWkHSuszu0VcblZtXkPBPckkivk99y5CdLw4slqfPylL2d/X4jQ==", "dev": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@jridgewell/sourcemap-codec": "^1.4.14", - "debug": "^4.3.1", - "eslint-compat-utils": "^0.1.2", + "@eslint-community/eslint-utils": "^4.4.0", + "@jridgewell/sourcemap-codec": "^1.4.15", + "eslint-compat-utils": "^0.5.1", "esutils": "^2.0.3", - "known-css-properties": "^0.29.0", - "postcss": "^8.4.5", + "known-css-properties": "^0.34.0", + "postcss": "^8.4.38", "postcss-load-config": "^3.1.4", "postcss-safe-parser": "^6.0.0", - "postcss-selector-parser": "^6.0.11", - "semver": "^7.5.3", - "svelte-eslint-parser": ">=0.33.0 <1.0.0" + "postcss-selector-parser": "^6.1.0", + "semver": "^7.6.2", + "svelte-eslint-parser": "^0.41.0" }, "engines": { "node": "^14.17.0 || >=16.0.0" @@ -4257,8 +4240,8 @@ "url": "https://github.com/sponsors/ota-meshi" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0-0", - "svelte": "^3.37.0 || ^4.0.0" + "eslint": "^7.0.0 || ^8.0.0-0 || ^9.0.0-0", + "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0-next.191" }, "peerDependenciesMeta": { "svelte": { @@ -4267,9 +4250,9 @@ } }, "node_modules/eslint-plugin-svelte/node_modules/postcss-selector-parser": { - "version": "6.0.16", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", - "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.1.tgz", + "integrity": "sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==", "dev": true, "dependencies": { "cssesc": "^3.0.0", @@ -5104,15 +5087,6 @@ "node": ">= 0.4" } }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, "node_modules/heimdalljs": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/heimdalljs/-/heimdalljs-0.2.6.tgz", @@ -5239,16 +5213,16 @@ } }, "node_modules/i18next-parser": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/i18next-parser/-/i18next-parser-8.13.0.tgz", - "integrity": "sha512-XU7resoeNcpJazh29OncQQUH6HsgCxk06RqBBDAmLHldafxopfCHY1vElyG/o3EY0Sn7XjelAmPTV0SgddJEww==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/i18next-parser/-/i18next-parser-9.0.1.tgz", + "integrity": "sha512-/Pr93/yEBdwsMKRsk4Zn63K368ALhzh8BRVrM6JNGOHy86ZKpiNJI6m8l1S/4T4Ofy1J4dlwkD7N98M70GP4aA==", "dev": true, "dependencies": { "@babel/runtime": "^7.23.2", "broccoli-plugin": "^4.0.7", "cheerio": "^1.0.0-rc.2", "colors": "1.4.0", - "commander": "~11.1.0", + "commander": "~12.1.0", "eol": "^0.9.1", "esbuild": "^0.20.1", "fs-extra": "^11.1.0", @@ -5260,14 +5234,13 @@ "sort-keys": "^5.0.0", "typescript": "^5.0.4", "vinyl": "~3.0.0", - "vinyl-fs": "^4.0.0", - "vue-template-compiler": "^2.6.11" + "vinyl-fs": "^4.0.0" }, "bin": { "i18next": "bin/cli.js" }, "engines": { - "node": ">=16.0.0 || >=18.0.0 || >=20.0.0", + "node": ">=18.0.0 || >=20.0.0 || >=22.0.0", "npm": ">=6", "yarn": ">=1" } @@ -5342,10 +5315,9 @@ } }, "node_modules/import-meta-resolve": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz", - "integrity": "sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==", - "dev": true, + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", + "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -5763,9 +5735,9 @@ } }, "node_modules/known-css-properties": { - "version": "0.29.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.29.0.tgz", - "integrity": "sha512-Ne7wqW7/9Cz54PDt4I3tcV+hAyat8ypyOGzYRJQfdxnnjeWsTxt1cy8pjvvKeI5kfXuyvULyeeAvwvvtAX3ayQ==", + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.34.0.tgz", + "integrity": "sha512-tBECoUqNFbyAY4RrbqsBQqDFpGXAEbdD5QKr8kACx3+rnArmuuR22nKQWKazvp07N9yjTyDZaw/20UIH8tL9DQ==", "dev": true }, "node_modules/layout-base": { @@ -6051,27 +6023,12 @@ "get-func-name": "^2.0.1" } }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/magic-string": { - "version": "0.30.8", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", - "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", + "version": "0.30.11", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" + "@jridgewell/sourcemap-codec": "^1.5.0" } }, "node_modules/marked": { @@ -6085,6 +6042,18 @@ "node": ">= 16" } }, + "node_modules/marked-katex-extension": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/marked-katex-extension/-/marked-katex-extension-5.1.1.tgz", + "integrity": "sha512-piquiCyZpZ1aiocoJlJkRXr+hkk5UI4xw9GhRZiIAAgvX5rhzUDSJ0seup1JcsgueC8MLNDuqe5cRcAzkFE42Q==", + "dependencies": { + "@types/katex": "^0.16.7" + }, + "peerDependencies": { + "katex": ">=0.16 <0.17", + "marked": ">=4 <15" + } + }, "node_modules/matcher-collection": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/matcher-collection/-/matcher-collection-2.0.1.tgz", @@ -6750,9 +6719,9 @@ } }, "node_modules/mrmime": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", - "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", "engines": { "node": ">=10" } @@ -7142,9 +7111,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -7210,9 +7179,9 @@ } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.4.41", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", + "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", "funding": [ { "type": "opencollective", @@ -7229,7 +7198,7 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "source-map-js": "^1.2.0" }, "engines": { @@ -7430,28 +7399,28 @@ } }, "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true, "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=14" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" } }, "node_modules/prettier-plugin-svelte": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-2.10.1.tgz", - "integrity": "sha512-Wlq7Z5v2ueCubWo0TZzKc9XHcm7TDxqcuzRuGd0gcENfzfT4JZ9yDlCbEgxWgiPmLHkBjfOtpAWkcT28MCDpUQ==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.2.6.tgz", + "integrity": "sha512-Y1XWLw7vXUQQZmgv1JAEiLcErqUniAF2wO7QJsw8BVMvpLET2dI5WpEIEJx1r11iHVdSMzQxivyfrH9On9t2IQ==", "dev": true, "peerDependencies": { - "prettier": "^1.16.4 || ^2.0.0", - "svelte": "^3.2.0 || ^4.0.0-next.0" + "prettier": "^3.0.0", + "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" } }, "node_modules/pretty-bytes": { @@ -7887,17 +7856,36 @@ "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" }, "node_modules/rollup": { - "version": "3.29.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", - "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.20.0.tgz", + "integrity": "sha512-6rbWBChcnSGzIlXeIdNIZTopKYad8ZG8ajhl78lGRLsI2rX8IkaotQhVas2Ma+GPxJav19wrSzvRvuiv0YKzWw==", + "dependencies": { + "@types/estree": "1.0.5" + }, "bin": { "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=14.18.0", + "node": ">=18.0.0", "npm": ">=8.0.0" }, "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.20.0", + "@rollup/rollup-android-arm64": "4.20.0", + "@rollup/rollup-darwin-arm64": "4.20.0", + "@rollup/rollup-darwin-x64": "4.20.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.20.0", + "@rollup/rollup-linux-arm-musleabihf": "4.20.0", + "@rollup/rollup-linux-arm64-gnu": "4.20.0", + "@rollup/rollup-linux-arm64-musl": "4.20.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.20.0", + "@rollup/rollup-linux-riscv64-gnu": "4.20.0", + "@rollup/rollup-linux-s390x-gnu": "4.20.0", + "@rollup/rollup-linux-x64-gnu": "4.20.0", + "@rollup/rollup-linux-x64-musl": "4.20.0", + "@rollup/rollup-win32-arm64-msvc": "4.20.0", + "@rollup/rollup-win32-ia32-msvc": "4.20.0", + "@rollup/rollup-win32-x64-msvc": "4.20.0", "fsevents": "~2.3.2" } }, @@ -8036,13 +8024,10 @@ } }, "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -8142,14 +8127,6 @@ "node": ">= 10" } }, - "node_modules/sirv/node_modules/mrmime": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", - "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", - "engines": { - "node": ">=10" - } - }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -8543,9 +8520,9 @@ } }, "node_modules/svelte": { - "version": "4.2.12", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.12.tgz", - "integrity": "sha512-d8+wsh5TfPwqVzbm4/HCXC783/KPHV60NvwitJnyTA5lWn1elhXMNWhXGCJ7PwPa8qFUnyJNIyuIRt2mT0WMug==", + "version": "4.2.18", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.18.tgz", + "integrity": "sha512-d0FdzYIiAePqRJEb90WlJDkjUEx42xhivxN8muUBmfZnP+tzUgz12DJ2hRJi8sIHCME7jeK1PTMgKPSfTd8JrA==", "dependencies": { "@ampproject/remapping": "^2.2.1", "@jridgewell/sourcemap-codec": "^1.4.15", @@ -8567,15 +8544,13 @@ } }, "node_modules/svelte-check": { - "version": "3.6.8", - "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.6.8.tgz", - "integrity": "sha512-rhXU7YCDtL+lq2gCqfJDXKTxJfSsCgcd08d7VWBFxTw6IWIbMWSaASbAOD3N0VV9TYSSLUqEBiratLd8WxAJJA==", + "version": "3.8.5", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.8.5.tgz", + "integrity": "sha512-3OGGgr9+bJ/+1nbPgsvulkLC48xBsqsgtc8Wam281H4G9F5v3mYGa2bHRsPuwHC5brKl4AxJH95QF73kmfihGQ==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.17", "chokidar": "^3.4.1", - "fast-glob": "^3.2.7", - "import-fresh": "^3.2.1", "picocolors": "^1.0.0", "sade": "^1.7.4", "svelte-preprocess": "^5.1.3", @@ -8598,16 +8573,16 @@ } }, "node_modules/svelte-eslint-parser": { - "version": "0.33.1", - "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.33.1.tgz", - "integrity": "sha512-vo7xPGTlKBGdLH8T5L64FipvTrqv3OQRx9d2z5X05KKZDlF4rQk8KViZO4flKERY+5BiVdOh7zZ7JGJWo5P0uA==", + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.41.0.tgz", + "integrity": "sha512-L6f4hOL+AbgfBIB52Z310pg1d2QjRqm7wy3kI1W6hhdhX5bvu7+f0R6w4ykp5HoDdzq+vGhIJmsisaiJDGmVfA==", "dev": true, "dependencies": { - "eslint-scope": "^7.0.0", - "eslint-visitor-keys": "^3.0.0", - "espree": "^9.0.0", - "postcss": "^8.4.29", - "postcss-scss": "^4.0.8" + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "postcss": "^8.4.39", + "postcss-scss": "^4.0.9" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -8616,7 +8591,7 @@ "url": "https://github.com/sponsors/ota-meshi" }, "peerDependencies": { - "svelte": "^3.37.0 || ^4.0.0" + "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0-next.191" }, "peerDependenciesMeta": { "svelte": { @@ -8625,9 +8600,9 @@ } }, "node_modules/svelte-hmr": { - "version": "0.15.3", - "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.15.3.tgz", - "integrity": "sha512-41snaPswvSf8TJUhlkoJBekRrABDXDMdpNpT2tfHIv4JuhgvHqLMhEPGtaQn0BmbNSTkuz2Ed20DF2eHw0SmBQ==", + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz", + "integrity": "sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==", "engines": { "node": "^12.20 || ^14.13.1 || >= 16" }, @@ -9119,9 +9094,9 @@ } }, "node_modules/typescript": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", - "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -9150,17 +9125,6 @@ "node": "*" } }, - "node_modules/undici": { - "version": "5.28.4", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", - "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", - "dependencies": { - "@fastify/busboy": "^2.0.0" - }, - "engines": { - "node": ">=14.0" - } - }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -9382,31 +9346,32 @@ } }, "node_modules/vite": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.3.tgz", - "integrity": "sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.0.tgz", + "integrity": "sha512-5xokfMX0PIiwCMCMb9ZJcMyh5wbBun0zUzKib+L65vAZ8GY9ePZMXxFrHbr/Kyll2+LSCY7xtERPpxkBDKngwg==", "dependencies": { - "esbuild": "^0.18.10", - "postcss": "^8.4.27", - "rollup": "^3.27.1" + "esbuild": "^0.21.3", + "postcss": "^8.4.40", + "rollup": "^4.13.0" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": "^18.0.0 || >=20.0.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" }, "optionalDependencies": { - "fsevents": "~2.3.2" + "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": ">= 14", + "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", + "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -9424,6 +9389,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, @@ -9457,100 +9425,25 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/vite-node/node_modules/rollup": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz", - "integrity": "sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==", - "dev": true, - "dependencies": { - "@types/estree": "1.0.5" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.17.2", - "@rollup/rollup-android-arm64": "4.17.2", - "@rollup/rollup-darwin-arm64": "4.17.2", - "@rollup/rollup-darwin-x64": "4.17.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.17.2", - "@rollup/rollup-linux-arm-musleabihf": "4.17.2", - "@rollup/rollup-linux-arm64-gnu": "4.17.2", - "@rollup/rollup-linux-arm64-musl": "4.17.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.17.2", - "@rollup/rollup-linux-riscv64-gnu": "4.17.2", - "@rollup/rollup-linux-s390x-gnu": "4.17.2", - "@rollup/rollup-linux-x64-gnu": "4.17.2", - "@rollup/rollup-linux-x64-musl": "4.17.2", - "@rollup/rollup-win32-arm64-msvc": "4.17.2", - "@rollup/rollup-win32-ia32-msvc": "4.17.2", - "@rollup/rollup-win32-x64-msvc": "4.17.2", - "fsevents": "~2.3.2" - } - }, - "node_modules/vite-node/node_modules/vite": { - "version": "5.2.11", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.11.tgz", - "integrity": "sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==", - "dev": true, - "dependencies": { - "esbuild": "^0.20.1", - "postcss": "^8.4.38", - "rollup": "^4.13.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } + "node": ">=12" } }, "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", - "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", "cpu": [ "arm" ], @@ -9563,9 +9456,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", - "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", "cpu": [ "arm64" ], @@ -9578,9 +9471,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", - "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", "cpu": [ "x64" ], @@ -9593,9 +9486,9 @@ } }, "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", - "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "cpu": [ "arm64" ], @@ -9608,9 +9501,9 @@ } }, "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", - "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", "cpu": [ "x64" ], @@ -9623,9 +9516,9 @@ } }, "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", - "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", "cpu": [ "arm64" ], @@ -9638,9 +9531,9 @@ } }, "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", - "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", "cpu": [ "x64" ], @@ -9653,9 +9546,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", - "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", "cpu": [ "arm" ], @@ -9668,9 +9561,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", - "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", "cpu": [ "arm64" ], @@ -9683,9 +9576,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", - "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", "cpu": [ "ia32" ], @@ -9698,9 +9591,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", - "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", "cpu": [ "loong64" ], @@ -9713,9 +9606,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", - "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", "cpu": [ "mips64el" ], @@ -9728,9 +9621,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", - "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", "cpu": [ "ppc64" ], @@ -9743,9 +9636,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", - "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", "cpu": [ "riscv64" ], @@ -9758,9 +9651,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", - "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", "cpu": [ "s390x" ], @@ -9773,9 +9666,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", - "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "cpu": [ "x64" ], @@ -9788,9 +9681,9 @@ } }, "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", - "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "cpu": [ "x64" ], @@ -9803,9 +9696,9 @@ } }, "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", - "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "cpu": [ "x64" ], @@ -9818,9 +9711,9 @@ } }, "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", - "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", "cpu": [ "x64" ], @@ -9833,9 +9726,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", - "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "cpu": [ "arm64" ], @@ -9848,9 +9741,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", - "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", "cpu": [ "ia32" ], @@ -9863,9 +9756,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", - "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "cpu": [ "x64" ], @@ -9878,9 +9771,9 @@ } }, "node_modules/vite/node_modules/esbuild": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", - "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -9889,28 +9782,29 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/android-arm": "0.18.20", - "@esbuild/android-arm64": "0.18.20", - "@esbuild/android-x64": "0.18.20", - "@esbuild/darwin-arm64": "0.18.20", - "@esbuild/darwin-x64": "0.18.20", - "@esbuild/freebsd-arm64": "0.18.20", - "@esbuild/freebsd-x64": "0.18.20", - "@esbuild/linux-arm": "0.18.20", - "@esbuild/linux-arm64": "0.18.20", - "@esbuild/linux-ia32": "0.18.20", - "@esbuild/linux-loong64": "0.18.20", - "@esbuild/linux-mips64el": "0.18.20", - "@esbuild/linux-ppc64": "0.18.20", - "@esbuild/linux-riscv64": "0.18.20", - "@esbuild/linux-s390x": "0.18.20", - "@esbuild/linux-x64": "0.18.20", - "@esbuild/netbsd-x64": "0.18.20", - "@esbuild/openbsd-x64": "0.18.20", - "@esbuild/sunos-x64": "0.18.20", - "@esbuild/win32-arm64": "0.18.20", - "@esbuild/win32-ia32": "0.18.20", - "@esbuild/win32-x64": "0.18.20" + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "node_modules/vitefu": { @@ -10101,41 +9995,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/vitest/node_modules/rollup": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz", - "integrity": "sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==", - "dev": true, - "dependencies": { - "@types/estree": "1.0.5" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.17.2", - "@rollup/rollup-android-arm64": "4.17.2", - "@rollup/rollup-darwin-arm64": "4.17.2", - "@rollup/rollup-darwin-x64": "4.17.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.17.2", - "@rollup/rollup-linux-arm-musleabihf": "4.17.2", - "@rollup/rollup-linux-arm64-gnu": "4.17.2", - "@rollup/rollup-linux-arm64-musl": "4.17.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.17.2", - "@rollup/rollup-linux-riscv64-gnu": "4.17.2", - "@rollup/rollup-linux-s390x-gnu": "4.17.2", - "@rollup/rollup-linux-x64-gnu": "4.17.2", - "@rollup/rollup-linux-x64-musl": "4.17.2", - "@rollup/rollup-win32-arm64-msvc": "4.17.2", - "@rollup/rollup-win32-ia32-msvc": "4.17.2", - "@rollup/rollup-win32-x64-msvc": "4.17.2", - "fsevents": "~2.3.2" - } - }, "node_modules/vitest/node_modules/strip-final-newline": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", @@ -10148,71 +10007,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/vitest/node_modules/vite": { - "version": "5.2.11", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.11.tgz", - "integrity": "sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==", - "dev": true, - "dependencies": { - "esbuild": "^0.20.1", - "postcss": "^8.4.38", - "rollup": "^4.13.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "node_modules/vue-template-compiler": { - "version": "2.7.16", - "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz", - "integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==", - "dev": true, - "dependencies": { - "de-indent": "^1.0.2", - "he": "^1.2.0" - } - }, "node_modules/w3c-keyname": { "version": "2.2.8", "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", @@ -10427,12 +10221,6 @@ "node": ">=0.4" } }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", diff --git a/package.json b/package.json index bbe3ac397..2d32422d1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "open-webui", - "version": "0.3.12", + "version": "0.3.13", "private": true, "scripts": { "dev": "npm run pyodide:fetch && vite dev --host", @@ -20,30 +20,31 @@ "pyodide:fetch": "node scripts/prepare-pyodide.js" }, "devDependencies": { - "@sveltejs/adapter-auto": "^2.0.0", - "@sveltejs/adapter-static": "^2.0.3", - "@sveltejs/kit": "^1.30.0", - "@tailwindcss/typography": "^0.5.10", + "@sveltejs/adapter-auto": "3.2.2", + "@sveltejs/adapter-static": "^3.0.2", + "@sveltejs/kit": "^2.5.20", + "@sveltejs/vite-plugin-svelte": "^3.1.1", + "@tailwindcss/typography": "^0.5.13", "@types/bun": "latest", "@typescript-eslint/eslint-plugin": "^6.17.0", "@typescript-eslint/parser": "^6.17.0", "autoprefixer": "^10.4.16", "cypress": "^13.8.1", "eslint": "^8.56.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-cypress": "^3.0.2", - "eslint-plugin-svelte": "^2.30.0", - "i18next-parser": "^8.13.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-cypress": "^3.4.0", + "eslint-plugin-svelte": "^2.43.0", + "i18next-parser": "^9.0.1", "postcss": "^8.4.31", - "prettier": "^2.8.0", - "prettier-plugin-svelte": "^2.10.1", - "svelte": "^4.0.5", - "svelte-check": "^3.4.3", + "prettier": "^3.3.3", + "prettier-plugin-svelte": "^3.2.6", + "svelte": "^4.2.18", + "svelte-check": "^3.8.5", "svelte-confetti": "^1.3.2", "tailwindcss": "^3.3.3", "tslib": "^2.4.1", - "typescript": "^5.0.0", - "vite": "^4.4.2", + "typescript": "^5.5.4", + "vite": "^5.3.5", "vitest": "^1.6.0" }, "type": "module", @@ -52,7 +53,7 @@ "@codemirror/lang-python": "^6.1.6", "@codemirror/theme-one-dark": "^6.1.2", "@pyscript/core": "^0.4.32", - "@sveltejs/adapter-node": "^1.3.1", + "@sveltejs/adapter-node": "^2.0.0", "async": "^3.2.5", "bits-ui": "^0.19.7", "codemirror": "^6.0.1", @@ -69,6 +70,7 @@ "js-sha256": "^0.10.1", "katex": "^0.16.9", "marked": "^9.1.0", + "marked-katex-extension": "^5.1.1", "mermaid": "^10.9.1", "pyodide": "^0.26.1", "socket.io-client": "^4.2.0", @@ -77,5 +79,9 @@ "tippy.js": "^6.3.7", "turndown": "^7.2.0", "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.13.0 <=21.x.x", + "npm": ">=6.0.0" } } diff --git a/pyproject.toml b/pyproject.toml index 0b7af7f18..159bce072 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "open-webui" -description = "Open WebUI (Formerly Ollama WebUI)" +description = "Open WebUI" authors = [ { name = "Timothy Jaeryang Baek", email = "tim@openwebui.com" } ] @@ -19,7 +19,7 @@ dependencies = [ "passlib[bcrypt]==1.7.4", "requests==2.32.3", - "aiohttp==3.9.5", + "aiohttp==3.10.2", "sqlalchemy==2.0.31", "alembic==1.13.2", @@ -41,12 +41,12 @@ dependencies = [ "google-generativeai==0.7.2", "tiktoken", - "langchain==0.2.11", + "langchain==0.2.12", "langchain-community==0.2.10", "langchain-chroma==0.1.2", "fake-useragent==1.5.1", - "chromadb==0.5.4", + "chromadb==0.5.5", "sentence-transformers==3.0.1", "pypdf==4.3.1", "docx2txt==0.8", @@ -69,11 +69,11 @@ dependencies = [ "faster-whisper==1.0.2", - "PyJWT[crypto]==2.8.0", + "PyJWT[crypto]==2.9.0", "authlib==1.3.1", "black==24.8.0", - "langfuse==2.39.2", + "langfuse==2.43.3", "youtube-transcript-api==0.6.2", "pytube==15.0.0", diff --git a/requirements-dev.lock b/requirements-dev.lock index da1f66fcc..6b3f51851 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -10,7 +10,9 @@ # universal: false -e file:. -aiohttp==3.9.5 +aiohappyeyeballs==2.3.5 + # via aiohttp +aiohttp==3.10.2 # via langchain # via langchain-community # via open-webui @@ -84,9 +86,9 @@ chardet==5.2.0 charset-normalizer==3.3.2 # via requests # via unstructured-client -chroma-hnswlib==0.7.5 +chroma-hnswlib==0.7.6 # via chromadb -chromadb==0.5.4 +chromadb==0.5.5 # via langchain-chroma # via open-webui click==8.1.7 @@ -269,7 +271,7 @@ jsonpointer==2.4 # via jsonpatch kubernetes==29.0.0 # via chromadb -langchain==0.2.11 +langchain==0.2.12 # via langchain-community # via open-webui langchain-chroma==0.1.2 @@ -285,7 +287,7 @@ langchain-text-splitters==0.2.0 # via langchain langdetect==1.0.9 # via unstructured -langfuse==2.39.2 +langfuse==2.43.3 # via open-webui langsmith==0.1.96 # via langchain @@ -491,7 +493,7 @@ pydub==0.25.1 # via open-webui pygments==2.18.0 # via rich -pyjwt==2.8.0 +pyjwt==2.9.0 # via open-webui pymongo==4.8.0 # via open-webui diff --git a/requirements.lock b/requirements.lock index da1f66fcc..6b3f51851 100644 --- a/requirements.lock +++ b/requirements.lock @@ -10,7 +10,9 @@ # universal: false -e file:. -aiohttp==3.9.5 +aiohappyeyeballs==2.3.5 + # via aiohttp +aiohttp==3.10.2 # via langchain # via langchain-community # via open-webui @@ -84,9 +86,9 @@ chardet==5.2.0 charset-normalizer==3.3.2 # via requests # via unstructured-client -chroma-hnswlib==0.7.5 +chroma-hnswlib==0.7.6 # via chromadb -chromadb==0.5.4 +chromadb==0.5.5 # via langchain-chroma # via open-webui click==8.1.7 @@ -269,7 +271,7 @@ jsonpointer==2.4 # via jsonpatch kubernetes==29.0.0 # via chromadb -langchain==0.2.11 +langchain==0.2.12 # via langchain-community # via open-webui langchain-chroma==0.1.2 @@ -285,7 +287,7 @@ langchain-text-splitters==0.2.0 # via langchain langdetect==1.0.9 # via unstructured -langfuse==2.39.2 +langfuse==2.43.3 # via open-webui langsmith==0.1.96 # via langchain @@ -491,7 +493,7 @@ pydub==0.25.1 # via open-webui pygments==2.18.0 # via rich -pyjwt==2.8.0 +pyjwt==2.9.0 # via open-webui pymongo==4.8.0 # via open-webui diff --git a/src/app.html b/src/app.html index 5d48e1d7e..718f7e194 100644 --- a/src/app.html +++ b/src/app.html @@ -1,4 +1,4 @@ - + diff --git a/src/lib/apis/index.ts b/src/lib/apis/index.ts index c2e90855b..c4778cadb 100644 --- a/src/lib/apis/index.ts +++ b/src/lib/apis/index.ts @@ -69,6 +69,7 @@ type ChatCompletedForm = { model: string; messages: string[]; chat_id: string; + session_id: string; }; export const chatCompleted = async (token: string, body: ChatCompletedForm) => { diff --git a/src/lib/apis/ollama/index.ts b/src/lib/apis/ollama/index.ts index 084d2d5f1..c4c449156 100644 --- a/src/lib/apis/ollama/index.ts +++ b/src/lib/apis/ollama/index.ts @@ -1,5 +1,4 @@ import { OLLAMA_API_BASE_URL } from '$lib/constants'; -import { titleGenerationTemplate } from '$lib/utils'; export const getOllamaConfig = async (token: string = '') => { let error = null; @@ -203,55 +202,6 @@ export const getOllamaModels = async (token: string = '') => { }); }; -// TODO: migrate to backend -export const generateTitle = async ( - token: string = '', - template: string, - model: string, - prompt: string -) => { - let error = null; - - template = titleGenerationTemplate(template, prompt); - - console.log(template); - - const res = await fetch(`${OLLAMA_API_BASE_URL}/api/generate`, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - Authorization: `Bearer ${token}` - }, - body: JSON.stringify({ - model: model, - prompt: template, - stream: false, - options: { - // Restrict the number of tokens generated to 50 - num_predict: 50 - } - }) - }) - .then(async (res) => { - if (!res.ok) throw await res.json(); - return res.json(); - }) - .catch((err) => { - console.log(err); - if ('detail' in err) { - error = err.detail; - } - return null; - }); - - if (error) { - throw error; - } - - return res?.response.replace(/["']/g, '') ?? 'New Chat'; -}; - export const generatePrompt = async (token: string = '', model: string, conversation: string) => { let error = null; diff --git a/src/lib/apis/openai/index.ts b/src/lib/apis/openai/index.ts index 2a52ebb32..2bb11d12a 100644 --- a/src/lib/apis/openai/index.ts +++ b/src/lib/apis/openai/index.ts @@ -1,6 +1,4 @@ import { OPENAI_API_BASE_URL } from '$lib/constants'; -import { titleGenerationTemplate } from '$lib/utils'; -import { type Model, models, settings } from '$lib/stores'; export const getOpenAIConfig = async (token: string = '') => { let error = null; @@ -260,7 +258,7 @@ export const getOpenAIModelsDirect = async ( throw error; } - const models = Array.isArray(res) ? res : res?.data ?? null; + const models = Array.isArray(res) ? res : (res?.data ?? null); return models .map((model) => ({ id: model.id, name: model.name ?? model.id, external: true })) @@ -330,126 +328,3 @@ export const synthesizeOpenAISpeech = async ( return res; }; - -export const generateTitle = async ( - token: string = '', - template: string, - model: string, - prompt: string, - chat_id?: string, - url: string = OPENAI_API_BASE_URL -) => { - let error = null; - - template = titleGenerationTemplate(template, prompt); - - console.log(template); - - const res = await fetch(`${url}/chat/completions`, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - Authorization: `Bearer ${token}` - }, - body: JSON.stringify({ - model: model, - messages: [ - { - role: 'user', - content: template - } - ], - stream: false, - // Restricting the max tokens to 50 to avoid long titles - max_tokens: 50, - ...(chat_id && { chat_id: chat_id }), - title: true - }) - }) - .then(async (res) => { - if (!res.ok) throw await res.json(); - return res.json(); - }) - .catch((err) => { - console.log(err); - if ('detail' in err) { - error = err.detail; - } - return null; - }); - - if (error) { - throw error; - } - - return res?.choices[0]?.message?.content.replace(/["']/g, '') ?? 'New Chat'; -}; - -export const generateSearchQuery = async ( - token: string = '', - model: string, - previousMessages: string[], - prompt: string, - url: string = OPENAI_API_BASE_URL -): Promise => { - let error = null; - - // TODO: Allow users to specify the prompt - // Get the current date in the format "January 20, 2024" - const currentDate = new Intl.DateTimeFormat('en-US', { - year: 'numeric', - month: 'long', - day: '2-digit' - }).format(new Date()); - - const res = await fetch(`${url}/chat/completions`, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - Authorization: `Bearer ${token}` - }, - body: JSON.stringify({ - model: model, - // Few shot prompting - messages: [ - { - role: 'assistant', - content: `You are tasked with generating web search queries. Give me an appropriate query to answer my question for google search. Answer with only the query. Today is ${currentDate}.` - }, - { - role: 'user', - content: prompt - } - // { - // role: 'user', - // content: - // (previousMessages.length > 0 - // ? `Previous Questions:\n${previousMessages.join('\n')}\n\n` - // : '') + `Current Question: ${prompt}` - // } - ], - stream: false, - // Restricting the max tokens to 30 to avoid long search queries - max_tokens: 30 - }) - }) - .then(async (res) => { - if (!res.ok) throw await res.json(); - return res.json(); - }) - .catch((err) => { - console.log(err); - if ('detail' in err) { - error = err.detail; - } - return undefined; - }); - - if (error) { - throw error; - } - - return res?.choices[0]?.message?.content.replace(/["']/g, '') ?? undefined; -}; diff --git a/src/lib/components/ChangelogModal.svelte b/src/lib/components/ChangelogModal.svelte index 48156f924..6a24ea5d9 100644 --- a/src/lib/components/ChangelogModal.svelte +++ b/src/lib/components/ChangelogModal.svelte @@ -75,12 +75,12 @@ class="font-semibold uppercase text-xs {section === 'added' ? 'text-white bg-blue-600' : section === 'fixed' - ? 'text-white bg-green-600' - : section === 'changed' - ? 'text-white bg-yellow-600' - : section === 'removed' - ? 'text-white bg-red-600' - : ''} w-fit px-3 rounded-full my-2.5" + ? 'text-white bg-green-600' + : section === 'changed' + ? 'text-white bg-yellow-600' + : section === 'removed' + ? 'text-white bg-red-600' + : ''} w-fit px-3 rounded-full my-2.5" > {section} diff --git a/src/lib/components/admin/Settings/Documents.svelte b/src/lib/components/admin/Settings/Documents.svelte index 1b0b2c3fa..bac84902f 100644 --- a/src/lib/components/admin/Settings/Documents.svelte +++ b/src/lib/components/admin/Settings/Documents.svelte @@ -112,7 +112,7 @@ url: OpenAIUrl, batch_size: OpenAIBatchSize } - } + } : {}) }).catch(async (error) => { toast.error(error); diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index 2c42c2046..64b51be96 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -579,8 +579,8 @@ let selectedModelIds = modelId ? [modelId] : atSelectedModel !== undefined - ? [atSelectedModel.id] - : selectedModels; + ? [atSelectedModel.id] + : selectedModels; // Create response messages for each selected model const responseMessageIds = {}; @@ -739,11 +739,11 @@ ? await getAndUpdateUserLocation(localStorage.token) : undefined )}${ - responseMessage?.userContext ?? null + (responseMessage?.userContext ?? null) ? `\n\nUser Context:\n${responseMessage?.userContext ?? ''}` : '' }` - } + } : undefined, ...messages ] @@ -811,10 +811,10 @@ options: { ...(params ?? $settings.params ?? {}), stop: - params?.stop ?? $settings?.params?.stop ?? undefined + (params?.stop ?? $settings?.params?.stop ?? undefined) ? (params?.stop.split(',').map((token) => token.trim()) ?? $settings.params.stop).map( (str) => decodeURIComponent(JSON.parse('"' + str.replace(/\"/g, '\\"') + '"')) - ) + ) : undefined, num_predict: params?.max_tokens ?? $settings?.params?.max_tokens ?? undefined, repeat_penalty: @@ -877,6 +877,10 @@ } else { responseMessage.content += data.message.content; + if (navigator.vibrate && ($settings?.hapticFeedback ?? false)) { + navigator.vibrate(5); + } + const sentences = extractSentencesForAudio(responseMessage.content); sentences.pop(); @@ -1056,10 +1060,10 @@ stream: true, model: model.id, stream_options: - model.info?.meta?.capabilities?.usage ?? false + (model.info?.meta?.capabilities?.usage ?? false) ? { include_usage: true - } + } : undefined, messages: [ params?.system || $settings.system || (responseMessage?.userContext ?? null) @@ -1072,11 +1076,11 @@ ? await getAndUpdateUserLocation(localStorage.token) : undefined )}${ - responseMessage?.userContext ?? null + (responseMessage?.userContext ?? null) ? `\n\nUser Context:\n${responseMessage?.userContext ?? ''}` : '' }` - } + } : undefined, ...messages ] @@ -1092,7 +1096,7 @@ text: arr.length - 1 !== idx ? message.content - : message?.raContent ?? message.content + : (message?.raContent ?? message.content) }, ...message.files .filter((file) => file.type === 'image') @@ -1103,20 +1107,20 @@ } })) ] - } + } : { content: arr.length - 1 !== idx ? message.content - : message?.raContent ?? message.content - }) + : (message?.raContent ?? message.content) + }) })), seed: params?.seed ?? $settings?.params?.seed ?? undefined, stop: - params?.stop ?? $settings?.params?.stop ?? undefined + (params?.stop ?? $settings?.params?.stop ?? undefined) ? (params?.stop.split(',').map((token) => token.trim()) ?? $settings.params.stop).map( (str) => decodeURIComponent(JSON.parse('"' + str.replace(/\"/g, '\\"') + '"')) - ) + ) : undefined, temperature: params?.temperature ?? $settings?.params?.temperature ?? undefined, top_p: params?.top_p ?? $settings?.params?.top_p ?? undefined, @@ -1177,6 +1181,10 @@ } else { responseMessage.content += value; + if (navigator.vibrate && ($settings?.hapticFeedback ?? false)) { + navigator.vibrate(5); + } + const sentences = extractSentencesForAudio(responseMessage.content); sentences.pop(); diff --git a/src/lib/components/chat/Controls/Controls.svelte b/src/lib/components/chat/Controls/Controls.svelte index 69034a305..31b58ab90 100644 --- a/src/lib/components/chat/Controls/Controls.svelte +++ b/src/lib/components/chat/Controls/Controls.svelte @@ -9,6 +9,8 @@ import FileItem from '$lib/components/common/FileItem.svelte'; import Collapsible from '$lib/components/common/Collapsible.svelte'; + import { user } from '$lib/stores'; + export let models = []; export let chatFiles = []; @@ -78,7 +80,7 @@
- +
diff --git a/src/lib/components/chat/MessageInput/CallOverlay.svelte b/src/lib/components/chat/MessageInput/CallOverlay.svelte index e28b4b5bd..ac14cc74e 100644 --- a/src/lib/components/chat/MessageInput/CallOverlay.svelte +++ b/src/lib/components/chat/MessageInput/CallOverlay.svelte @@ -609,10 +609,10 @@ style="font-size:{rmsLevel * 100 > 4 ? '4.5' : rmsLevel * 100 > 2 - ? '4.25' - : rmsLevel * 100 > 1 - ? '3.75' - : '3.5'}rem;width: 100%; text-align:center;" + ? '4.25' + : rmsLevel * 100 > 1 + ? '3.75' + : '3.5'}rem;width: 100%; text-align:center;" > {emoji} @@ -658,10 +658,10 @@ class=" {rmsLevel * 100 > 4 ? ' size-[4.5rem]' : rmsLevel * 100 > 2 - ? ' size-16' - : rmsLevel * 100 > 1 - ? 'size-14' - : 'size-12'} transition-all rounded-full {(model?.info?.meta + ? ' size-16' + : rmsLevel * 100 > 1 + ? 'size-14' + : 'size-12'} transition-all rounded-full {(model?.info?.meta ?.profile_image_url ?? '/static/favicon.png') !== '/static/favicon.png' ? ' bg-cover bg-center bg-no-repeat' : 'bg-black dark:bg-white'} bg-black dark:bg-white" @@ -691,10 +691,10 @@ style="font-size:{rmsLevel * 100 > 4 ? '13' : rmsLevel * 100 > 2 - ? '12' - : rmsLevel * 100 > 1 - ? '11.5' - : '11'}rem;width:100%;text-align:center;" + ? '12' + : rmsLevel * 100 > 1 + ? '11.5' + : '11'}rem;width:100%;text-align:center;" > {emoji} @@ -740,10 +740,10 @@ class=" {rmsLevel * 100 > 4 ? ' size-52' : rmsLevel * 100 > 2 - ? 'size-48' - : rmsLevel * 100 > 1 - ? 'size-[11.5rem]' - : 'size-44'} transition-all rounded-full {(model?.info?.meta + ? 'size-48' + : rmsLevel * 100 > 1 + ? 'size-[11.5rem]' + : 'size-44'} transition-all rounded-full {(model?.info?.meta ?.profile_image_url ?? '/static/favicon.png') !== '/static/favicon.png' ? ' bg-cover bg-center bg-no-repeat' : 'bg-black dark:bg-white'} " diff --git a/src/lib/components/chat/MessageInput/Documents.svelte b/src/lib/components/chat/MessageInput/Documents.svelte index 64c4bc458..50956e4c0 100644 --- a/src/lib/components/chat/MessageInput/Documents.svelte +++ b/src/lib/components/chat/MessageInput/Documents.svelte @@ -27,7 +27,7 @@ title: $i18n.t('All Documents'), collection_names: $documents.map((doc) => doc.collection_name) } - ] + ] : []), ...$documents .reduce((a, e, i, arr) => { diff --git a/src/lib/components/chat/Messages.svelte b/src/lib/components/chat/Messages.svelte index 9e3c147b1..fb6754e86 100644 --- a/src/lib/components/chat/Messages.svelte +++ b/src/lib/components/chat/Messages.svelte @@ -305,7 +305,7 @@ {#each messages as message, messageIdx}
@@ -317,10 +317,10 @@ {message} isFirstMessage={messageIdx === 0} siblings={message.parentId !== null - ? history.messages[message.parentId]?.childrenIds ?? [] - : Object.values(history.messages) + ? (history.messages[message.parentId]?.childrenIds ?? []) + : (Object.values(history.messages) .filter((message) => message.parentId === null) - .map((message) => message.id) ?? []} + .map((message) => message.id) ?? [])} {confirmEditMessage} {showPreviousMessage} {showNextMessage} diff --git a/src/lib/components/chat/Messages/CitationsModal.svelte b/src/lib/components/chat/Messages/CitationsModal.svelte index 83b82f8f8..e8d63e080 100644 --- a/src/lib/components/chat/Messages/CitationsModal.svelte +++ b/src/lib/components/chat/Messages/CitationsModal.svelte @@ -60,8 +60,8 @@ href={document?.metadata?.file_id ? `/api/v1/files/${document?.metadata?.file_id}/content` : document.source.name.includes('http') - ? document.source.name - : `#`} + ? document.source.name + : `#`} target="_blank" > {document?.metadata?.name ?? document.source.name} diff --git a/src/lib/components/chat/Messages/CodeBlock.svelte b/src/lib/components/chat/Messages/CodeBlock.svelte index daa8a8cd0..bb10af409 100644 --- a/src/lib/components/chat/Messages/CodeBlock.svelte +++ b/src/lib/components/chat/Messages/CodeBlock.svelte @@ -1,17 +1,25 @@
-
-
{@html lang}
+ {#if lang === 'mermaid'} + {#if mermaidHtml} + {@html mermaidHtml} + {:else} +
{code}
+ {/if} + {:else} +
+
{@html lang}
-
- {#if lang.toLowerCase() === 'python' || lang.toLowerCase() === 'py' || (lang === '' && checkPythonCode(code))} - {#if executing} -
Running
- {:else} - +
+ {#if lang.toLowerCase() === 'python' || lang.toLowerCase() === 'py' || (lang === '' && checkPythonCode(code))} + {#if executing} +
Running
+ {:else} + + {/if} {/if} - {/if} - + +
-
-
{#if highlightedCode}{@html highlightedCode}{:else}{code}{/if}
+
{#if highlightedCode}{@html highlightedCode}{:else}{code}{/if}
-
+
- {#if executing} -
-
STDOUT/STDERR
-
Running...
-
- {:else if stdout || stderr || result} -
-
STDOUT/STDERR
-
{stdout || stderr || result}
-
+ {#if executing} +
+
STDOUT/STDERR
+
Running...
+
+ {:else if stdout || stderr || result} +
+
STDOUT/STDERR
+
{stdout || stderr || result}
+
+ {/if} {/if}
diff --git a/src/lib/components/chat/Messages/KatexRenderer.svelte b/src/lib/components/chat/Messages/KatexRenderer.svelte new file mode 100644 index 000000000..766454c29 --- /dev/null +++ b/src/lib/components/chat/Messages/KatexRenderer.svelte @@ -0,0 +1,9 @@ + + +{@html katex.renderToString(content, { displayMode, throwOnError: false })} diff --git a/src/lib/components/chat/Messages/MarkdownInlineTokens.svelte b/src/lib/components/chat/Messages/MarkdownInlineTokens.svelte index 19d22de37..4567cf507 100644 --- a/src/lib/components/chat/Messages/MarkdownInlineTokens.svelte +++ b/src/lib/components/chat/Messages/MarkdownInlineTokens.svelte @@ -1,8 +1,11 @@ @@ -25,14 +28,18 @@ {:else if token.type === 'codespan'} - {unescapeHtml(token.text.replaceAll('&', '&'))} + {revertSanitizedResponseContent(token.raw)} {:else if token.type === 'br'}
{:else if token.type === 'del'} + {:else if token.type === 'inlineKatex'} + {#if token.text} + + {/if} {:else if token.type === 'text'} - {unescapeHtml(token.text)} + {token.raw} {/if} {/each} diff --git a/src/lib/components/chat/Messages/MarkdownTokens.svelte b/src/lib/components/chat/Messages/MarkdownTokens.svelte index 33b898485..028524aa5 100644 --- a/src/lib/components/chat/Messages/MarkdownTokens.svelte +++ b/src/lib/components/chat/Messages/MarkdownTokens.svelte @@ -1,137 +1,132 @@ -
- {#each tokens as token, tokenIdx (`${id}-${tokenIdx}`)} - {#if token.type === 'code'} - {#if token.lang === 'mermaid'} -
{revertSanitizedResponseContent(token.text)}
- {:else} - - {/if} + +{#each tokens as token, tokenIdx} + {#if token.type === 'hr'} +
+ {:else if token.type === 'heading'} + + + + {:else if token.type === 'code'} + + {:else if token.type === 'table'} + + + + {#each token.header as header, headerIdx} + + {/each} + + + + {#each token.rows as row, rowIdx} + + {#each row ?? [] as cell, cellIdx} + + {/each} + + {/each} + +
+ +
+ +
+ {:else if token.type === 'blockquote'} +
+ +
+ {:else if token.type === 'list'} + {#if token.ordered} +
    + {#each token.items as item, itemIdx} +
  1. + +
  2. + {/each} +
{:else} - {@html marked.parse(token.raw, { - ...defaults, - gfm: true, - breaks: true, - renderer - })} +
    + {#each token.items as item, itemIdx} +
  • + +
  • + {/each} +
{/if} - {/each} -
+ {:else if token.type === 'html'} + {@html token.text} + {:else if token.type === 'paragraph'} +

+ +

+ {:else if token.type === 'text'} + {#if top} +

+ {#if token.tokens} + + {:else} + {unescapeHtml(token.text)} + {/if} +

+ {:else if token.tokens} + + {:else} + {unescapeHtml(token.text)} + {/if} + {:else if token.type === 'inlineKatex'} + {#if token.text} + + {/if} + {:else if token.type === 'blockKatex'} + {#if token.text} + + {/if} + {:else if token.type === 'space'} + {''} + {:else} + {console.log('Unknown token', token)} + {/if} +{/each} diff --git a/src/lib/components/chat/Messages/ResponseMessage.svelte b/src/lib/components/chat/Messages/ResponseMessage.svelte index fdc846205..8dd332d25 100644 --- a/src/lib/components/chat/Messages/ResponseMessage.svelte +++ b/src/lib/components/chat/Messages/ResponseMessage.svelte @@ -2,10 +2,6 @@ import { toast } from 'svelte-sonner'; import dayjs from 'dayjs'; import { marked } from 'marked'; - import tippy from 'tippy.js'; - import auto_render from 'katex/dist/contrib/auto-render.mjs'; - import 'katex/dist/katex.min.css'; - import mermaid from 'mermaid'; import { fade } from 'svelte/transition'; import { createEventDispatcher } from 'svelte'; @@ -79,104 +75,24 @@ let tokens; + import 'katex/dist/katex.min.css'; + + import markedKatex from '$lib/utils/katex-extension'; + + const options = { + throwOnError: false + }; + + marked.use(markedKatex(options)); + $: (async () => { if (message?.content) { tokens = marked.lexer( replaceTokens(sanitizeResponseContent(message?.content), model?.name, $user?.name) ); - // console.log(message?.content, tokens); } })(); - $: if (message) { - renderStyling(); - } - - const renderStyling = async () => { - await tick(); - - if (tooltipInstance) { - tooltipInstance[0]?.destroy(); - } - - renderLatex(); - - if (message.info) { - let tooltipContent = ''; - if (message.info.openai) { - tooltipContent = `prompt_tokens: ${message.info.prompt_tokens ?? 'N/A'}
- completion_tokens: ${message.info.completion_tokens ?? 'N/A'}
- total_tokens: ${message.info.total_tokens ?? 'N/A'}`; - } else { - tooltipContent = `response_token/s: ${ - `${ - Math.round( - ((message.info.eval_count ?? 0) / (message.info.eval_duration / 1000000000)) * 100 - ) / 100 - } tokens` ?? 'N/A' - }
- prompt_token/s: ${ - Math.round( - ((message.info.prompt_eval_count ?? 0) / - (message.info.prompt_eval_duration / 1000000000)) * - 100 - ) / 100 ?? 'N/A' - } tokens
- total_duration: ${ - Math.round(((message.info.total_duration ?? 0) / 1000000) * 100) / 100 ?? - 'N/A' - }ms
- load_duration: ${ - Math.round(((message.info.load_duration ?? 0) / 1000000) * 100) / 100 ?? 'N/A' - }ms
- prompt_eval_count: ${message.info.prompt_eval_count ?? 'N/A'}
- prompt_eval_duration: ${ - Math.round(((message.info.prompt_eval_duration ?? 0) / 1000000) * 100) / - 100 ?? 'N/A' - }ms
- eval_count: ${message.info.eval_count ?? 'N/A'}
- eval_duration: ${ - Math.round(((message.info.eval_duration ?? 0) / 1000000) * 100) / 100 ?? 'N/A' - }ms
- approximate_total: ${approximateToHumanReadable(message.info.total_duration)}`; - } - tooltipInstance = tippy(`#info-${message.id}`, { - content: `${tooltipContent}`, - allowHTML: true, - theme: 'dark', - arrow: false, - offset: [0, 4] - }); - } - }; - - const renderLatex = () => { - let chatMessageElements = document - .getElementById(`message-${message.id}`) - ?.getElementsByClassName('chat-assistant'); - - if (chatMessageElements) { - for (const element of chatMessageElements) { - auto_render(element, { - // customised options - // • auto-render specific keys, e.g.: - delimiters: [ - { left: '$$', right: '$$', display: false }, - { left: '$ ', right: ' $', display: false }, - { left: '\\pu{', right: '}', display: false }, - { left: '\\ce{', right: '}', display: false }, - { left: '\\(', right: '\\)', display: false }, - { left: '( ', right: ' )', display: false }, - { left: '\\[', right: '\\]', display: false }, - { left: '[ ', right: ' ]', display: false } - ], - // • rendering keys, e.g.: - throwOnError: false - }); - } - } - }; - const playAudio = (idx) => { return new Promise((res) => { speakingIdx = idx; @@ -242,7 +158,7 @@ const res = await synthesizeOpenAISpeech( localStorage.token, $settings?.audio?.tts?.defaultVoice === $config.audio.tts.voice - ? $settings?.audio?.tts?.voice ?? $config?.audio?.tts?.voice + ? ($settings?.audio?.tts?.voice ?? $config?.audio?.tts?.voice) : $config?.audio?.tts?.voice, sentence ).catch((error) => { @@ -330,14 +246,12 @@ editedContent = ''; await tick(); - renderStyling(); }; const cancelEditMessage = async () => { edit = false; editedContent = ''; await tick(); - renderStyling(); }; const generateImage = async (message) => { @@ -362,21 +276,11 @@ $: if (!edit) { (async () => { await tick(); - renderStyling(); - - await mermaid.run({ - querySelector: '.mermaid' - }); })(); } onMount(async () => { await tick(); - renderStyling(); - - await mermaid.run({ - querySelector: '.mermaid' - }); }); @@ -420,7 +324,7 @@ {/if}
{#if (message?.statusHistory ?? [...(message?.status ? [message?.status] : [])]).length > 0} @@ -841,31 +745,71 @@ {/if} {#if message.info} - - + + + + + {/if} diff --git a/src/lib/components/chat/Messages/UserMessage.svelte b/src/lib/components/chat/Messages/UserMessage.svelte index 22b7081d7..1dea97f11 100644 --- a/src/lib/components/chat/Messages/UserMessage.svelte +++ b/src/lib/components/chat/Messages/UserMessage.svelte @@ -62,8 +62,8 @@ {#if !($settings?.chatBubble ?? true)} m.id === message.user)?.info?.meta?.profile_image_url ?? '/user.png' - : user?.profile_image_url ?? '/user.png'} + ? ($models.find((m) => m.id === message.user)?.info?.meta?.profile_image_url ?? '/user.png') + : (user?.profile_image_url ?? '/user.png')} /> {/if}
@@ -96,7 +96,7 @@ {#if message.files}
{#each message.files as file} -
+
{#if file.type === 'image'} input {:else} @@ -162,12 +162,12 @@
{:else}
-
+
{message.content}
@@ -175,7 +175,7 @@
diff --git a/src/lib/components/chat/ModelSelector/Selector.svelte b/src/lib/components/chat/ModelSelector/Selector.svelte index d36f3aa72..3471d344a 100644 --- a/src/lib/components/chat/ModelSelector/Selector.svelte +++ b/src/lib/components/chat/ModelSelector/Selector.svelte @@ -66,7 +66,7 @@ $: filteredItems = searchValue ? fuse.search(searchValue).map((e) => { return e.item; - }) + }) : items.filter((item) => !item.model?.info?.meta?.hidden); const pullModelHandler = async () => { diff --git a/src/lib/components/chat/Settings/About.svelte b/src/lib/components/chat/Settings/About.svelte index 11177d45b..2c21e3926 100644 --- a/src/lib/components/chat/Settings/About.svelte +++ b/src/lib/components/chat/Settings/About.svelte @@ -65,8 +65,8 @@ {updateAvailable === null ? $i18n.t('Checking for updates...') : updateAvailable - ? `(v${version.latest} ${$i18n.t('available!')})` - : $i18n.t('(latest)')} + ? `(v${version.latest} ${$i18n.t('available!')})` + : $i18n.t('(latest)')}
diff --git a/src/lib/components/chat/Settings/Advanced/AdvancedParams.svelte b/src/lib/components/chat/Settings/Advanced/AdvancedParams.svelte index ad9ea2104..915874aec 100644 --- a/src/lib/components/chat/Settings/Advanced/AdvancedParams.svelte +++ b/src/lib/components/chat/Settings/Advanced/AdvancedParams.svelte @@ -29,6 +29,7 @@ use_mmap: null, use_mlock: null, num_thread: null, + num_gpu: null, template: null }; @@ -864,6 +865,52 @@ {/if}
+
+
+
{$i18n.t('num_gpu (Ollama)')}
+ + +
+ + {#if (params?.num_gpu ?? null) !== null} +
+
+ +
+
+ +
+
+ {/if} +
+