Browse Source

Merge pull request #3669 from open-webui/dev-migration-session

dev
Timothy Jaeryang Baek 10 months ago
parent
commit
4e75150174

+ 47 - 0
backend/apps/webui/internal/db.py

@@ -2,6 +2,10 @@ import os
 import logging
 import logging
 import json
 import json
 from contextlib import contextmanager
 from contextlib import contextmanager
+
+from peewee_migrate import Router
+from apps.webui.internal.wrappers import register_connection
+
 from typing import Optional, Any
 from typing import Optional, Any
 from typing_extensions import Self
 from typing_extensions import Self
 
 
@@ -46,6 +50,35 @@ if os.path.exists(f"{DATA_DIR}/ollama.db"):
 else:
 else:
     pass
     pass
 
 
+
+# Workaround to handle the peewee migration
+# This is required to ensure the peewee migration is handled before the alembic migration
+def handle_peewee_migration():
+    try:
+        db = register_connection(DATABASE_URL)
+        migrate_dir = BACKEND_DIR / "apps" / "webui" / "internal" / "migrations"
+        router = Router(db, logger=log, migrate_dir=migrate_dir)
+        router.run()
+        db.close()
+
+        # check if db connection has been closed
+
+    except Exception as e:
+        log.error(f"Failed to initialize the database connection: {e}")
+        raise
+
+    finally:
+        # Properly closing the database connection
+        if db and not db.is_closed():
+            db.close()
+
+        # Assert if db connection has been closed
+        assert db.is_closed(), "Database connection is still open."
+
+
+handle_peewee_migration()
+
+
 SQLALCHEMY_DATABASE_URL = DATABASE_URL
 SQLALCHEMY_DATABASE_URL = DATABASE_URL
 if "sqlite" in SQLALCHEMY_DATABASE_URL:
 if "sqlite" in SQLALCHEMY_DATABASE_URL:
     engine = create_engine(
     engine = create_engine(
@@ -53,8 +86,22 @@ if "sqlite" in SQLALCHEMY_DATABASE_URL:
     )
     )
 else:
 else:
     engine = create_engine(SQLALCHEMY_DATABASE_URL, pool_pre_ping=True)
     engine = create_engine(SQLALCHEMY_DATABASE_URL, pool_pre_ping=True)
+
+
 SessionLocal = sessionmaker(
 SessionLocal = sessionmaker(
     autocommit=False, autoflush=False, bind=engine, expire_on_commit=False
     autocommit=False, autoflush=False, bind=engine, expire_on_commit=False
 )
 )
 Base = declarative_base()
 Base = declarative_base()
 Session = scoped_session(SessionLocal)
 Session = scoped_session(SessionLocal)
+
+
+# Dependency
+def get_session():
+    db = SessionLocal()
+    try:
+        yield db
+    finally:
+        db.close()
+
+
+get_db = contextmanager(get_session)

+ 72 - 0
backend/apps/webui/internal/wrappers.py

@@ -0,0 +1,72 @@
+from contextvars import ContextVar
+from peewee import *
+from peewee import PostgresqlDatabase, InterfaceError as PeeWeeInterfaceError
+
+import logging
+from playhouse.db_url import connect, parse
+from playhouse.shortcuts import ReconnectMixin
+
+from config import SRC_LOG_LEVELS
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["DB"])
+
+db_state_default = {"closed": None, "conn": None, "ctx": None, "transactions": None}
+db_state = ContextVar("db_state", default=db_state_default.copy())
+
+
+class PeeweeConnectionState(object):
+    def __init__(self, **kwargs):
+        super().__setattr__("_state", db_state)
+        super().__init__(**kwargs)
+
+    def __setattr__(self, name, value):
+        self._state.get()[name] = value
+
+    def __getattr__(self, name):
+        value = self._state.get()[name]
+        return value
+
+
+class CustomReconnectMixin(ReconnectMixin):
+    reconnect_errors = (
+        # psycopg2
+        (OperationalError, "termin"),
+        (InterfaceError, "closed"),
+        # peewee
+        (PeeWeeInterfaceError, "closed"),
+    )
+
+
+class ReconnectingPostgresqlDatabase(CustomReconnectMixin, PostgresqlDatabase):
+    pass
+
+
+def register_connection(db_url):
+    db = connect(db_url)
+    if isinstance(db, PostgresqlDatabase):
+        # Enable autoconnect for SQLite databases, managed by Peewee
+        db.autoconnect = True
+        db.reuse_if_open = True
+        log.info("Connected to PostgreSQL database")
+
+        # Get the connection details
+        connection = parse(db_url)
+
+        # Use our custom database class that supports reconnection
+        db = ReconnectingPostgresqlDatabase(
+            connection["database"],
+            user=connection["user"],
+            password=connection["password"],
+            host=connection["host"],
+            port=connection["port"],
+        )
+        db.connect(reuse_if_open=True)
+    elif isinstance(db, SqliteDatabase):
+        # Enable autoconnect for SQLite databases, managed by Peewee
+        db.autoconnect = True
+        db.reuse_if_open = True
+        log.info("Connected to SQLite database")
+    else:
+        raise ValueError("Unsupported database connection")
+    return db

+ 76 - 0
backend/apps/webui/main.py

@@ -19,8 +19,13 @@ from apps.webui.routers import (
     functions,
     functions,
 )
 )
 from apps.webui.models.functions import Functions
 from apps.webui.models.functions import Functions
+from apps.webui.models.models import Models
+
 from apps.webui.utils import load_function_module_by_id
 from apps.webui.utils import load_function_module_by_id
+
 from utils.misc import stream_message_template
 from utils.misc import stream_message_template
+from utils.task import prompt_template
+
 
 
 from config import (
 from config import (
     WEBUI_BUILD_HASH,
     WEBUI_BUILD_HASH,
@@ -186,6 +191,77 @@ async def get_pipe_models():
 
 
 
 
 async def generate_function_chat_completion(form_data, user):
 async def generate_function_chat_completion(form_data, user):
+    model_id = form_data.get("model")
+    model_info = Models.get_model_by_id(model_id)
+
+    if model_info:
+        if model_info.base_model_id:
+            form_data["model"] = model_info.base_model_id
+
+        model_info.params = model_info.params.model_dump()
+
+        if model_info.params:
+            if model_info.params.get("temperature", None) is not None:
+                form_data["temperature"] = float(model_info.params.get("temperature"))
+
+            if model_info.params.get("top_p", None):
+                form_data["top_p"] = int(model_info.params.get("top_p", None))
+
+            if model_info.params.get("max_tokens", None):
+                form_data["max_tokens"] = int(model_info.params.get("max_tokens", None))
+
+            if model_info.params.get("frequency_penalty", None):
+                form_data["frequency_penalty"] = int(
+                    model_info.params.get("frequency_penalty", None)
+                )
+
+            if model_info.params.get("seed", None):
+                form_data["seed"] = model_info.params.get("seed", None)
+
+            if model_info.params.get("stop", None):
+                form_data["stop"] = (
+                    [
+                        bytes(stop, "utf-8").decode("unicode_escape")
+                        for stop in model_info.params["stop"]
+                    ]
+                    if model_info.params.get("stop", None)
+                    else 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 {}
+                ),
+            )
+            # Check if the payload already has a system message
+            # If not, add a system message to the payload
+            if form_data.get("messages"):
+                for message in form_data["messages"]:
+                    if message.get("role") == "system":
+                        message["content"] = system + message["content"]
+                        break
+                else:
+                    form_data["messages"].insert(
+                        0,
+                        {
+                            "role": "system",
+                            "content": system,
+                        },
+                    )
+
+    else:
+        pass
+
     async def job():
     async def job():
         pipe_id = form_data["model"]
         pipe_id = form_data["model"]
         if "." in pipe_id:
         if "." in pipe_id:

+ 52 - 41
backend/apps/webui/models/auths.py

@@ -7,7 +7,7 @@ from sqlalchemy import String, Column, Boolean, Text
 from apps.webui.models.users import UserModel, Users
 from apps.webui.models.users import UserModel, Users
 from utils.utils import verify_password
 from utils.utils import verify_password
 
 
-from apps.webui.internal.db import Base, Session
+from apps.webui.internal.db import Base, get_db
 
 
 from config import SRC_LOG_LEVELS
 from config import SRC_LOG_LEVELS
 
 
@@ -102,40 +102,44 @@ class AuthsTable:
         role: str = "pending",
         role: str = "pending",
         oauth_sub: Optional[str] = None,
         oauth_sub: Optional[str] = None,
     ) -> Optional[UserModel]:
     ) -> Optional[UserModel]:
-        log.info("insert_new_auth")
+        with get_db() as db:
 
 
-        id = str(uuid.uuid4())
+            log.info("insert_new_auth")
 
 
-        auth = AuthModel(
-            **{"id": id, "email": email, "password": password, "active": True}
-        )
-        result = Auth(**auth.model_dump())
-        Session.add(result)
+            id = str(uuid.uuid4())
 
 
-        user = Users.insert_new_user(
-            id, name, email, profile_image_url, role, oauth_sub
-        )
+            auth = AuthModel(
+                **{"id": id, "email": email, "password": password, "active": True}
+            )
+            result = Auth(**auth.model_dump())
+            db.add(result)
 
 
-        Session.commit()
-        Session.refresh(result)
+            user = Users.insert_new_user(
+                id, name, email, profile_image_url, role, oauth_sub
+            )
 
 
-        if result and user:
-            return user
-        else:
-            return None
+            db.commit()
+            db.refresh(result)
+
+            if result and user:
+                return user
+            else:
+                return None
 
 
     def authenticate_user(self, email: str, password: str) -> Optional[UserModel]:
     def authenticate_user(self, email: str, password: str) -> Optional[UserModel]:
         log.info(f"authenticate_user: {email}")
         log.info(f"authenticate_user: {email}")
         try:
         try:
-            auth = Session.query(Auth).filter_by(email=email, active=True).first()
-            if auth:
-                if verify_password(password, auth.password):
-                    user = Users.get_user_by_id(auth.id)
-                    return user
+            with get_db() as db:
+
+                auth = db.query(Auth).filter_by(email=email, active=True).first()
+                if auth:
+                    if verify_password(password, auth.password):
+                        user = Users.get_user_by_id(auth.id)
+                        return user
+                    else:
+                        return None
                 else:
                 else:
                     return None
                     return None
-            else:
-                return None
         except:
         except:
             return None
             return None
 
 
@@ -154,40 +158,47 @@ class AuthsTable:
     def authenticate_user_by_trusted_header(self, email: str) -> Optional[UserModel]:
     def authenticate_user_by_trusted_header(self, email: str) -> Optional[UserModel]:
         log.info(f"authenticate_user_by_trusted_header: {email}")
         log.info(f"authenticate_user_by_trusted_header: {email}")
         try:
         try:
-            auth = Session.query(Auth).filter(email=email, active=True).first()
-            if auth:
-                user = Users.get_user_by_id(auth.id)
-                return user
+            with get_db() as db:
+                auth = db.query(Auth).filter(email=email, active=True).first()
+                if auth:
+                    user = Users.get_user_by_id(auth.id)
+                    return user
         except:
         except:
             return None
             return None
 
 
     def update_user_password_by_id(self, id: str, new_password: str) -> bool:
     def update_user_password_by_id(self, id: str, new_password: str) -> bool:
         try:
         try:
-            result = (
-                Session.query(Auth).filter_by(id=id).update({"password": new_password})
-            )
-            return True if result == 1 else False
+            with get_db() as db:
+
+                result = (
+                    db.query(Auth).filter_by(id=id).update({"password": new_password})
+                )
+                return True if result == 1 else False
         except:
         except:
             return False
             return False
 
 
     def update_email_by_id(self, id: str, email: str) -> bool:
     def update_email_by_id(self, id: str, email: str) -> bool:
         try:
         try:
-            result = Session.query(Auth).filter_by(id=id).update({"email": email})
-            return True if result == 1 else False
+            with get_db() as db:
+
+                result = db.query(Auth).filter_by(id=id).update({"email": email})
+                return True if result == 1 else False
         except:
         except:
             return False
             return False
 
 
     def delete_auth_by_id(self, id: str) -> bool:
     def delete_auth_by_id(self, id: str) -> bool:
         try:
         try:
-            # Delete User
-            result = Users.delete_user_by_id(id)
+            with get_db() as db:
 
 
-            if result:
-                Session.query(Auth).filter_by(id=id).delete()
+                # Delete User
+                result = Users.delete_user_by_id(id)
 
 
-                return True
-            else:
-                return False
+                if result:
+                    db.query(Auth).filter_by(id=id).delete()
+
+                    return True
+                else:
+                    return False
         except:
         except:
             return False
             return False
 
 

+ 188 - 142
backend/apps/webui/models/chats.py

@@ -7,7 +7,7 @@ import time
 
 
 from sqlalchemy import Column, String, BigInteger, Boolean, Text
 from sqlalchemy import Column, String, BigInteger, Boolean, Text
 
 
-from apps.webui.internal.db import Base, Session
+from apps.webui.internal.db import Base, get_db
 
 
 
 
 ####################
 ####################
@@ -79,87 +79,99 @@ class ChatTitleIdResponse(BaseModel):
 class ChatTable:
 class ChatTable:
 
 
     def insert_new_chat(self, user_id: str, form_data: ChatForm) -> Optional[ChatModel]:
     def insert_new_chat(self, user_id: str, form_data: ChatForm) -> Optional[ChatModel]:
-        id = str(uuid.uuid4())
-        chat = ChatModel(
-            **{
-                "id": id,
-                "user_id": user_id,
-                "title": (
-                    form_data.chat["title"] if "title" in form_data.chat else "New Chat"
-                ),
-                "chat": json.dumps(form_data.chat),
-                "created_at": int(time.time()),
-                "updated_at": int(time.time()),
-            }
-        )
-
-        result = Chat(**chat.model_dump())
-        Session.add(result)
-        Session.commit()
-        Session.refresh(result)
-        return ChatModel.model_validate(result) if result else None
+        with get_db() as db:
+
+            id = str(uuid.uuid4())
+            chat = ChatModel(
+                **{
+                    "id": id,
+                    "user_id": user_id,
+                    "title": (
+                        form_data.chat["title"]
+                        if "title" in form_data.chat
+                        else "New Chat"
+                    ),
+                    "chat": json.dumps(form_data.chat),
+                    "created_at": int(time.time()),
+                    "updated_at": int(time.time()),
+                }
+            )
+
+            result = Chat(**chat.model_dump())
+            db.add(result)
+            db.commit()
+            db.refresh(result)
+            return ChatModel.model_validate(result) if result else None
 
 
     def update_chat_by_id(self, id: str, chat: dict) -> Optional[ChatModel]:
     def update_chat_by_id(self, id: str, chat: dict) -> Optional[ChatModel]:
         try:
         try:
-            chat_obj = Session.get(Chat, id)
-            chat_obj.chat = json.dumps(chat)
-            chat_obj.title = chat["title"] if "title" in chat else "New Chat"
-            chat_obj.updated_at = int(time.time())
-            Session.commit()
-            Session.refresh(chat_obj)
-
-            return ChatModel.model_validate(chat_obj)
+            with get_db() as db:
+
+                chat_obj = db.get(Chat, id)
+                chat_obj.chat = json.dumps(chat)
+                chat_obj.title = chat["title"] if "title" in chat else "New Chat"
+                chat_obj.updated_at = int(time.time())
+                db.commit()
+                db.refresh(chat_obj)
+
+                return ChatModel.model_validate(chat_obj)
         except Exception as e:
         except Exception as e:
             return None
             return None
 
 
     def insert_shared_chat_by_chat_id(self, chat_id: str) -> Optional[ChatModel]:
     def insert_shared_chat_by_chat_id(self, chat_id: str) -> Optional[ChatModel]:
-        # Get the existing chat to share
-        chat = Session.get(Chat, chat_id)
-        # Check if the chat is already shared
-        if chat.share_id:
-            return self.get_chat_by_id_and_user_id(chat.share_id, "shared")
-        # Create a new chat with the same data, but with a new ID
-        shared_chat = ChatModel(
-            **{
-                "id": str(uuid.uuid4()),
-                "user_id": f"shared-{chat_id}",
-                "title": chat.title,
-                "chat": chat.chat,
-                "created_at": chat.created_at,
-                "updated_at": int(time.time()),
-            }
-        )
-        shared_result = Chat(**shared_chat.model_dump())
-        Session.add(shared_result)
-        Session.commit()
-        Session.refresh(shared_result)
-        # Update the original chat with the share_id
-        result = (
-            Session.query(Chat)
-            .filter_by(id=chat_id)
-            .update({"share_id": shared_chat.id})
-        )
-
-        return shared_chat if (shared_result and result) else None
+        with get_db() as db:
+
+            # Get the existing chat to share
+            chat = db.get(Chat, chat_id)
+            # Check if the chat is already shared
+            if chat.share_id:
+                return self.get_chat_by_id_and_user_id(chat.share_id, "shared")
+            # Create a new chat with the same data, but with a new ID
+            shared_chat = ChatModel(
+                **{
+                    "id": str(uuid.uuid4()),
+                    "user_id": f"shared-{chat_id}",
+                    "title": chat.title,
+                    "chat": chat.chat,
+                    "created_at": chat.created_at,
+                    "updated_at": int(time.time()),
+                }
+            )
+            shared_result = Chat(**shared_chat.model_dump())
+            db.add(shared_result)
+            db.commit()
+            db.refresh(shared_result)
+            # Update the original chat with the share_id
+            result = (
+                db.query(Chat)
+                .filter_by(id=chat_id)
+                .update({"share_id": shared_chat.id})
+            )
+
+            return shared_chat if (shared_result and result) else None
 
 
     def update_shared_chat_by_chat_id(self, chat_id: str) -> Optional[ChatModel]:
     def update_shared_chat_by_chat_id(self, chat_id: str) -> Optional[ChatModel]:
         try:
         try:
-            print("update_shared_chat_by_id")
-            chat = Session.get(Chat, chat_id)
-            print(chat)
-            chat.title = chat.title
-            chat.chat = chat.chat
-            Session.commit()
-            Session.refresh(chat)
-
-            return self.get_chat_by_id(chat.share_id)
+            with get_db() as db:
+
+                print("update_shared_chat_by_id")
+                chat = db.get(Chat, chat_id)
+                print(chat)
+                chat.title = chat.title
+                chat.chat = chat.chat
+                db.commit()
+                db.refresh(chat)
+
+                return self.get_chat_by_id(chat.share_id)
         except:
         except:
             return None
             return None
 
 
     def delete_shared_chat_by_chat_id(self, chat_id: str) -> bool:
     def delete_shared_chat_by_chat_id(self, chat_id: str) -> bool:
         try:
         try:
-            Session.query(Chat).filter_by(user_id=f"shared-{chat_id}").delete()
-            return True
+            with get_db() as db:
+
+                db.query(Chat).filter_by(user_id=f"shared-{chat_id}").delete()
+                return True
         except:
         except:
             return False
             return False
 
 
@@ -167,42 +179,50 @@ class ChatTable:
         self, id: str, share_id: Optional[str]
         self, id: str, share_id: Optional[str]
     ) -> Optional[ChatModel]:
     ) -> Optional[ChatModel]:
         try:
         try:
-            chat = Session.get(Chat, id)
-            chat.share_id = share_id
-            Session.commit()
-            Session.refresh(chat)
-            return ChatModel.model_validate(chat)
+            with get_db() as db:
+
+                chat = db.get(Chat, id)
+                chat.share_id = share_id
+                db.commit()
+                db.refresh(chat)
+                return ChatModel.model_validate(chat)
         except:
         except:
             return None
             return None
 
 
     def toggle_chat_archive_by_id(self, id: str) -> Optional[ChatModel]:
     def toggle_chat_archive_by_id(self, id: str) -> Optional[ChatModel]:
         try:
         try:
-            chat = Session.get(Chat, id)
-            chat.archived = not chat.archived
-            Session.commit()
-            Session.refresh(chat)
-            return ChatModel.model_validate(chat)
+            with get_db() as db:
+
+                chat = db.get(Chat, id)
+                chat.archived = not chat.archived
+                db.commit()
+                db.refresh(chat)
+                return ChatModel.model_validate(chat)
         except:
         except:
             return None
             return None
 
 
     def archive_all_chats_by_user_id(self, user_id: str) -> bool:
     def archive_all_chats_by_user_id(self, user_id: str) -> bool:
         try:
         try:
-            Session.query(Chat).filter_by(user_id=user_id).update({"archived": True})
-            return True
+            with get_db() as db:
+
+                db.query(Chat).filter_by(user_id=user_id).update({"archived": True})
+                return True
         except:
         except:
             return False
             return False
 
 
     def get_archived_chat_list_by_user_id(
     def get_archived_chat_list_by_user_id(
         self, user_id: str, skip: int = 0, limit: int = 50
         self, user_id: str, skip: int = 0, limit: int = 50
     ) -> List[ChatModel]:
     ) -> List[ChatModel]:
-        all_chats = (
-            Session.query(Chat)
-            .filter_by(user_id=user_id, archived=True)
-            .order_by(Chat.updated_at.desc())
-            # .limit(limit).offset(skip)
-            .all()
-        )
-        return [ChatModel.model_validate(chat) for chat in all_chats]
+        with get_db() as db:
+
+            all_chats = (
+                db.query(Chat)
+                .filter_by(user_id=user_id, archived=True)
+                .order_by(Chat.updated_at.desc())
+                # .limit(limit).offset(skip)
+                .all()
+            )
+            return [ChatModel.model_validate(chat) for chat in all_chats]
 
 
     def get_chat_list_by_user_id(
     def get_chat_list_by_user_id(
         self,
         self,
@@ -211,110 +231,136 @@ class ChatTable:
         skip: int = 0,
         skip: int = 0,
         limit: int = 50,
         limit: int = 50,
     ) -> List[ChatModel]:
     ) -> List[ChatModel]:
-        query = Session.query(Chat).filter_by(user_id=user_id)
-        if not include_archived:
-            query = query.filter_by(archived=False)
-        all_chats = (
-            query.order_by(Chat.updated_at.desc())
-            # .limit(limit).offset(skip)
-            .all()
-        )
-        return [ChatModel.model_validate(chat) for chat in all_chats]
+        with get_db() as db:
+            query = db.query(Chat).filter_by(user_id=user_id)
+            if not include_archived:
+                query = query.filter_by(archived=False)
+            all_chats = (
+                query.order_by(Chat.updated_at.desc())
+                # .limit(limit).offset(skip)
+                .all()
+            )
+            return [ChatModel.model_validate(chat) for chat in all_chats]
 
 
     def get_chat_list_by_chat_ids(
     def get_chat_list_by_chat_ids(
         self, chat_ids: List[str], skip: int = 0, limit: int = 50
         self, chat_ids: List[str], skip: int = 0, limit: int = 50
     ) -> List[ChatModel]:
     ) -> List[ChatModel]:
-        all_chats = (
-            Session.query(Chat)
-            .filter(Chat.id.in_(chat_ids))
-            .filter_by(archived=False)
-            .order_by(Chat.updated_at.desc())
-            .all()
-        )
-        return [ChatModel.model_validate(chat) for chat in all_chats]
+
+        with get_db() as db:
+
+            all_chats = (
+                db.query(Chat)
+                .filter(Chat.id.in_(chat_ids))
+                .filter_by(archived=False)
+                .order_by(Chat.updated_at.desc())
+                .all()
+            )
+            return [ChatModel.model_validate(chat) for chat in all_chats]
 
 
     def get_chat_by_id(self, id: str) -> Optional[ChatModel]:
     def get_chat_by_id(self, id: str) -> Optional[ChatModel]:
         try:
         try:
-            chat = Session.get(Chat, id)
-            return ChatModel.model_validate(chat)
+            with get_db() as db:
+
+                chat = db.get(Chat, id)
+                return ChatModel.model_validate(chat)
         except:
         except:
             return None
             return None
 
 
     def get_chat_by_share_id(self, id: str) -> Optional[ChatModel]:
     def get_chat_by_share_id(self, id: str) -> Optional[ChatModel]:
         try:
         try:
-            chat = Session.query(Chat).filter_by(share_id=id).first()
+            with get_db() as db:
 
 
-            if chat:
-                return self.get_chat_by_id(id)
-            else:
-                return None
+                chat = db.query(Chat).filter_by(share_id=id).first()
+
+                if chat:
+                    return self.get_chat_by_id(id)
+                else:
+                    return None
         except Exception as e:
         except Exception as e:
             return None
             return None
 
 
     def get_chat_by_id_and_user_id(self, id: str, user_id: str) -> Optional[ChatModel]:
     def get_chat_by_id_and_user_id(self, id: str, user_id: str) -> Optional[ChatModel]:
         try:
         try:
-            chat = Session.query(Chat).filter_by(id=id, user_id=user_id).first()
-            return ChatModel.model_validate(chat)
+            with get_db() as db:
+
+                chat = db.query(Chat).filter_by(id=id, user_id=user_id).first()
+                return ChatModel.model_validate(chat)
         except:
         except:
             return None
             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]:
-        all_chats = (
-            Session.query(Chat)
-            # .limit(limit).offset(skip)
-            .order_by(Chat.updated_at.desc())
-        )
-        return [ChatModel.model_validate(chat) for chat in all_chats]
+        with get_db() as db:
+
+            all_chats = (
+                db.query(Chat)
+                # .limit(limit).offset(skip)
+                .order_by(Chat.updated_at.desc())
+            )
+            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]:
-        all_chats = (
-            Session.query(Chat)
-            .filter_by(user_id=user_id)
-            .order_by(Chat.updated_at.desc())
-        )
-        return [ChatModel.model_validate(chat) for chat in all_chats]
+        with get_db() as db:
+
+            all_chats = (
+                db.query(Chat)
+                .filter_by(user_id=user_id)
+                .order_by(Chat.updated_at.desc())
+            )
+            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]:
-        all_chats = (
-            Session.query(Chat)
-            .filter_by(user_id=user_id, archived=True)
-            .order_by(Chat.updated_at.desc())
-        )
-        return [ChatModel.model_validate(chat) for chat in all_chats]
+        with get_db() as db:
+
+            all_chats = (
+                db.query(Chat)
+                .filter_by(user_id=user_id, archived=True)
+                .order_by(Chat.updated_at.desc())
+            )
+            return [ChatModel.model_validate(chat) for chat in all_chats]
 
 
     def delete_chat_by_id(self, id: str) -> bool:
     def delete_chat_by_id(self, id: str) -> bool:
         try:
         try:
-            Session.query(Chat).filter_by(id=id).delete()
+            with get_db() as db:
+
+                db.query(Chat).filter_by(id=id).delete()
 
 
-            return True and self.delete_shared_chat_by_chat_id(id)
+                return True and self.delete_shared_chat_by_chat_id(id)
         except:
         except:
             return False
             return False
 
 
     def delete_chat_by_id_and_user_id(self, id: str, user_id: str) -> bool:
     def delete_chat_by_id_and_user_id(self, id: str, user_id: str) -> bool:
         try:
         try:
-            Session.query(Chat).filter_by(id=id, user_id=user_id).delete()
+            with get_db() as db:
 
 
-            return True and self.delete_shared_chat_by_chat_id(id)
+                db.query(Chat).filter_by(id=id, user_id=user_id).delete()
+
+                return True and self.delete_shared_chat_by_chat_id(id)
         except:
         except:
             return False
             return False
 
 
     def delete_chats_by_user_id(self, user_id: str) -> bool:
     def delete_chats_by_user_id(self, user_id: str) -> bool:
         try:
         try:
-            self.delete_shared_chats_by_user_id(user_id)
 
 
-            Session.query(Chat).filter_by(user_id=user_id).delete()
-            return True
+            with get_db() as db:
+
+                self.delete_shared_chats_by_user_id(user_id)
+
+                db.query(Chat).filter_by(user_id=user_id).delete()
+                return True
         except:
         except:
             return False
             return False
 
 
     def delete_shared_chats_by_user_id(self, user_id: str) -> bool:
     def delete_shared_chats_by_user_id(self, user_id: str) -> bool:
         try:
         try:
-            chats_by_user = Session.query(Chat).filter_by(user_id=user_id).all()
-            shared_chat_ids = [f"shared-{chat.id}" for chat in chats_by_user]
 
 
-            Session.query(Chat).filter(Chat.user_id.in_(shared_chat_ids)).delete()
+            with get_db() as db:
+
+                chats_by_user = db.query(Chat).filter_by(user_id=user_id).all()
+                shared_chat_ids = [f"shared-{chat.id}" for chat in chats_by_user]
+
+                db.query(Chat).filter(Chat.user_id.in_(shared_chat_ids)).delete()
 
 
-            return True
+                return True
         except:
         except:
             return False
             return False
 
 

+ 54 - 42
backend/apps/webui/models/documents.py

@@ -5,7 +5,7 @@ import logging
 
 
 from sqlalchemy import String, Column, BigInteger, Text
 from sqlalchemy import String, Column, BigInteger, Text
 
 
-from apps.webui.internal.db import Base, Session
+from apps.webui.internal.db import Base, get_db
 
 
 import json
 import json
 
 
@@ -74,51 +74,59 @@ class DocumentsTable:
     def insert_new_doc(
     def insert_new_doc(
         self, user_id: str, form_data: DocumentForm
         self, user_id: str, form_data: DocumentForm
     ) -> Optional[DocumentModel]:
     ) -> Optional[DocumentModel]:
-        document = DocumentModel(
-            **{
-                **form_data.model_dump(),
-                "user_id": user_id,
-                "timestamp": int(time.time()),
-            }
-        )
+        with get_db() as db:
 
 
-        try:
-            result = Document(**document.model_dump())
-            Session.add(result)
-            Session.commit()
-            Session.refresh(result)
-            if result:
-                return DocumentModel.model_validate(result)
-            else:
+            document = DocumentModel(
+                **{
+                    **form_data.model_dump(),
+                    "user_id": user_id,
+                    "timestamp": int(time.time()),
+                }
+            )
+
+            try:
+                result = Document(**document.model_dump())
+                db.add(result)
+                db.commit()
+                db.refresh(result)
+                if result:
+                    return DocumentModel.model_validate(result)
+                else:
+                    return None
+            except:
                 return None
                 return None
-        except:
-            return None
 
 
     def get_doc_by_name(self, name: str) -> Optional[DocumentModel]:
     def get_doc_by_name(self, name: str) -> Optional[DocumentModel]:
         try:
         try:
-            document = Session.query(Document).filter_by(name=name).first()
-            return DocumentModel.model_validate(document) if document else None
+            with get_db() as db:
+
+                document = db.query(Document).filter_by(name=name).first()
+                return DocumentModel.model_validate(document) if document else None
         except:
         except:
             return None
             return None
 
 
     def get_docs(self) -> List[DocumentModel]:
     def get_docs(self) -> List[DocumentModel]:
-        return [
-            DocumentModel.model_validate(doc) for doc in Session.query(Document).all()
-        ]
+        with get_db() as db:
+
+            return [
+                DocumentModel.model_validate(doc) for doc in db.query(Document).all()
+            ]
 
 
     def update_doc_by_name(
     def update_doc_by_name(
         self, name: str, form_data: DocumentUpdateForm
         self, name: str, form_data: DocumentUpdateForm
     ) -> Optional[DocumentModel]:
     ) -> Optional[DocumentModel]:
         try:
         try:
-            Session.query(Document).filter_by(name=name).update(
-                {
-                    "title": form_data.title,
-                    "name": form_data.name,
-                    "timestamp": int(time.time()),
-                }
-            )
-            Session.commit()
-            return self.get_doc_by_name(form_data.name)
+            with get_db() as db:
+
+                db.query(Document).filter_by(name=name).update(
+                    {
+                        "title": form_data.title,
+                        "name": form_data.name,
+                        "timestamp": int(time.time()),
+                    }
+                )
+                db.commit()
+                return self.get_doc_by_name(form_data.name)
         except Exception as e:
         except Exception as e:
             log.exception(e)
             log.exception(e)
             return None
             return None
@@ -131,22 +139,26 @@ class DocumentsTable:
             doc_content = json.loads(doc.content if doc.content else "{}")
             doc_content = json.loads(doc.content if doc.content else "{}")
             doc_content = {**doc_content, **updated}
             doc_content = {**doc_content, **updated}
 
 
-            Session.query(Document).filter_by(name=name).update(
-                {
-                    "content": json.dumps(doc_content),
-                    "timestamp": int(time.time()),
-                }
-            )
-            Session.commit()
-            return self.get_doc_by_name(name)
+            with get_db() as db:
+
+                db.query(Document).filter_by(name=name).update(
+                    {
+                        "content": json.dumps(doc_content),
+                        "timestamp": int(time.time()),
+                    }
+                )
+                db.commit()
+                return self.get_doc_by_name(name)
         except Exception as e:
         except Exception as e:
             log.exception(e)
             log.exception(e)
             return None
             return None
 
 
     def delete_doc_by_name(self, name: str) -> bool:
     def delete_doc_by_name(self, name: str) -> bool:
         try:
         try:
-            Session.query(Document).filter_by(name=name).delete()
-            return True
+            with get_db() as db:
+
+                db.query(Document).filter_by(name=name).delete()
+                return True
         except:
         except:
             return False
             return False
 
 

+ 48 - 36
backend/apps/webui/models/files.py

@@ -5,7 +5,7 @@ import logging
 
 
 from sqlalchemy import Column, String, BigInteger, Text
 from sqlalchemy import Column, String, BigInteger, Text
 
 
-from apps.webui.internal.db import JSONField, Base, Session
+from apps.webui.internal.db import JSONField, Base, get_db
 
 
 import json
 import json
 
 
@@ -61,50 +61,62 @@ class FileForm(BaseModel):
 class FilesTable:
 class FilesTable:
 
 
     def insert_new_file(self, user_id: str, form_data: FileForm) -> Optional[FileModel]:
     def insert_new_file(self, user_id: str, form_data: FileForm) -> Optional[FileModel]:
-        file = FileModel(
-            **{
-                **form_data.model_dump(),
-                "user_id": user_id,
-                "created_at": int(time.time()),
-            }
-        )
-
-        try:
-            result = File(**file.model_dump())
-            Session.add(result)
-            Session.commit()
-            Session.refresh(result)
-            if result:
-                return FileModel.model_validate(result)
-            else:
+        with get_db() as db:
+
+            file = FileModel(
+                **{
+                    **form_data.model_dump(),
+                    "user_id": user_id,
+                    "created_at": int(time.time()),
+                }
+            )
+
+            try:
+                result = File(**file.model_dump())
+                db.add(result)
+                db.commit()
+                db.refresh(result)
+                if result:
+                    return FileModel.model_validate(result)
+                else:
+                    return None
+            except Exception as e:
+                print(f"Error creating tool: {e}")
                 return None
                 return None
-        except Exception as e:
-            print(f"Error creating tool: {e}")
-            return None
 
 
     def get_file_by_id(self, id: str) -> Optional[FileModel]:
     def get_file_by_id(self, id: str) -> Optional[FileModel]:
-        try:
-            file = Session.get(File, id)
-            return FileModel.model_validate(file)
-        except:
-            return None
+        with get_db() as db:
+
+            try:
+                file = db.get(File, id)
+                return FileModel.model_validate(file)
+            except:
+                return None
 
 
     def get_files(self) -> List[FileModel]:
     def get_files(self) -> List[FileModel]:
-        return [FileModel.model_validate(file) for file in Session.query(File).all()]
+        with get_db() as db:
+
+            return [FileModel.model_validate(file) for file in db.query(File).all()]
 
 
     def delete_file_by_id(self, id: str) -> bool:
     def delete_file_by_id(self, id: str) -> bool:
-        try:
-            Session.query(File).filter_by(id=id).delete()
-            return True
-        except:
-            return False
+
+        with get_db() as db:
+
+            try:
+                db.query(File).filter_by(id=id).delete()
+                return True
+            except:
+                return False
 
 
     def delete_all_files(self) -> bool:
     def delete_all_files(self) -> bool:
-        try:
-            Session.query(File).delete()
-            return True
-        except:
-            return False
+
+        with get_db() as db:
+
+            try:
+                db.query(File).delete()
+                return True
+            except:
+                return False
 
 
 
 
 Files = FilesTable()
 Files = FilesTable()

+ 103 - 81
backend/apps/webui/models/functions.py

@@ -5,7 +5,7 @@ import logging
 
 
 from sqlalchemy import Column, String, Text, BigInteger, Boolean
 from sqlalchemy import Column, String, Text, BigInteger, Boolean
 
 
-from apps.webui.internal.db import JSONField, Base, Session
+from apps.webui.internal.db import JSONField, Base, get_db
 from apps.webui.models.users import Users
 from apps.webui.models.users import Users
 
 
 import json
 import json
@@ -91,6 +91,7 @@ class FunctionsTable:
     def insert_new_function(
     def insert_new_function(
         self, user_id: str, type: str, form_data: FunctionForm
         self, user_id: str, type: str, form_data: FunctionForm
     ) -> Optional[FunctionModel]:
     ) -> Optional[FunctionModel]:
+
         function = FunctionModel(
         function = FunctionModel(
             **{
             **{
                 **form_data.model_dump(),
                 **form_data.model_dump(),
@@ -102,88 +103,102 @@ class FunctionsTable:
         )
         )
 
 
         try:
         try:
-            result = Function(**function.model_dump())
-            Session.add(result)
-            Session.commit()
-            Session.refresh(result)
-            if result:
-                return FunctionModel.model_validate(result)
-            else:
-                return None
+            with get_db() as db:
+                result = Function(**function.model_dump())
+                db.add(result)
+                db.commit()
+                db.refresh(result)
+                if result:
+                    return FunctionModel.model_validate(result)
+                else:
+                    return None
         except Exception as e:
         except Exception as e:
             print(f"Error creating tool: {e}")
             print(f"Error creating tool: {e}")
             return None
             return None
 
 
     def get_function_by_id(self, id: str) -> Optional[FunctionModel]:
     def get_function_by_id(self, id: str) -> Optional[FunctionModel]:
         try:
         try:
-            function = Session.get(Function, id)
-            return FunctionModel.model_validate(function)
+            with get_db() as db:
+
+                function = db.get(Function, id)
+                return FunctionModel.model_validate(function)
         except:
         except:
             return None
             return None
 
 
     def get_functions(self, active_only=False) -> List[FunctionModel]:
     def get_functions(self, active_only=False) -> List[FunctionModel]:
-        if active_only:
-            return [
-                FunctionModel.model_validate(function)
-                for function in Session.query(Function).filter_by(is_active=True).all()
-            ]
-        else:
-            return [
-                FunctionModel.model_validate(function)
-                for function in Session.query(Function).all()
-            ]
+        with get_db() as db:
+
+            if active_only:
+                return [
+                    FunctionModel.model_validate(function)
+                    for function in db.query(Function).filter_by(is_active=True).all()
+                ]
+            else:
+                return [
+                    FunctionModel.model_validate(function)
+                    for function in db.query(Function).all()
+                ]
 
 
     def get_functions_by_type(
     def get_functions_by_type(
         self, type: str, active_only=False
         self, type: str, active_only=False
     ) -> List[FunctionModel]:
     ) -> List[FunctionModel]:
-        if active_only:
+        with get_db() as db:
+
+            if active_only:
+                return [
+                    FunctionModel.model_validate(function)
+                    for function in db.query(Function)
+                    .filter_by(type=type, is_active=True)
+                    .all()
+                ]
+            else:
+                return [
+                    FunctionModel.model_validate(function)
+                    for function in db.query(Function).filter_by(type=type).all()
+                ]
+
+    def get_global_filter_functions(self) -> List[FunctionModel]:
+        with get_db() as db:
+
             return [
             return [
                 FunctionModel.model_validate(function)
                 FunctionModel.model_validate(function)
-                for function in Session.query(Function)
-                .filter_by(type=type, is_active=True)
+                for function in db.query(Function)
+                .filter_by(type="filter", is_active=True, is_global=True)
                 .all()
                 .all()
             ]
             ]
-        else:
-            return [
-                FunctionModel.model_validate(function)
-                for function in Session.query(Function).filter_by(type=type).all()
-            ]
-
-    def get_global_filter_functions(self) -> List[FunctionModel]:
-        return [
-            FunctionModel.model_validate(function)
-            for function in Session.query(Function)
-            .filter_by(type="filter", is_active=True, is_global=True)
-            .all()
-        ]
 
 
     def get_function_valves_by_id(self, id: str) -> Optional[dict]:
     def get_function_valves_by_id(self, id: str) -> Optional[dict]:
-        try:
-            function = Session.get(Function, id)
-            return function.valves if function.valves else {}
-        except Exception as e:
-            print(f"An error occurred: {e}")
-            return None
+        with get_db() as db:
+
+            try:
+                function = db.get(Function, id)
+                return function.valves if function.valves else {}
+            except Exception as e:
+                print(f"An error occurred: {e}")
+                return None
 
 
     def update_function_valves_by_id(
     def update_function_valves_by_id(
         self, id: str, valves: dict
         self, id: str, valves: dict
     ) -> Optional[FunctionValves]:
     ) -> Optional[FunctionValves]:
-        try:
-            function = Session.get(Function, id)
-            function.valves = valves
-            function.updated_at = int(time.time())
-            Session.commit()
-            Session.refresh(function)
-            return self.get_function_by_id(id)
-        except:
-            return None
+        with get_db() as db:
+
+            try:
+                function = db.get(Function, id)
+                function.valves = valves
+                function.updated_at = int(time.time())
+                db.commit()
+                db.refresh(function)
+                return self.get_function_by_id(id)
+            except:
+                return None
 
 
     def get_user_valves_by_id_and_user_id(
     def get_user_valves_by_id_and_user_id(
         self, id: str, user_id: str
         self, id: str, user_id: str
     ) -> Optional[dict]:
     ) -> Optional[dict]:
+
         try:
         try:
             user = Users.get_user_by_id(user_id)
             user = Users.get_user_by_id(user_id)
-            user_settings = user.settings.model_dump()
+            user_settings = user.settings.model_dump() if user.settings else {}
 
 
             # Check if user has "functions" and "valves" settings
             # Check if user has "functions" and "valves" settings
             if "functions" not in user_settings:
             if "functions" not in user_settings:
@@ -199,9 +214,10 @@ class FunctionsTable:
     def update_user_valves_by_id_and_user_id(
     def update_user_valves_by_id_and_user_id(
         self, id: str, user_id: str, valves: dict
         self, id: str, user_id: str, valves: dict
     ) -> Optional[dict]:
     ) -> Optional[dict]:
+
         try:
         try:
             user = Users.get_user_by_id(user_id)
             user = Users.get_user_by_id(user_id)
-            user_settings = user.settings.model_dump()
+            user_settings = user.settings.model_dump() if user.settings else {}
 
 
             # Check if user has "functions" and "valves" settings
             # Check if user has "functions" and "valves" settings
             if "functions" not in user_settings:
             if "functions" not in user_settings:
@@ -220,37 +236,43 @@ class FunctionsTable:
             return None
             return None
 
 
     def update_function_by_id(self, id: str, updated: dict) -> Optional[FunctionModel]:
     def update_function_by_id(self, id: str, updated: dict) -> Optional[FunctionModel]:
-        try:
-            Session.query(Function).filter_by(id=id).update(
-                {
-                    **updated,
-                    "updated_at": int(time.time()),
-                }
-            )
-            Session.commit()
-            return self.get_function_by_id(id)
-        except:
-            return None
+        with get_db() as db:
+
+            try:
+                db.query(Function).filter_by(id=id).update(
+                    {
+                        **updated,
+                        "updated_at": int(time.time()),
+                    }
+                )
+                db.commit()
+                return self.get_function_by_id(id)
+            except:
+                return None
 
 
     def deactivate_all_functions(self) -> Optional[bool]:
     def deactivate_all_functions(self) -> Optional[bool]:
-        try:
-            Session.query(Function).update(
-                {
-                    "is_active": False,
-                    "updated_at": int(time.time()),
-                }
-            )
-            Session.commit()
-            return True
-        except:
-            return None
+        with get_db() as db:
+
+            try:
+                db.query(Function).update(
+                    {
+                        "is_active": False,
+                        "updated_at": int(time.time()),
+                    }
+                )
+                db.commit()
+                return True
+            except:
+                return None
 
 
     def delete_function_by_id(self, id: str) -> bool:
     def delete_function_by_id(self, id: str) -> bool:
-        try:
-            Session.query(Function).filter_by(id=id).delete()
-            return True
-        except:
-            return False
+        with get_db() as db:
+
+            try:
+                db.query(Function).filter_by(id=id).delete()
+                return True
+            except:
+                return False
 
 
 
 
 Functions = FunctionsTable()
 Functions = FunctionsTable()

+ 74 - 58
backend/apps/webui/models/memories.py

@@ -3,7 +3,7 @@ from typing import List, Union, Optional
 
 
 from sqlalchemy import Column, String, BigInteger, Text
 from sqlalchemy import Column, String, BigInteger, Text
 
 
-from apps.webui.internal.db import Base, Session
+from apps.webui.internal.db import Base, get_db
 
 
 import time
 import time
 import uuid
 import uuid
@@ -45,82 +45,98 @@ class MemoriesTable:
         user_id: str,
         user_id: str,
         content: str,
         content: str,
     ) -> Optional[MemoryModel]:
     ) -> Optional[MemoryModel]:
-        id = str(uuid.uuid4())
-
-        memory = MemoryModel(
-            **{
-                "id": id,
-                "user_id": user_id,
-                "content": content,
-                "created_at": int(time.time()),
-                "updated_at": int(time.time()),
-            }
-        )
-        result = Memory(**memory.model_dump())
-        Session.add(result)
-        Session.commit()
-        Session.refresh(result)
-        if result:
-            return MemoryModel.model_validate(result)
-        else:
-            return None
+
+        with get_db() as db:
+            id = str(uuid.uuid4())
+
+            memory = MemoryModel(
+                **{
+                    "id": id,
+                    "user_id": user_id,
+                    "content": content,
+                    "created_at": int(time.time()),
+                    "updated_at": int(time.time()),
+                }
+            )
+            result = Memory(**memory.model_dump())
+            db.add(result)
+            db.commit()
+            db.refresh(result)
+            if result:
+                return MemoryModel.model_validate(result)
+            else:
+                return None
 
 
     def update_memory_by_id(
     def update_memory_by_id(
         self,
         self,
         id: str,
         id: str,
         content: str,
         content: str,
     ) -> Optional[MemoryModel]:
     ) -> Optional[MemoryModel]:
-        try:
-            Session.query(Memory).filter_by(id=id).update(
-                {"content": content, "updated_at": int(time.time())}
-            )
-            Session.commit()
-            return self.get_memory_by_id(id)
-        except:
-            return None
+        with get_db() as db:
+
+            try:
+                db.query(Memory).filter_by(id=id).update(
+                    {"content": content, "updated_at": int(time.time())}
+                )
+                db.commit()
+                return self.get_memory_by_id(id)
+            except:
+                return None
 
 
     def get_memories(self) -> List[MemoryModel]:
     def get_memories(self) -> List[MemoryModel]:
-        try:
-            memories = Session.query(Memory).all()
-            return [MemoryModel.model_validate(memory) for memory in memories]
-        except:
-            return None
+        with get_db() as db:
+
+            try:
+                memories = db.query(Memory).all()
+                return [MemoryModel.model_validate(memory) for memory in memories]
+            except:
+                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]:
-        try:
-            memories = Session.query(Memory).filter_by(user_id=user_id).all()
-            return [MemoryModel.model_validate(memory) for memory in memories]
-        except:
-            return None
+        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:
+                return None
 
 
     def get_memory_by_id(self, id: str) -> Optional[MemoryModel]:
     def get_memory_by_id(self, id: str) -> Optional[MemoryModel]:
-        try:
-            memory = Session.get(Memory, id)
-            return MemoryModel.model_validate(memory)
-        except:
-            return None
+        with get_db() as db:
+
+            try:
+                memory = db.get(Memory, id)
+                return MemoryModel.model_validate(memory)
+            except:
+                return None
 
 
     def delete_memory_by_id(self, id: str) -> bool:
     def delete_memory_by_id(self, id: str) -> bool:
-        try:
-            Session.query(Memory).filter_by(id=id).delete()
-            return True
+        with get_db() as db:
+
+            try:
+                db.query(Memory).filter_by(id=id).delete()
+                return True
 
 
-        except:
-            return False
+            except:
+                return False
 
 
     def delete_memories_by_user_id(self, user_id: str) -> bool:
     def delete_memories_by_user_id(self, user_id: str) -> bool:
-        try:
-            Session.query(Memory).filter_by(user_id=user_id).delete()
-            return True
-        except:
-            return False
+        with get_db() as db:
+
+            try:
+                db.query(Memory).filter_by(user_id=user_id).delete()
+                return True
+            except:
+                return False
 
 
     def delete_memory_by_id_and_user_id(self, id: str, user_id: str) -> bool:
     def delete_memory_by_id_and_user_id(self, id: str, user_id: str) -> bool:
-        try:
-            Session.query(Memory).filter_by(id=id, user_id=user_id).delete()
-            return True
-        except:
-            return False
+        with get_db() as db:
+
+            try:
+                db.query(Memory).filter_by(id=id, user_id=user_id).delete()
+                return True
+            except:
+                return False
 
 
 
 
 Memories = MemoriesTable()
 Memories = MemoriesTable()

+ 32 - 23
backend/apps/webui/models/models.py

@@ -5,7 +5,7 @@ from typing import Optional
 from pydantic import BaseModel, ConfigDict
 from pydantic import BaseModel, ConfigDict
 from sqlalchemy import String, Column, BigInteger, Text
 from sqlalchemy import String, Column, BigInteger, Text
 
 
-from apps.webui.internal.db import Base, JSONField, Session
+from apps.webui.internal.db import Base, JSONField, get_db
 
 
 from typing import List, Union, Optional
 from typing import List, Union, Optional
 from config import SRC_LOG_LEVELS
 from config import SRC_LOG_LEVELS
@@ -126,39 +126,46 @@ class ModelsTable:
             }
             }
         )
         )
         try:
         try:
-            result = Model(**model.model_dump())
-            Session.add(result)
-            Session.commit()
-            Session.refresh(result)
-
-            if result:
-                return ModelModel.model_validate(result)
-            else:
-                return None
+
+            with get_db() as db:
+
+                result = Model(**model.model_dump())
+                db.add(result)
+                db.commit()
+                db.refresh(result)
+
+                if result:
+                    return ModelModel.model_validate(result)
+                else:
+                    return None
         except Exception as e:
         except Exception as e:
             print(e)
             print(e)
             return None
             return None
 
 
     def get_all_models(self) -> List[ModelModel]:
     def get_all_models(self) -> List[ModelModel]:
-        return [
-            ModelModel.model_validate(model) for model in Session.query(Model).all()
-        ]
+        with get_db() as db:
+
+            return [ModelModel.model_validate(model) for model in db.query(Model).all()]
 
 
     def get_model_by_id(self, id: str) -> Optional[ModelModel]:
     def get_model_by_id(self, id: str) -> Optional[ModelModel]:
         try:
         try:
-            model = Session.get(Model, id)
-            return ModelModel.model_validate(model)
+            with get_db() as db:
+
+                model = db.get(Model, id)
+                return ModelModel.model_validate(model)
         except:
         except:
             return None
             return None
 
 
     def update_model_by_id(self, id: str, model: ModelForm) -> Optional[ModelModel]:
     def update_model_by_id(self, id: str, model: ModelForm) -> Optional[ModelModel]:
         try:
         try:
-            # update only the fields that are present in the model
-            model = Session.query(Model).get(id)
-            model.update(**model.model_dump())
-            Session.commit()
-            Session.refresh(model)
-            return ModelModel.model_validate(model)
+            with get_db() as db:
+
+                # update only the fields that are present in the model
+                model = db.query(Model).get(id)
+                model.update(**model.model_dump())
+                db.commit()
+                db.refresh(model)
+                return ModelModel.model_validate(model)
         except Exception as e:
         except Exception as e:
             print(e)
             print(e)
 
 
@@ -166,8 +173,10 @@ class ModelsTable:
 
 
     def delete_model_by_id(self, id: str) -> bool:
     def delete_model_by_id(self, id: str) -> bool:
         try:
         try:
-            Session.query(Model).filter_by(id=id).delete()
-            return True
+            with get_db() as db:
+
+                db.query(Model).filter_by(id=id).delete()
+                return True
         except:
         except:
             return False
             return False
 
 

+ 32 - 22
backend/apps/webui/models/prompts.py

@@ -4,7 +4,7 @@ import time
 
 
 from sqlalchemy import String, Column, BigInteger, Text
 from sqlalchemy import String, Column, BigInteger, Text
 
 
-from apps.webui.internal.db import Base, Session
+from apps.webui.internal.db import Base, get_db
 
 
 import json
 import json
 
 
@@ -60,46 +60,56 @@ class PromptsTable:
         )
         )
 
 
         try:
         try:
-            result = Prompt(**prompt.dict())
-            Session.add(result)
-            Session.commit()
-            Session.refresh(result)
-            if result:
-                return PromptModel.model_validate(result)
-            else:
-                return None
+            with get_db() as db:
+
+                result = Prompt(**prompt.dict())
+                db.add(result)
+                db.commit()
+                db.refresh(result)
+                if result:
+                    return PromptModel.model_validate(result)
+                else:
+                    return None
         except Exception as e:
         except Exception as e:
             return None
             return None
 
 
     def get_prompt_by_command(self, command: str) -> Optional[PromptModel]:
     def get_prompt_by_command(self, command: str) -> Optional[PromptModel]:
         try:
         try:
-            prompt = Session.query(Prompt).filter_by(command=command).first()
-            return PromptModel.model_validate(prompt)
+            with get_db() as db:
+
+                prompt = db.query(Prompt).filter_by(command=command).first()
+                return PromptModel.model_validate(prompt)
         except:
         except:
             return None
             return None
 
 
     def get_prompts(self) -> List[PromptModel]:
     def get_prompts(self) -> List[PromptModel]:
-        return [
-            PromptModel.model_validate(prompt) for prompt in Session.query(Prompt).all()
-        ]
+        with get_db() as db:
+
+            return [
+                PromptModel.model_validate(prompt) for prompt in db.query(Prompt).all()
+            ]
 
 
     def update_prompt_by_command(
     def update_prompt_by_command(
         self, command: str, form_data: PromptForm
         self, command: str, form_data: PromptForm
     ) -> Optional[PromptModel]:
     ) -> Optional[PromptModel]:
         try:
         try:
-            prompt = Session.query(Prompt).filter_by(command=command).first()
-            prompt.title = form_data.title
-            prompt.content = form_data.content
-            prompt.timestamp = int(time.time())
-            Session.commit()
-            return PromptModel.model_validate(prompt)
+            with get_db() as db:
+
+                prompt = db.query(Prompt).filter_by(command=command).first()
+                prompt.title = form_data.title
+                prompt.content = form_data.content
+                prompt.timestamp = int(time.time())
+                db.commit()
+                return PromptModel.model_validate(prompt)
         except:
         except:
             return None
             return None
 
 
     def delete_prompt_by_command(self, command: str) -> bool:
     def delete_prompt_by_command(self, command: str) -> bool:
         try:
         try:
-            Session.query(Prompt).filter_by(command=command).delete()
-            return True
+            with get_db() as db:
+
+                db.query(Prompt).filter_by(command=command).delete()
+                return True
         except:
         except:
             return False
             return False
 
 

+ 120 - 102
backend/apps/webui/models/tags.py

@@ -8,7 +8,7 @@ import logging
 
 
 from sqlalchemy import String, Column, BigInteger, Text
 from sqlalchemy import String, Column, BigInteger, Text
 
 
-from apps.webui.internal.db import Base, Session
+from apps.webui.internal.db import Base, get_db
 
 
 from config import SRC_LOG_LEVELS
 from config import SRC_LOG_LEVELS
 
 
@@ -79,26 +79,29 @@ class ChatTagsResponse(BaseModel):
 class TagTable:
 class TagTable:
 
 
     def insert_new_tag(self, name: str, user_id: str) -> Optional[TagModel]:
     def insert_new_tag(self, name: str, user_id: str) -> Optional[TagModel]:
-        id = str(uuid.uuid4())
-        tag = TagModel(**{"id": id, "user_id": user_id, "name": name})
-        try:
-            result = Tag(**tag.model_dump())
-            Session.add(result)
-            Session.commit()
-            Session.refresh(result)
-            if result:
-                return TagModel.model_validate(result)
-            else:
+        with get_db() as db:
+
+            id = str(uuid.uuid4())
+            tag = TagModel(**{"id": id, "user_id": user_id, "name": name})
+            try:
+                result = Tag(**tag.model_dump())
+                db.add(result)
+                db.commit()
+                db.refresh(result)
+                if result:
+                    return TagModel.model_validate(result)
+                else:
+                    return None
+            except Exception as e:
                 return None
                 return None
-        except Exception as e:
-            return None
 
 
     def get_tag_by_name_and_user_id(
     def get_tag_by_name_and_user_id(
         self, name: str, user_id: str
         self, name: str, user_id: str
     ) -> Optional[TagModel]:
     ) -> Optional[TagModel]:
         try:
         try:
-            tag = Session.query(Tag).filter(name=name, user_id=user_id).first()
-            return TagModel.model_validate(tag)
+            with get_db() as db:
+                tag = db.query(Tag).filter(name=name, user_id=user_id).first()
+                return TagModel.model_validate(tag)
         except Exception as e:
         except Exception as e:
             return None
             return None
 
 
@@ -120,98 +123,109 @@ class TagTable:
             }
             }
         )
         )
         try:
         try:
-            result = ChatIdTag(**chatIdTag.model_dump())
-            Session.add(result)
-            Session.commit()
-            Session.refresh(result)
-            if result:
-                return ChatIdTagModel.model_validate(result)
-            else:
-                return None
+            with get_db() as db:
+                result = ChatIdTag(**chatIdTag.model_dump())
+                db.add(result)
+                db.commit()
+                db.refresh(result)
+                if result:
+                    return ChatIdTagModel.model_validate(result)
+                else:
+                    return None
         except:
         except:
             return None
             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]:
-        tag_names = [
-            chat_id_tag.tag_name
-            for chat_id_tag in (
-                Session.query(ChatIdTag)
-                .filter_by(user_id=user_id)
-                .order_by(ChatIdTag.timestamp.desc())
-                .all()
-            )
-        ]
-
-        return [
-            TagModel.model_validate(tag)
-            for tag in (
-                Session.query(Tag)
-                .filter_by(user_id=user_id)
-                .filter(Tag.name.in_(tag_names))
-                .all()
-            )
-        ]
+        with get_db() as db:
+            tag_names = [
+                chat_id_tag.tag_name
+                for chat_id_tag in (
+                    db.query(ChatIdTag)
+                    .filter_by(user_id=user_id)
+                    .order_by(ChatIdTag.timestamp.desc())
+                    .all()
+                )
+            ]
+
+            return [
+                TagModel.model_validate(tag)
+                for tag in (
+                    db.query(Tag)
+                    .filter_by(user_id=user_id)
+                    .filter(Tag.name.in_(tag_names))
+                    .all()
+                )
+            ]
 
 
     def get_tags_by_chat_id_and_user_id(
     def get_tags_by_chat_id_and_user_id(
         self, chat_id: str, user_id: str
         self, chat_id: str, user_id: str
     ) -> List[TagModel]:
     ) -> List[TagModel]:
-        tag_names = [
-            chat_id_tag.tag_name
-            for chat_id_tag in (
-                Session.query(ChatIdTag)
-                .filter_by(user_id=user_id, chat_id=chat_id)
-                .order_by(ChatIdTag.timestamp.desc())
-                .all()
-            )
-        ]
-
-        return [
-            TagModel.model_validate(tag)
-            for tag in (
-                Session.query(Tag)
-                .filter_by(user_id=user_id)
-                .filter(Tag.name.in_(tag_names))
-                .all()
-            )
-        ]
+        with get_db() as db:
+
+            tag_names = [
+                chat_id_tag.tag_name
+                for chat_id_tag in (
+                    db.query(ChatIdTag)
+                    .filter_by(user_id=user_id, chat_id=chat_id)
+                    .order_by(ChatIdTag.timestamp.desc())
+                    .all()
+                )
+            ]
+
+            return [
+                TagModel.model_validate(tag)
+                for tag in (
+                    db.query(Tag)
+                    .filter_by(user_id=user_id)
+                    .filter(Tag.name.in_(tag_names))
+                    .all()
+                )
+            ]
 
 
     def get_chat_ids_by_tag_name_and_user_id(
     def get_chat_ids_by_tag_name_and_user_id(
         self, tag_name: str, user_id: str
         self, tag_name: str, user_id: str
     ) -> List[ChatIdTagModel]:
     ) -> List[ChatIdTagModel]:
-        return [
-            ChatIdTagModel.model_validate(chat_id_tag)
-            for chat_id_tag in (
-                Session.query(ChatIdTag)
-                .filter_by(user_id=user_id, tag_name=tag_name)
-                .order_by(ChatIdTag.timestamp.desc())
-                .all()
-            )
-        ]
+        with get_db() as db:
+
+            return [
+                ChatIdTagModel.model_validate(chat_id_tag)
+                for chat_id_tag in (
+                    db.query(ChatIdTag)
+                    .filter_by(user_id=user_id, tag_name=tag_name)
+                    .order_by(ChatIdTag.timestamp.desc())
+                    .all()
+                )
+            ]
 
 
     def count_chat_ids_by_tag_name_and_user_id(
     def count_chat_ids_by_tag_name_and_user_id(
         self, tag_name: str, user_id: str
         self, tag_name: str, user_id: str
     ) -> int:
     ) -> int:
-        return (
-            Session.query(ChatIdTag)
-            .filter_by(tag_name=tag_name, user_id=user_id)
-            .count()
-        )
+        with get_db() as db:
 
 
-    def delete_tag_by_tag_name_and_user_id(self, tag_name: str, user_id: str) -> bool:
-        try:
-            res = (
-                Session.query(ChatIdTag)
+            return (
+                db.query(ChatIdTag)
                 .filter_by(tag_name=tag_name, user_id=user_id)
                 .filter_by(tag_name=tag_name, user_id=user_id)
-                .delete()
+                .count()
             )
             )
-            log.debug(f"res: {res}")
-            Session.commit()
-
-            tag_count = self.count_chat_ids_by_tag_name_and_user_id(tag_name, user_id)
-            if tag_count == 0:
-                # Remove tag item from Tag col as well
-                Session.query(Tag).filter_by(name=tag_name, user_id=user_id).delete()
-            return True
+
+    def delete_tag_by_tag_name_and_user_id(self, tag_name: str, user_id: str) -> bool:
+        try:
+            with get_db() as db:
+                res = (
+                    db.query(ChatIdTag)
+                    .filter_by(tag_name=tag_name, user_id=user_id)
+                    .delete()
+                )
+                log.debug(f"res: {res}")
+                db.commit()
+
+                tag_count = self.count_chat_ids_by_tag_name_and_user_id(
+                    tag_name, user_id
+                )
+                if tag_count == 0:
+                    # Remove tag item from Tag col as well
+                    db.query(Tag).filter_by(name=tag_name, user_id=user_id).delete()
+                return True
         except Exception as e:
         except Exception as e:
             log.error(f"delete_tag: {e}")
             log.error(f"delete_tag: {e}")
             return False
             return False
@@ -220,20 +234,24 @@ class TagTable:
         self, tag_name: str, chat_id: str, user_id: str
         self, tag_name: str, chat_id: str, user_id: str
     ) -> bool:
     ) -> bool:
         try:
         try:
-            res = (
-                Session.query(ChatIdTag)
-                .filter_by(tag_name=tag_name, chat_id=chat_id, user_id=user_id)
-                .delete()
-            )
-            log.debug(f"res: {res}")
-            Session.commit()
-
-            tag_count = self.count_chat_ids_by_tag_name_and_user_id(tag_name, user_id)
-            if tag_count == 0:
-                # Remove tag item from Tag col as well
-                Session.query(Tag).filter_by(name=tag_name, user_id=user_id).delete()
-
-            return True
+            with get_db() as db:
+
+                res = (
+                    db.query(ChatIdTag)
+                    .filter_by(tag_name=tag_name, chat_id=chat_id, user_id=user_id)
+                    .delete()
+                )
+                log.debug(f"res: {res}")
+                db.commit()
+
+                tag_count = self.count_chat_ids_by_tag_name_and_user_id(
+                    tag_name, user_id
+                )
+                if tag_count == 0:
+                    # Remove tag item from Tag col as well
+                    db.query(Tag).filter_by(name=tag_name, user_id=user_id).delete()
+
+                return True
         except Exception as e:
         except Exception as e:
             log.error(f"delete_tag: {e}")
             log.error(f"delete_tag: {e}")
             return False
             return False

+ 53 - 41
backend/apps/webui/models/tools.py

@@ -4,7 +4,7 @@ import time
 import logging
 import logging
 from sqlalchemy import String, Column, BigInteger, Text
 from sqlalchemy import String, Column, BigInteger, Text
 
 
-from apps.webui.internal.db import Base, JSONField, Session
+from apps.webui.internal.db import Base, JSONField, get_db
 from apps.webui.models.users import Users
 from apps.webui.models.users import Users
 
 
 import json
 import json
@@ -83,54 +83,64 @@ class ToolsTable:
     def insert_new_tool(
     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]:
     ) -> Optional[ToolModel]:
-        tool = ToolModel(
-            **{
-                **form_data.model_dump(),
-                "specs": specs,
-                "user_id": user_id,
-                "updated_at": int(time.time()),
-                "created_at": int(time.time()),
-            }
-        )
 
 
-        try:
-            result = Tool(**tool.model_dump())
-            Session.add(result)
-            Session.commit()
-            Session.refresh(result)
-            if result:
-                return ToolModel.model_validate(result)
-            else:
+        with get_db() as db:
+
+            tool = ToolModel(
+                **{
+                    **form_data.model_dump(),
+                    "specs": specs,
+                    "user_id": user_id,
+                    "updated_at": int(time.time()),
+                    "created_at": int(time.time()),
+                }
+            )
+
+            try:
+                result = Tool(**tool.model_dump())
+                db.add(result)
+                db.commit()
+                db.refresh(result)
+                if result:
+                    return ToolModel.model_validate(result)
+                else:
+                    return None
+            except Exception as e:
+                print(f"Error creating tool: {e}")
                 return None
                 return None
-        except Exception as e:
-            print(f"Error creating tool: {e}")
-            return None
 
 
     def get_tool_by_id(self, id: str) -> Optional[ToolModel]:
     def get_tool_by_id(self, id: str) -> Optional[ToolModel]:
         try:
         try:
-            tool = Session.get(Tool, id)
-            return ToolModel.model_validate(tool)
+            with get_db() as db:
+
+                tool = db.get(Tool, id)
+                return ToolModel.model_validate(tool)
         except:
         except:
             return None
             return None
 
 
     def get_tools(self) -> List[ToolModel]:
     def get_tools(self) -> List[ToolModel]:
-        return [ToolModel.model_validate(tool) for tool in Session.query(Tool).all()]
+        with get_db() as db:
+            return [ToolModel.model_validate(tool) for tool in db.query(Tool).all()]
 
 
     def get_tool_valves_by_id(self, id: str) -> Optional[dict]:
     def get_tool_valves_by_id(self, id: str) -> Optional[dict]:
         try:
         try:
-            tool = Session.get(Tool, id)
-            return tool.valves if tool.valves else {}
+            with get_db() as db:
+
+                tool = db.get(Tool, id)
+                return tool.valves if tool.valves else {}
         except Exception as e:
         except Exception as e:
             print(f"An error occurred: {e}")
             print(f"An error occurred: {e}")
             return None
             return None
 
 
     def update_tool_valves_by_id(self, id: str, valves: dict) -> Optional[ToolValves]:
     def update_tool_valves_by_id(self, id: str, valves: dict) -> Optional[ToolValves]:
         try:
         try:
-            Session.query(Tool).filter_by(id=id).update(
-                {"valves": valves, "updated_at": int(time.time())}
-            )
-            Session.commit()
-            return self.get_tool_by_id(id)
+            with get_db() as db:
+
+                db.query(Tool).filter_by(id=id).update(
+                    {"valves": valves, "updated_at": int(time.time())}
+                )
+                db.commit()
+                return self.get_tool_by_id(id)
         except:
         except:
             return None
             return None
 
 
@@ -139,7 +149,7 @@ class ToolsTable:
     ) -> Optional[dict]:
     ) -> Optional[dict]:
         try:
         try:
             user = Users.get_user_by_id(user_id)
             user = Users.get_user_by_id(user_id)
-            user_settings = user.settings.model_dump()
+            user_settings = user.settings.model_dump() if user.settings else {}
 
 
             # Check if user has "tools" and "valves" settings
             # Check if user has "tools" and "valves" settings
             if "tools" not in user_settings:
             if "tools" not in user_settings:
@@ -157,7 +167,7 @@ class ToolsTable:
     ) -> Optional[dict]:
     ) -> Optional[dict]:
         try:
         try:
             user = Users.get_user_by_id(user_id)
             user = Users.get_user_by_id(user_id)
-            user_settings = user.settings.model_dump()
+            user_settings = user.settings.model_dump() if user.settings else {}
 
 
             # Check if user has "tools" and "valves" settings
             # Check if user has "tools" and "valves" settings
             if "tools" not in user_settings:
             if "tools" not in user_settings:
@@ -177,19 +187,21 @@ class ToolsTable:
 
 
     def update_tool_by_id(self, id: str, updated: dict) -> Optional[ToolModel]:
     def update_tool_by_id(self, id: str, updated: dict) -> Optional[ToolModel]:
         try:
         try:
-            tool = Session.get(Tool, id)
-            tool.update(**updated)
-            tool.updated_at = int(time.time())
-            Session.commit()
-            Session.refresh(tool)
-            return ToolModel.model_validate(tool)
+            with get_db() as db:
+                tool = db.get(Tool, id)
+                tool.update(**updated)
+                tool.updated_at = int(time.time())
+                db.commit()
+                db.refresh(tool)
+                return ToolModel.model_validate(tool)
         except:
         except:
             return None
             return None
 
 
     def delete_tool_by_id(self, id: str) -> bool:
     def delete_tool_by_id(self, id: str) -> bool:
         try:
         try:
-            Session.query(Tool).filter_by(id=id).delete()
-            return True
+            with get_db() as db:
+                db.query(Tool).filter_by(id=id).delete()
+                return True
         except:
         except:
             return False
             return False
 
 

+ 92 - 73
backend/apps/webui/models/users.py

@@ -6,7 +6,7 @@ from sqlalchemy import String, Column, BigInteger, Text
 
 
 from utils.misc import get_gravatar_url
 from utils.misc import get_gravatar_url
 
 
-from apps.webui.internal.db import Base, JSONField, Session
+from apps.webui.internal.db import Base, JSONField, Session, get_db
 from apps.webui.models.chats import Chats
 from apps.webui.models.chats import Chats
 
 
 ####################
 ####################
@@ -88,81 +88,92 @@ class UsersTable:
         role: str = "pending",
         role: str = "pending",
         oauth_sub: Optional[str] = None,
         oauth_sub: Optional[str] = None,
     ) -> Optional[UserModel]:
     ) -> Optional[UserModel]:
-        user = UserModel(
-            **{
-                "id": id,
-                "name": name,
-                "email": email,
-                "role": role,
-                "profile_image_url": profile_image_url,
-                "last_active_at": int(time.time()),
-                "created_at": int(time.time()),
-                "updated_at": int(time.time()),
-                "oauth_sub": oauth_sub,
-            }
-        )
-        result = User(**user.model_dump())
-        Session.add(result)
-        Session.commit()
-        Session.refresh(result)
-        if result:
-            return user
-        else:
-            return None
+        with get_db() as db:
+            user = UserModel(
+                **{
+                    "id": id,
+                    "name": name,
+                    "email": email,
+                    "role": role,
+                    "profile_image_url": profile_image_url,
+                    "last_active_at": int(time.time()),
+                    "created_at": int(time.time()),
+                    "updated_at": int(time.time()),
+                    "oauth_sub": oauth_sub,
+                }
+            )
+            result = User(**user.model_dump())
+            db.add(result)
+            db.commit()
+            db.refresh(result)
+            if result:
+                return user
+            else:
+                return None
 
 
     def get_user_by_id(self, id: str) -> Optional[UserModel]:
     def get_user_by_id(self, id: str) -> Optional[UserModel]:
         try:
         try:
-            user = Session.query(User).filter_by(id=id).first()
-            return UserModel.model_validate(user)
+            with get_db() as db:
+                user = db.query(User).filter_by(id=id).first()
+                return UserModel.model_validate(user)
         except Exception as e:
         except Exception as e:
             return None
             return None
 
 
     def get_user_by_api_key(self, api_key: str) -> Optional[UserModel]:
     def get_user_by_api_key(self, api_key: str) -> Optional[UserModel]:
         try:
         try:
-            user = Session.query(User).filter_by(api_key=api_key).first()
-            return UserModel.model_validate(user)
+            with get_db() as db:
+
+                user = db.query(User).filter_by(api_key=api_key).first()
+                return UserModel.model_validate(user)
         except:
         except:
             return None
             return None
 
 
     def get_user_by_email(self, email: str) -> Optional[UserModel]:
     def get_user_by_email(self, email: str) -> Optional[UserModel]:
         try:
         try:
-            user = Session.query(User).filter_by(email=email).first()
-            return UserModel.model_validate(user)
+            with get_db() as db:
+
+                user = db.query(User).filter_by(email=email).first()
+                return UserModel.model_validate(user)
         except:
         except:
             return None
             return None
 
 
     def get_user_by_oauth_sub(self, sub: str) -> Optional[UserModel]:
     def get_user_by_oauth_sub(self, sub: str) -> Optional[UserModel]:
         try:
         try:
-            user = Session.query(User).filter_by(oauth_sub=sub).first()
-            return UserModel.model_validate(user)
+            with get_db() as db:
+
+                user = db.query(User).filter_by(oauth_sub=sub).first()
+                return UserModel.model_validate(user)
         except:
         except:
             return None
             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]:
-        users = (
-            Session.query(User)
-            # .offset(skip).limit(limit)
-            .all()
-        )
-        return [UserModel.model_validate(user) for user in users]
+        with get_db() as db:
+            users = (
+                db.query(User)
+                # .offset(skip).limit(limit)
+                .all()
+            )
+            return [UserModel.model_validate(user) for user in users]
 
 
     def get_num_users(self) -> Optional[int]:
     def get_num_users(self) -> Optional[int]:
-        return Session.query(User).count()
+        with get_db() as db:
+            return db.query(User).count()
 
 
     def get_first_user(self) -> UserModel:
     def get_first_user(self) -> UserModel:
         try:
         try:
-            user = Session.query(User).order_by(User.created_at).first()
-            return UserModel.model_validate(user)
+            with get_db() as db:
+                user = db.query(User).order_by(User.created_at).first()
+                return UserModel.model_validate(user)
         except:
         except:
             return None
             return None
 
 
     def update_user_role_by_id(self, id: str, role: str) -> Optional[UserModel]:
     def update_user_role_by_id(self, id: str, role: str) -> Optional[UserModel]:
         try:
         try:
-            Session.query(User).filter_by(id=id).update({"role": role})
-            Session.commit()
-
-            user = Session.query(User).filter_by(id=id).first()
-            return UserModel.model_validate(user)
+            with get_db() as db:
+                db.query(User).filter_by(id=id).update({"role": role})
+                db.commit()
+                user = db.query(User).filter_by(id=id).first()
+                return UserModel.model_validate(user)
         except:
         except:
             return None
             return None
 
 
@@ -170,25 +181,28 @@ class UsersTable:
         self, id: str, profile_image_url: str
         self, id: str, profile_image_url: str
     ) -> Optional[UserModel]:
     ) -> Optional[UserModel]:
         try:
         try:
-            Session.query(User).filter_by(id=id).update(
-                {"profile_image_url": profile_image_url}
-            )
-            Session.commit()
-
-            user = Session.query(User).filter_by(id=id).first()
-            return UserModel.model_validate(user)
+            with get_db() as db:
+                db.query(User).filter_by(id=id).update(
+                    {"profile_image_url": profile_image_url}
+                )
+                db.commit()
+
+                user = db.query(User).filter_by(id=id).first()
+                return UserModel.model_validate(user)
         except:
         except:
             return None
             return None
 
 
     def update_user_last_active_by_id(self, id: str) -> Optional[UserModel]:
     def update_user_last_active_by_id(self, id: str) -> Optional[UserModel]:
         try:
         try:
-            Session.query(User).filter_by(id=id).update(
-                {"last_active_at": int(time.time())}
-            )
-            Session.commit()
+            with get_db() as db:
+
+                db.query(User).filter_by(id=id).update(
+                    {"last_active_at": int(time.time())}
+                )
+                db.commit()
 
 
-            user = Session.query(User).filter_by(id=id).first()
-            return UserModel.model_validate(user)
+                user = db.query(User).filter_by(id=id).first()
+                return UserModel.model_validate(user)
         except:
         except:
             return None
             return None
 
 
@@ -196,21 +210,23 @@ class UsersTable:
         self, id: str, oauth_sub: str
         self, id: str, oauth_sub: str
     ) -> Optional[UserModel]:
     ) -> Optional[UserModel]:
         try:
         try:
-            Session.query(User).filter_by(id=id).update({"oauth_sub": oauth_sub})
+            with get_db() as db:
+                db.query(User).filter_by(id=id).update({"oauth_sub": oauth_sub})
 
 
-            user = Session.query(User).filter_by(id=id).first()
-            return UserModel.model_validate(user)
+                user = db.query(User).filter_by(id=id).first()
+                return UserModel.model_validate(user)
         except:
         except:
             return None
             return None
 
 
     def update_user_by_id(self, id: str, updated: dict) -> Optional[UserModel]:
     def update_user_by_id(self, id: str, updated: dict) -> Optional[UserModel]:
         try:
         try:
-            Session.query(User).filter_by(id=id).update(updated)
-            Session.commit()
+            with get_db() as db:
+                db.query(User).filter_by(id=id).update(updated)
+                db.commit()
 
 
-            user = Session.query(User).filter_by(id=id).first()
-            return UserModel.model_validate(user)
-            # return UserModel(**user.dict())
+                user = db.query(User).filter_by(id=id).first()
+                return UserModel.model_validate(user)
+                # return UserModel(**user.dict())
         except Exception as e:
         except Exception as e:
             return None
             return None
 
 
@@ -220,9 +236,10 @@ class UsersTable:
             result = Chats.delete_chats_by_user_id(id)
             result = Chats.delete_chats_by_user_id(id)
 
 
             if result:
             if result:
-                # Delete User
-                Session.query(User).filter_by(id=id).delete()
-                Session.commit()
+                with get_db() as db:
+                    # Delete User
+                    db.query(User).filter_by(id=id).delete()
+                    db.commit()
 
 
                 return True
                 return True
             else:
             else:
@@ -232,16 +249,18 @@ class UsersTable:
 
 
     def update_user_api_key_by_id(self, id: str, api_key: str) -> str:
     def update_user_api_key_by_id(self, id: str, api_key: str) -> str:
         try:
         try:
-            result = Session.query(User).filter_by(id=id).update({"api_key": api_key})
-            Session.commit()
-            return True if result == 1 else False
+            with get_db() as db:
+                result = db.query(User).filter_by(id=id).update({"api_key": api_key})
+                db.commit()
+                return True if result == 1 else False
         except:
         except:
             return False
             return False
 
 
     def get_user_api_key_by_id(self, id: str) -> Optional[str]:
     def get_user_api_key_by_id(self, id: str) -> Optional[str]:
         try:
         try:
-            user = Session.query(User).filter_by(id=id).first()
-            return user.api_key
+            with get_db() as db:
+                user = db.query(User).filter_by(id=id).first()
+                return user.api_key
         except Exception as e:
         except Exception as e:
             return None
             return None
 
 

+ 5 - 1
backend/main.py

@@ -999,12 +999,16 @@ async def get_all_models():
                     model["info"] = custom_model.model_dump()
                     model["info"] = custom_model.model_dump()
         else:
         else:
             owned_by = "openai"
             owned_by = "openai"
+            pipe = None
+
             for model in models:
             for model in models:
                 if (
                 if (
                     custom_model.base_model_id == model["id"]
                     custom_model.base_model_id == model["id"]
                     or custom_model.base_model_id == model["id"].split(":")[0]
                     or custom_model.base_model_id == model["id"].split(":")[0]
                 ):
                 ):
                     owned_by = model["owned_by"]
                     owned_by = model["owned_by"]
+                    if "pipe" in model:
+                        pipe = model["pipe"]
                     break
                     break
 
 
             models.append(
             models.append(
@@ -1016,11 +1020,11 @@ async def get_all_models():
                     "owned_by": owned_by,
                     "owned_by": owned_by,
                     "info": custom_model.model_dump(),
                     "info": custom_model.model_dump(),
                     "preset": True,
                     "preset": True,
+                    **({"pipe": pipe} if pipe is not None else {}),
                 }
                 }
             )
             )
 
 
     app.state.MODELS = {model["id"]: model for model in models}
     app.state.MODELS = {model["id"]: model for model in models}
-
     webui_app.state.MODELS = app.state.MODELS
     webui_app.state.MODELS = app.state.MODELS
 
 
     return models
     return models

+ 15 - 2
src/lib/components/admin/Settings.svelte

@@ -1,5 +1,5 @@
 <script>
 <script>
-	import { getContext, tick } from 'svelte';
+	import { getContext, tick, onMount } from 'svelte';
 	import { toast } from 'svelte-sonner';
 	import { toast } from 'svelte-sonner';
 
 
 	import Database from './Settings/Database.svelte';
 	import Database from './Settings/Database.svelte';
@@ -21,11 +21,24 @@
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 
 
 	let selectedTab = 'general';
 	let selectedTab = 'general';
+
+	onMount(() => {
+		const containerElement = document.getElementById('admin-settings-tabs-container');
+
+		if (containerElement) {
+			containerElement.addEventListener('wheel', function (event) {
+				if (event.deltaY !== 0) {
+					// Adjust horizontal scroll position based on vertical scroll
+					containerElement.scrollLeft += event.deltaY;
+				}
+			});
+		}
+	});
 </script>
 </script>
 
 
 <div class="flex flex-col lg:flex-row w-full h-full py-2 lg:space-x-4">
 <div class="flex flex-col lg:flex-row w-full h-full py-2 lg:space-x-4">
 	<div
 	<div
-		class="tabs flex flex-row overflow-x-auto space-x-1 max-w-full lg:space-x-0 lg:space-y-1 lg:flex-col lg:flex-none lg:w-44 dark:text-gray-200 text-xs text-left scrollbar-none"
+		id="admin-settings-tabs-container" class="tabs flex flex-row overflow-x-auto space-x-1 max-w-full lg:space-x-0 lg:space-y-1 lg:flex-col lg:flex-none lg:w-44 dark:text-gray-200 text-xs text-left scrollbar-none"
 	>
 	>
 		<button
 		<button
 			class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 lg:flex-none flex text-right transition {selectedTab ===
 			class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 lg:flex-none flex text-right transition {selectedTab ===

+ 8 - 6
src/lib/components/admin/Settings/Models.svelte

@@ -158,12 +158,14 @@
 			return;
 			return;
 		}
 		}
 
 
-		const [res, controller] = await pullModel(localStorage.token, sanitizedModelTag, '0').catch(
-			(error) => {
-				toast.error(error);
-				return null;
-			}
-		);
+		const [res, controller] = await pullModel(
+			localStorage.token,
+			sanitizedModelTag,
+			selectedOllamaUrlIdx
+		).catch((error) => {
+			toast.error(error);
+			return null;
+		});
 
 
 		if (res) {
 		if (res) {
 			const reader = res.body
 			const reader = res.body

+ 19 - 11
src/lib/components/chat/Chat.svelte

@@ -126,21 +126,29 @@
 		})();
 		})();
 	}
 	}
 
 
-	const chatEventHandler = async (data) => {
-		if (data.chat_id === $chatId) {
+	const chatEventHandler = async (event) => {
+		if (event.chat_id === $chatId) {
 			await tick();
 			await tick();
-			console.log(data);
-			let message = history.messages[data.message_id];
+			console.log(event);
+			let message = history.messages[event.message_id];
 
 
-			const status = {
-				done: data?.data?.done ?? null,
-				description: data?.data?.status ?? null
-			};
+			const type = event?.data?.type ?? null;
+			const data = event?.data?.data ?? null;
 
 
-			if (message.statusHistory) {
-				message.statusHistory.push(status);
+			if (type === 'status') {
+				if (message.statusHistory) {
+					message.statusHistory.push(data);
+				} else {
+					message.statusHistory = [data];
+				}
+			} else if (type === 'citation') {
+				if (message.citations) {
+					message.citations.push(data);
+				} else {
+					message.citations = [data];
+				}
 			} else {
 			} else {
-				message.statusHistory = [status];
+				console.log('Unknown message type', data);
 			}
 			}
 
 
 			messages = messages;
 			messages = messages;

+ 15 - 22
src/lib/components/chat/Messages/ResponseMessage/WebSearchResults.svelte

@@ -2,32 +2,25 @@
 	import ChevronDown from '$lib/components/icons/ChevronDown.svelte';
 	import ChevronDown from '$lib/components/icons/ChevronDown.svelte';
 	import ChevronUp from '$lib/components/icons/ChevronUp.svelte';
 	import ChevronUp from '$lib/components/icons/ChevronUp.svelte';
 	import MagnifyingGlass from '$lib/components/icons/MagnifyingGlass.svelte';
 	import MagnifyingGlass from '$lib/components/icons/MagnifyingGlass.svelte';
-	import { Collapsible } from 'bits-ui';
-	import { slide } from 'svelte/transition';
+	import Collapsible from '$lib/components/common/Collapsible.svelte';
 
 
 	export let status = { urls: [], query: '' };
 	export let status = { urls: [], query: '' };
 	let state = false;
 	let state = false;
 </script>
 </script>
 
 
-<Collapsible.Root class="w-full space-y-1" bind:open={state}>
-	<Collapsible.Trigger>
-		<div
-			class="flex items-center gap-2 text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition"
-		>
-			<slot />
-
-			{#if state}
-				<ChevronUp strokeWidth="3.5" className="size-3.5 " />
-			{:else}
-				<ChevronDown strokeWidth="3.5" className="size-3.5 " />
-			{/if}
-		</div>
-	</Collapsible.Trigger>
-
-	<Collapsible.Content
-		class=" text-sm border border-gray-300/30 dark:border-gray-700/50 rounded-xl"
-		transition={slide}
+<Collapsible bind:open={state} className="w-full space-y-1">
+	<div
+		class="flex items-center gap-2 text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition"
 	>
 	>
+		<slot />
+
+		{#if state}
+			<ChevronUp strokeWidth="3.5" className="size-3.5 " />
+		{:else}
+			<ChevronDown strokeWidth="3.5" className="size-3.5 " />
+		{/if}
+	</div>
+	<div class="text-sm border border-gray-300/30 dark:border-gray-700/50 rounded-xl" slot="content">
 		{#if status?.query}
 		{#if status?.query}
 			<a
 			<a
 				href="https://www.google.com/search?q={status.query}"
 				href="https://www.google.com/search?q={status.query}"
@@ -93,5 +86,5 @@
 				</div>
 				</div>
 			</a>
 			</a>
 		{/each}
 		{/each}
-	</Collapsible.Content>
-</Collapsible.Root>
+	</div>
+</Collapsible>

+ 35 - 4
src/lib/components/chat/SettingsModal.svelte

@@ -1,9 +1,10 @@
 <script lang="ts">
 <script lang="ts">
-	import { getContext } from 'svelte';
+	import { getContext, tick } from 'svelte';
 	import { toast } from 'svelte-sonner';
 	import { toast } from 'svelte-sonner';
 	import { models, settings, user } from '$lib/stores';
 	import { models, settings, user } from '$lib/stores';
-
+	import { updateUserSettings } from '$lib/apis/users';
 	import { getModels as _getModels } from '$lib/apis';
 	import { getModels as _getModels } from '$lib/apis';
+	import { goto } from '$app/navigation';
 
 
 	import Modal from '../common/Modal.svelte';
 	import Modal from '../common/Modal.svelte';
 	import Account from './Settings/Account.svelte';
 	import Account from './Settings/Account.svelte';
@@ -14,8 +15,6 @@
 	import Chats from './Settings/Chats.svelte';
 	import Chats from './Settings/Chats.svelte';
 	import User from '../icons/User.svelte';
 	import User from '../icons/User.svelte';
 	import Personalization from './Settings/Personalization.svelte';
 	import Personalization from './Settings/Personalization.svelte';
-	import { updateUserSettings } from '$lib/apis/users';
-	import { goto } from '$app/navigation';
 	import Valves from './Settings/Valves.svelte';
 	import Valves from './Settings/Valves.svelte';
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
@@ -34,6 +33,37 @@
 	};
 	};
 
 
 	let selectedTab = 'general';
 	let selectedTab = 'general';
+
+	// Function to handle sideways scrolling
+	const scrollHandler = (event) => {
+		const settingsTabsContainer = document.getElementById('settings-tabs-container');
+		if (settingsTabsContainer) {
+			event.preventDefault(); // Prevent default vertical scrolling
+			settingsTabsContainer.scrollLeft += event.deltaY; // Scroll sideways
+		}
+	};
+
+	const addScrollListener = async () => {
+		await tick();
+		const settingsTabsContainer = document.getElementById('settings-tabs-container');
+		if (settingsTabsContainer) {
+			settingsTabsContainer.addEventListener('wheel', scrollHandler);
+		}
+	};
+
+	const removeScrollListener = async () => {
+		await tick();
+		const settingsTabsContainer = document.getElementById('settings-tabs-container');
+		if (settingsTabsContainer) {
+			settingsTabsContainer.removeEventListener('wheel', scrollHandler);
+		}
+	};
+
+	$: if (show) {
+		addScrollListener();
+	} else {
+		removeScrollListener();
+	}
 </script>
 </script>
 
 
 <Modal bind:show>
 <Modal bind:show>
@@ -61,6 +91,7 @@
 
 
 		<div class="flex flex-col md:flex-row w-full p-4 md:space-x-4">
 		<div class="flex flex-col md:flex-row w-full p-4 md:space-x-4">
 			<div
 			<div
+				id="settings-tabs-container"
 				class="tabs flex flex-row overflow-x-auto space-x-1 md:space-x-0 md:space-y-1 md:flex-col flex-1 md:flex-none md:w-40 dark:text-gray-200 text-xs text-left mb-3 md:mb-0"
 				class="tabs flex flex-row overflow-x-auto space-x-1 md:space-x-0 md:space-y-1 md:flex-col flex-1 md:flex-none md:w-40 dark:text-gray-200 text-xs text-left mb-3 md:mb-0"
 			>
 			>
 				<button
 				<button

+ 18 - 0
src/lib/components/common/Collapsible.svelte

@@ -0,0 +1,18 @@
+<script lang="ts">
+	import { slide } from 'svelte/transition';
+	import { quintOut } from 'svelte/easing';
+	export let open = false;
+	export let className = '';
+</script>
+
+<div class={className}>
+	<button on:click={() => (open = !open)}>
+		<slot />
+	</button>
+
+	{#if open}
+		<div transition:slide={{ duration: 300, easing: quintOut, axis: 'y' }}>
+			<slot name="content" />
+		</div>
+	{/if}
+</div>

+ 1 - 0
src/lib/components/layout/Sidebar.svelte

@@ -186,6 +186,7 @@
 				goto('/');
 				goto('/');
 			}
 			}
 			await chats.set(await getChatList(localStorage.token));
 			await chats.set(await getChatList(localStorage.token));
+			await pinnedChats.set(await getChatListByTagName(localStorage.token, 'pinned'));
 		}
 		}
 	};
 	};
 </script>
 </script>

+ 9 - 9
src/lib/i18n/locales/ca-ES/translation.json

@@ -126,7 +126,7 @@
 	"Connections": "Connexions",
 	"Connections": "Connexions",
 	"Contact Admin for WebUI Access": "Posat en contacte amb l'administrador per accedir a WebUI",
 	"Contact Admin for WebUI Access": "Posat en contacte amb l'administrador per accedir a WebUI",
 	"Content": "Contingut",
 	"Content": "Contingut",
-	"Content Extraction": "",
+	"Content Extraction": "Extraccció de contingut",
 	"Context Length": "Mida del context",
 	"Context Length": "Mida del context",
 	"Continue Response": "Continuar la resposta",
 	"Continue Response": "Continuar la resposta",
 	"Continue with {{provider}}": "Continuar amb {{provider}}",
 	"Continue with {{provider}}": "Continuar amb {{provider}}",
@@ -213,7 +213,7 @@
 	"Enable Community Sharing": "Activar l'ús compartit amb la comunitat",
 	"Enable Community Sharing": "Activar l'ús compartit amb la comunitat",
 	"Enable New Sign Ups": "Permetre nous registres",
 	"Enable New Sign Ups": "Permetre nous registres",
 	"Enable Web Search": "Activar la cerca web",
 	"Enable Web Search": "Activar la cerca web",
-	"Engine": "",
+	"Engine": "Motor",
 	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Assegura't que els teus fitxers CSV inclouen 4 columnes en aquest ordre: Nom, Correu electrònic, Contrasenya, Rol.",
 	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Assegura't que els teus fitxers CSV inclouen 4 columnes en aquest ordre: Nom, Correu electrònic, Contrasenya, Rol.",
 	"Enter {{role}} message here": "Introdueix aquí el missatge de {{role}}",
 	"Enter {{role}} message here": "Introdueix aquí el missatge de {{role}}",
 	"Enter a detail about yourself for your LLMs to recall": "Introdueix un detall sobre tu què els teus models de llenguatge puguin recordar",
 	"Enter a detail about yourself for your LLMs to recall": "Introdueix un detall sobre tu què els teus models de llenguatge puguin recordar",
@@ -235,7 +235,7 @@
 	"Enter Serpstack API Key": "Introdueix la clau API Serpstack",
 	"Enter Serpstack API Key": "Introdueix la clau API Serpstack",
 	"Enter stop sequence": "Introdueix la seqüència de parada",
 	"Enter stop sequence": "Introdueix la seqüència de parada",
 	"Enter Tavily API Key": "Introdueix la clau API de Tavily",
 	"Enter Tavily API Key": "Introdueix la clau API de Tavily",
-	"Enter Tika Server URL": "",
+	"Enter Tika Server URL": "Introdueix l'URL del servidor Tika",
 	"Enter Top K": "Introdueix Top K",
 	"Enter Top K": "Introdueix Top K",
 	"Enter URL (e.g. http://127.0.0.1:7860/)": "Introdueix l'URL (p. ex. http://127.0.0.1:7860/)",
 	"Enter URL (e.g. http://127.0.0.1:7860/)": "Introdueix l'URL (p. ex. http://127.0.0.1:7860/)",
 	"Enter URL (e.g. http://localhost:11434)": "Introdueix l'URL (p. ex. http://localhost:11434)",
 	"Enter URL (e.g. http://localhost:11434)": "Introdueix l'URL (p. ex. http://localhost:11434)",
@@ -412,7 +412,7 @@
 	"Open": "Obre",
 	"Open": "Obre",
 	"Open AI (Dall-E)": "Open AI (Dall-E)",
 	"Open AI (Dall-E)": "Open AI (Dall-E)",
 	"Open new chat": "Obre un xat nou",
 	"Open new chat": "Obre un xat nou",
-	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "La versió d'Open WebUI (v{{OPEN_WEBUI_VERSION}}) és inferior a la versió requerida (v{{REQUIRED_VERSION}})",
 	"OpenAI": "OpenAI",
 	"OpenAI": "OpenAI",
 	"OpenAI API": "API d'OpenAI",
 	"OpenAI API": "API d'OpenAI",
 	"OpenAI API Config": "Configuració de l'API d'OpenAI",
 	"OpenAI API Config": "Configuració de l'API d'OpenAI",
@@ -428,8 +428,8 @@
 	"Permission denied when accessing microphone": "Permís denegat en accedir al micròfon",
 	"Permission denied when accessing microphone": "Permís denegat en accedir al micròfon",
 	"Permission denied when accessing microphone: {{error}}": "Permís denegat en accedir al micròfon: {{error}}",
 	"Permission denied when accessing microphone: {{error}}": "Permís denegat en accedir al micròfon: {{error}}",
 	"Personalization": "Personalització",
 	"Personalization": "Personalització",
-	"Pin": "",
-	"Pinned": "",
+	"Pin": "Fixar",
+	"Pinned": "Fixat",
 	"Pipeline deleted successfully": "Pipeline eliminada correctament",
 	"Pipeline deleted successfully": "Pipeline eliminada correctament",
 	"Pipeline downloaded successfully": "Pipeline descarregada correctament",
 	"Pipeline downloaded successfully": "Pipeline descarregada correctament",
 	"Pipelines": "Pipelines",
 	"Pipelines": "Pipelines",
@@ -580,8 +580,8 @@
 	"This setting does not sync across browsers or devices.": "Aquesta preferència no es sincronitza entre navegadors ni dispositius.",
 	"This setting does not sync across browsers or devices.": "Aquesta preferència no es sincronitza entre navegadors ni dispositius.",
 	"This will delete": "Això eliminarà",
 	"This will delete": "Això eliminarà",
 	"Thorough explanation": "Explicació en detall",
 	"Thorough explanation": "Explicació en detall",
-	"Tika": "",
-	"Tika Server URL required.": "",
+	"Tika": "Tika",
+	"Tika Server URL required.": "La URL del servidor Tika és obligatòria.",
 	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Consell: Actualitza les diverses variables consecutivament prement la tecla de tabulació en l'entrada del xat després de cada reemplaçament.",
 	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Consell: Actualitza les diverses variables consecutivament prement la tecla de tabulació en l'entrada del xat després de cada reemplaçament.",
 	"Title": "Títol",
 	"Title": "Títol",
 	"Title (e.g. Tell me a fun fact)": "Títol (p. ex. Digues-me quelcom divertit)",
 	"Title (e.g. Tell me a fun fact)": "Títol (p. ex. Digues-me quelcom divertit)",
@@ -616,7 +616,7 @@
 	"Uh-oh! There was an issue connecting to {{provider}}.": "Oh! Hi ha hagut un problema connectant a {{provider}}.",
 	"Uh-oh! There was an issue connecting to {{provider}}.": "Oh! Hi ha hagut un problema connectant a {{provider}}.",
 	"UI": "UI",
 	"UI": "UI",
 	"Unknown file type '{{file_type}}'. Proceeding with the file upload anyway.": "Tipus de fitxer desconegut '{{file_type}}'. Continuant amb la càrrega del fitxer.",
 	"Unknown file type '{{file_type}}'. Proceeding with the file upload anyway.": "Tipus de fitxer desconegut '{{file_type}}'. Continuant amb la càrrega del fitxer.",
-	"Unpin": "",
+	"Unpin": "Alliberar",
 	"Update": "Actualitzar",
 	"Update": "Actualitzar",
 	"Update and Copy Link": "Actualitzar i copiar l'enllaç",
 	"Update and Copy Link": "Actualitzar i copiar l'enllaç",
 	"Update password": "Actualitzar la contrasenya",
 	"Update password": "Actualitzar la contrasenya",

+ 9 - 9
src/lib/i18n/locales/zh-CN/translation.json

@@ -126,7 +126,7 @@
 	"Connections": "外部连接",
 	"Connections": "外部连接",
 	"Contact Admin for WebUI Access": "请联系管理员以获取访问权限",
 	"Contact Admin for WebUI Access": "请联系管理员以获取访问权限",
 	"Content": "内容",
 	"Content": "内容",
-	"Content Extraction": "",
+	"Content Extraction": "内容提取",
 	"Context Length": "上下文长度",
 	"Context Length": "上下文长度",
 	"Continue Response": "继续生成",
 	"Continue Response": "继续生成",
 	"Continue with {{provider}}": "使用 {{provider}} 继续",
 	"Continue with {{provider}}": "使用 {{provider}} 继续",
@@ -213,7 +213,7 @@
 	"Enable Community Sharing": "启用分享至社区",
 	"Enable Community Sharing": "启用分享至社区",
 	"Enable New Sign Ups": "允许新用户注册",
 	"Enable New Sign Ups": "允许新用户注册",
 	"Enable Web Search": "启用网络搜索",
 	"Enable Web Search": "启用网络搜索",
-	"Engine": "",
+	"Engine": "引擎",
 	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "确保您的 CSV 文件按以下顺序包含 4 列: 姓名、电子邮箱、密码、角色。",
 	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "确保您的 CSV 文件按以下顺序包含 4 列: 姓名、电子邮箱、密码、角色。",
 	"Enter {{role}} message here": "在此处输入 {{role}} 信息",
 	"Enter {{role}} message here": "在此处输入 {{role}} 信息",
 	"Enter a detail about yourself for your LLMs to recall": "输入一个关于你自己的详细信息,方便你的大语言模型记住这些内容",
 	"Enter a detail about yourself for your LLMs to recall": "输入一个关于你自己的详细信息,方便你的大语言模型记住这些内容",
@@ -235,7 +235,7 @@
 	"Enter Serpstack API Key": "输入 Serpstack API 密钥",
 	"Enter Serpstack API Key": "输入 Serpstack API 密钥",
 	"Enter stop sequence": "输入停止序列 (Stop Sequence)",
 	"Enter stop sequence": "输入停止序列 (Stop Sequence)",
 	"Enter Tavily API Key": "输入 Tavily API 密钥",
 	"Enter Tavily API Key": "输入 Tavily API 密钥",
-	"Enter Tika Server URL": "",
+	"Enter Tika Server URL": "输入 Tika 服务器地址",
 	"Enter Top K": "输入 Top K",
 	"Enter Top K": "输入 Top K",
 	"Enter URL (e.g. http://127.0.0.1:7860/)": "输入地址 (例如:http://127.0.0.1:7860/)",
 	"Enter URL (e.g. http://127.0.0.1:7860/)": "输入地址 (例如:http://127.0.0.1:7860/)",
 	"Enter URL (e.g. http://localhost:11434)": "输入地址 (例如:http://localhost:11434)",
 	"Enter URL (e.g. http://localhost:11434)": "输入地址 (例如:http://localhost:11434)",
@@ -412,7 +412,7 @@
 	"Open": "打开",
 	"Open": "打开",
 	"Open AI (Dall-E)": "Open AI (Dall-E)",
 	"Open AI (Dall-E)": "Open AI (Dall-E)",
 	"Open new chat": "打开新对话",
 	"Open new chat": "打开新对话",
-	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "当前 Open WebUI 版本 (v{{OPEN_WEBUI_VERSION}}) 低于所需的版本 (v{{REQUIRED_VERSION}})",
 	"OpenAI": "OpenAI",
 	"OpenAI": "OpenAI",
 	"OpenAI API": "OpenAI API",
 	"OpenAI API": "OpenAI API",
 	"OpenAI API Config": "OpenAI API 配置",
 	"OpenAI API Config": "OpenAI API 配置",
@@ -428,8 +428,8 @@
 	"Permission denied when accessing microphone": "申请麦克风权限被拒绝",
 	"Permission denied when accessing microphone": "申请麦克风权限被拒绝",
 	"Permission denied when accessing microphone: {{error}}": "申请麦克风权限被拒绝:{{error}}",
 	"Permission denied when accessing microphone: {{error}}": "申请麦克风权限被拒绝:{{error}}",
 	"Personalization": "个性化",
 	"Personalization": "个性化",
-	"Pin": "",
-	"Pinned": "",
+	"Pin": "置顶",
+	"Pinned": "已置顶",
 	"Pipeline deleted successfully": "Pipeline 删除成功",
 	"Pipeline deleted successfully": "Pipeline 删除成功",
 	"Pipeline downloaded successfully": "Pipeline 下载成功",
 	"Pipeline downloaded successfully": "Pipeline 下载成功",
 	"Pipelines": "Pipeline",
 	"Pipelines": "Pipeline",
@@ -578,8 +578,8 @@
 	"This setting does not sync across browsers or devices.": "此设置不会在浏览器或设备之间同步。",
 	"This setting does not sync across browsers or devices.": "此设置不会在浏览器或设备之间同步。",
 	"This will delete": "这将删除",
 	"This will delete": "这将删除",
 	"Thorough explanation": "解释较为详细",
 	"Thorough explanation": "解释较为详细",
-	"Tika": "",
-	"Tika Server URL required.": "",
+	"Tika": "Tika",
+	"Tika Server URL required.": "请输入 Tika 服务器地址。",
 	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "提示:在每次替换后,在对话输入中按 Tab 键可以连续更新多个变量。",
 	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "提示:在每次替换后,在对话输入中按 Tab 键可以连续更新多个变量。",
 	"Title": "标题",
 	"Title": "标题",
 	"Title (e.g. Tell me a fun fact)": "标题(例如 给我讲一个有趣的事实)",
 	"Title (e.g. Tell me a fun fact)": "标题(例如 给我讲一个有趣的事实)",
@@ -614,7 +614,7 @@
 	"Uh-oh! There was an issue connecting to {{provider}}.": "糟糕!连接到 {{provider}} 时出现问题。",
 	"Uh-oh! There was an issue connecting to {{provider}}.": "糟糕!连接到 {{provider}} 时出现问题。",
 	"UI": "界面",
 	"UI": "界面",
 	"Unknown file type '{{file_type}}'. Proceeding with the file upload anyway.": "未知文件类型“{{file_type}}”,将无视继续上传文件。",
 	"Unknown file type '{{file_type}}'. Proceeding with the file upload anyway.": "未知文件类型“{{file_type}}”,将无视继续上传文件。",
-	"Unpin": "",
+	"Unpin": "取消置顶",
 	"Update": "更新",
 	"Update": "更新",
 	"Update and Copy Link": "更新和复制链接",
 	"Update and Copy Link": "更新和复制链接",
 	"Update password": "更新密码",
 	"Update password": "更新密码",